diff --git a/CONTRIBUTING.md b/CONTRIBUTING.md index c96027705..312e6c825 100644 --- a/CONTRIBUTING.md +++ b/CONTRIBUTING.md @@ -144,7 +144,7 @@ We will then pull from your repository and merge manually. * When fixing a bug, describe it and how your patch fixes it. * When fixing a reported issue add an `Fixes https://github.com/profanity-im/profanity/issues/23` in the commit body. * When adding a new feature add a description of the feature and how it should be used (workflow). -* If your patch adds a new configuration option add this to the `profrc.example` file. +* If your patch adds or changes a configuration option update the `profrc.example` file. * If your patch adds a new theming option add this to the `theme_template` file. * Each patch or pull request should only contain related modifications. * Run the tests and code formatters before submitting (c.f. Chapter 'Check everything' of this README). diff --git a/README.md b/README.md index c7789ffbe..8de4143ad 100644 --- a/README.md +++ b/README.md @@ -21,7 +21,12 @@ Our [user guide](https://profanity-im.github.io/userguide.html) contains an [ins We would highly appreciate if you support us via [GitHub Sponsors](https://github.com/sponsors/jubalh/). Especially if you make feature requests or need help using Profanity. Sponsoring enables us to spend time on Profanity. -An alternative way to support us would be to ask for our IBAN or use Bitcoin: `bc1qx265eat7hfasqkqmk9qf38delydnrnuvzhzy0x`. +An alternative way to support us would be: +* to ask for our IBAN +* Bitcoin: `bc1qx265eat7hfasqkqmk9qf38delydnrnuvzhzy0x` +* Monero (XMR): `85Mz3CXUrom2LJSJRBqcbWj5rg7PfkkVrUzAW5MyGzZiTFxbXjkGckAiLFfze6bjAuLTmPoZ4VNgXZVKHhPWgeWg2nWeU69` + +Also see [https://profanity-im.github.io/donate](https://profanity-im.github.io/donate). Thank you! <3 diff --git a/profrc.example b/profrc.example index c8909acbc..ecb909b58 100644 --- a/profrc.example +++ b/profrc.example @@ -1,62 +1,631 @@ +# ============================================================================== +# [ui] - User Interface +# ============================================================================== [ui] +# Show splash screen on startup. +# Possible values: true, false (Default: true) splash=true -intype=true -beep=false -statuses.muc=all + +# Theme name to load from the themes folder. +# Possible values: default, aqua, gruvbox, solarized-dark, etc. (Default: default) theme=default -history=true -titlebar=true -mouse=false + +# Show active conversation/window title in terminal emulator title. +# Possible values: true, false (Default: true) +wintitle.show=true + +# Set terminal window title to "Goodbye" on exit. +# Possible values: true, false (Default: true) +wintitle.goodbye=true + +# Beep on message notifications. +# Possible values: true, false (Default: false) +beep=false + +# Flash terminal/window manager on new messages. +# Possible values: true, false (Default: false) flash=false + +# Use interactive text input system in chat windows. +# Possible values: true, false (Default: true) +intype=true + +# Use interactive text input system in console window. +# Possible values: true, false (Default: true) +intype.console=true + +# Save and reload scrollback history for windows. +# Possible values: true, false (Default: true) +history=true + +# Clear saved scrollback history files on clean exit. +# Possible values: true, false (Default: false) +clear.persist_history=false + +# Check for Profanity updates on startup. +# Possible values: true, false (Default: false) vercheck=false -statuses.console=all -statuses.chat=all -color.nick=true +# Guard against accidentally sending slash commands as raw text. +# Possible values: true, false (Default: false) +slashguard=false + +# Wrap text lines in chat/MUC window output. +# Possible values: true, false (Default: true) +wrap=true + +# Enable mood publishing and display support (XEP-0107). +# Possible values: true, false (Default: true) +mood=true + +# Milliseconds to wait/block input during UI lag protection. +# Possible values: Integer in milliseconds (Default: 1000) +inpblock=1000 + +# Use dynamic typing blocks. +# Possible values: true, false (Default: true) +inpblock.dynamic=true + +# Warn when sending unencrypted messages to encrypted contacts. +# Possible values: true, false (Default: true) +enc.warn=true + +# Show TLS certificate information upon successful connection. +# Possible values: true, false (Default: true) +tls.show=true + +# Default UI logging verbosity. +# Possible values: all, none (Default: all) +console.chat=all +console.muc=all +console.private=all + +# Color contact nicknames in chat windows. +# Possible values: true, false, theme (Default: false) +color.nick=false + +# Color your own nickname in windows. +# Possible values: true, false (Default: true) +color.nick.own=true + +# Use nick colors in the roster view. +# Possible values: true, false (Default: false) +color.roster.nick=false + +# Use nick colors in the MUC occupants list view. +# Possible values: true, false (Default: false) +color.occupants.nick=false + +# Enable the roster view panel on the side. +# Possible values: true, false (Default: true) +roster=true + +# Width of the roster panel in characters (0 for automatic/default). +# Possible values: Integer (Default: 0) +roster.size=0 + +# Show offline contacts in the roster. +# Possible values: true, false (Default: true) +roster.offline=true + +# Show empty groups in the roster. +# Possible values: true, false (Default: true) +roster.empty=true + +# Show connected resources/clients for roster contacts. +# Possible values: true, false (Default: false) +roster.resource=false + +# Show presence icon/indicator next to roster names. +# Possible values: true, false (Default: false) +roster.presence=false + +# Show status messages directly in the roster. +# Possible values: true, false (Default: false) +roster.status=false + +# Group/Sort roster. +# Possible values: presence, name (Default: presence) +roster.by=presence + +# Sort contacts order in roster. +# Possible values: presence, name (Default: presence) +roster.order=presence + +# Order unread items position. +# Possible values: after, before, none (Default: after) +roster.unread=after + +# Unread items counting format. +# Possible values: unread, all, none (Default: unread) +roster.count=unread + +# Show unread badge count even when it is 0. +# Possible values: true, false (Default: true) +roster.count.zero=true + +# Show presence priority for contact resources. +# Possible values: true, false (Default: true) +roster.priority=true + +# Wrap long roster entries. +# Possible values: true, false (Default: false) +roster.wrap=false + +# Join multiple resources into a comma-separated list. +# Possible values: true, false (Default: true) +roster.resource.join=true + +# Show the standard roster contact list. +# Possible values: true, false (Default: true) +roster.contacts=true + +# Show unsubscribed contacts in the roster. +# Possible values: true, false (Default: true) +roster.unsubscribed=true + +# Show chat rooms list in the roster panel. +# Possible values: true, false (Default: true) +roster.rooms=true + +# Position of the room list in the roster panel. +# Possible values: last, first (Default: last) +roster.rooms.pos=last + +# Sort room list in the roster panel. +# Possible values: name, none (Default: none) +roster.rooms.by=none + +# Sort room list order in the roster panel. +# Possible values: name (Default: name) +roster.rooms.order=name + +# Order unread rooms position. +# Possible values: after, before, none (Default: after) +roster.rooms.unread=after + +# Display format for room titles in the roster. +# Possible values: name, jid (Default: name) +roster.rooms.title=name + +# Placement of private MUC occupant chats in the roster. +# Possible values: room, roster, none (Default: room) +roster.private=room + +# Show occupants list in active MUC chat windows. +# Possible values: true, false (Default: true) +occupants=true + +# Width of occupants panel in characters (0 for automatic/default). +# Possible values: Integer (Default: 0) +occupants.size=0 + +# Show occupant real JIDs when room privileges permit. +# Possible values: true, false (Default: false) +occupants.jid=false + +# Show offline occupants in MUC windows. +# Possible values: true, false (Default: false) +occupants.offline=false + +# Wrap occupant nicknames in occupants panel. +# Possible values: true, false (Default: false) +occupants.wrap=false + +# Show MUC room privilege and role modifications. +# Possible values: true, false (Default: true) +privileges=true + +# Show contact presence updates inside active conversation windows. +# Possible values: true, false (Default: true) +presence=true + +# Resource display toggles. +# Possible values: true, false (Default: true) +resource.title=true +resource.message=true + +# Show the connected account JID/name in the statusbar. +# Possible values: true, false (Default: false) +statusbar.show.name=false + +# Show window numbers in the statusbar. +# Possible values: true, false (Default: true) +statusbar.show.number=true + +# Show read/unread indicators in the statusbar. +# Possible values: true, false (Default: true) +statusbar.show.read=true + +# Display format for own information. +# Possible values: fulljid, barejid, nick, none (Default: fulljid) +statusbar.self=fulljid + +# Display format for active 1:1 chat windows. +# Possible values: user, barejid, fulljid (Default: user) +statusbar.chat=user + +# Display format for active room windows. +# Possible values: name, jid (Default: name) +statusbar.room.title=name + +# Multi-window tab display mode. +# Possible values: default, titles, numbers (Default: default) +statusbar.tabmode=default + +# Limit the number of statusbar tabs shown (0 for unlimited). +# Possible values: Integer (Default: 0) +statusbar.tabs=0 + +# Limit the character length of statusbar tab titles (0 for unlimited). +# Possible values: Integer (Default: 0) +statusbar.tablen=0 + +# Room title layout shown in the titlebar. +# Possible values: name, jid (Default: name) +titlebar.muc.title=name + +# --- Custom prefix characters for list views --- +# Example values: +# roster.header.char=> +# roster.contact.char=* +# roster.resource.char=- +# roster.private.char=p +# roster.rooms.char=# +# roster.rooms.private.char=x +# occupants.char=+ +# occupants.header.char=@ +# correction.char=* + +# --- Window placements/positions --- +# Integer coordinate/order of UI panes (1-4) +titlebar.position=1 +mainwin.position=2 +statusbar.position=3 +inputwin.position=4 + + +# ============================================================================== +# [connection] - XMPP Connectivity, Features & Extensions +# ============================================================================== [connection] +# The default account profile to log in with (profile defined in account storage). +# Possible values: account profile name, e.g., me@server.org (Default: NULL) +account=me@server.org + +# Default global account profile. +# Possible values: profile JID (Default: NULL) +defaccount= + +# Keep-alive ping interval in seconds (0 to disable). +# Possible values: Integer (Default: 60) autoping=60 + +# Keep-alive ping response timeout limit in seconds. +# Possible values: Integer (Default: 0 - libstrophe default) +autoping.timeout=0 + +# MUC room ping check interval/timeout. +# Possible values: Integer in seconds (Default: 0 - disabled) +muc.ping.interval=0 +muc.ping.timeout=0 + +# Reconnection attempt interval in seconds (0 to disable). +# Possible values: Integer (Default: 5) reconnect=5 -account=me@server.org +# Enable Message Carbons (XEP-0280) to sync chats on other devices. +# Possible values: true, false (Default: true) +carbons=true + +# Automatically send delivery receipts to requestors (XEP-0184). +# Possible values: true, false (Default: true) +receipts.send=true + +# Automatically request delivery receipts on outgoing messages. +# Possible values: true, false (Default: false) +receipts.request=false + +# Reveal local OS name via Software Version query responses (XEP-0092). +# Possible values: true, false (Default: true) +reveal.os=true + +# Custom directory/file path to trusted CA TLS certificate bundle. +# Possible values: Path to CA file/directory (Default: NULL) +tls.certpath= + +# Support Last Message Correction (XEP-0308). +# Possible values: true, false (Default: true) +correction.allow=true + +# Support Message Archive Management queries (XEP-0313). +# Possible values: true, false (Default: true) +mam=true + +# Silently discard all incoming messages from non-contacts. +# Possible values: true, false (Default: false) +silence.incoming.nonroster=false + +# libstrophe parser logging verbosity. +# Possible values: Integer, e.g., 0, 1, 2, 3 (Default: 0 - disabled) +strophe.verbosity=0 + +# Enable Stream Management (XEP-0198). +# Possible values: true, false (Default: true) +strophe.sm.enabled=true + +# Automatically resend unacknowledged messages upon recovery. +# Possible values: true, false (Default: true) +strophe.sm.resend=true + + +# ============================================================================== +# [chatstates] - Chat State Notifications (XEP-0085) +# ============================================================================== [chatstates] +# Send chat state notifications (active, typing, paused, gone). +# Possible values: true, false (Default: true) enabled=true + +# Send state notifications in group/MUC chats. +# Possible values: true, false (Default: false) outtype=false + +# Idle minutes before sending "gone" state notification. +# Possible values: Integer in minutes (Default: 10) gone=10 + +# ============================================================================== +# [notifications] - Desktop, Tray, and UI Notification settings +# ============================================================================== [notifications] +# Notification reminder interval in seconds (0 to disable). +# Possible values: Integer (Default: 60) remind=60 + +# Notify on incoming chat room invitation. +# Possible values: true, false (Default: true) invite=true + +# Notify on incoming subscription requests. +# Possible values: true, false (Default: true) sub=true + +# Notify on incoming 1:1 messages. +# Possible values: true, false (Default: true) message=true -room=mention + +# Notify even if the active, focused window receives the message. +# Possible values: true, false (Default: true) message.current=true + +# Include message content text inside notifications. +# Possible values: true, false (Default: true) +message.text=true + +# Notify on any incoming MUC messages. +# Possible values: true, false (Default: false) +room=false + +# Notify only when your nickname is mentioned in a MUC. +# Possible values: true, false (Default: true) +room.mention=true + +# Enable strict nickname casing match for mentions. +# Possible values: true, false (Default: false) +room.mention.casesensitive=false + +# Require nickname mention to be isolated whole words. +# Possible values: true, false (Default: true) +room.mention.wholeword=true + +# Notify when a keyword from the trigger list is matched in MUC. +# Possible values: true, false (Default: false) +room.trigger=false + +# Notify if you receive groupchat messages while being offline. +# Possible values: true, false (Default: true) +room.offline=true + +# Notify even if active focused MUC window receives a message. +# Possible values: true, false (Default: true) room.current=true + +# Include room message content text inside notifications. +# Possible values: true, false (Default: true) +room.text=true + +# Show typing notifications from contacts. +# Possible values: true, false (Default: true) typing=true + +# Show typing notifications even in focused active chat window. +# Possible values: true, false (Default: false) typing.current=false -message.text=true -room.text=true -room.offline=true -[alias] -colour=/color -topic=/subject -friends=/who online friends -bob=/msg bob@server.org hey wassup? +# Enable system tray icon notification support. +# Possible values: true, false (Default: false) +tray=false + +# Clear tray icon status when the conversation is marked read. +# Possible values: true, false (Default: true) +tray.read=true + +# Limit system tray notification display duration (0 to disable). +# Possible values: Integer in seconds (Default: 0) +tray.timer=0 + +# Notify on capabilities/disco changes. +# Possible values: true, false (Default: false) +adv.notify.discoversion=false + +# ============================================================================== +# [logging] - Logging +# ============================================================================== [logging] +# Enable file logging of 1:1 chat conversations. +# Possible values: true, false (Default: false) chlog=true + +# Enable file logging of MUC (group) chat conversations. +# Possible values: true, false (Default: false) grlog=true + +# Save message history in internal SQLite database. +# Possible values: on, off (Default: on) +dblog=on + +# Maximum log file size in bytes before auto-rotation. +# Possible values: Integer, minimum 64 (Default: 0 - disable limit, Example: 1048580) maxsize=1048580 + +# Rotate logs when maxsize limit is reached. +# Possible values: true, false (Default: true) rotate=true + +# Share logs across accounts. +# Possible values: true, false (Default: true) shared=true -[otr] -warn=true -log=redact -policy=manual +# ============================================================================== +# [presence] - User Auto-Away Status Customization +# ============================================================================== [presence] +# Behavior for auto-away. +# Possible values: away, xa (extended away), off (Default: off) autoaway.mode=away -autoaway.time=15 -autoaway.message=Away from computer + +# Check keyboard/screen idle states for autoaway. +# Possible values: true, false (Default: true) autoaway.check=true + +# Idle minutes before switching to "away". +# Possible values: Integer in minutes (Default: 15) +autoaway.awaytime=15 + +# Status message when automatically set to "away". +# Possible values: String (Default: "Away from computer") +autoaway.awaymessage=Away from computer + +# Idle minutes before switching to "xa" (extended away). +# Possible values: Integer in minutes (Default: 45) +autoaway.xatime=45 + +# Status message when automatically set to "xa" (extended away). +# Possible values: String (Default: "Extended away") +autoaway.xamessage=Extended away + +# Enable Last Activity tracking (XEP-0012). +# Possible values: true, false (Default: true) +lastactivity=true + + +# ============================================================================== +# [otr] - Off-the-Record (OTR) Encryption +# ============================================================================== +[otr] +# OTR conversation logging behavior. +# Possible values: on (unencrypted/decrypted), redact (metadata only), off (Default: on) +log=redact + +# OTR usage policy. +# Possible values: manual, opportunistic, always (Default: manual) +policy=manual + +# Encrypt OTR files. +# Possible values: true, false (Default: false) +sendfile=false + + +# ============================================================================== +# [pgp] - Legacy OpenPGP Encryption (XEP-0027) +# ============================================================================== +[pgp] +# Legacy PGP conversation logging behavior. +# Possible values: on, redact, off (Default: on) +log=on + +# Encrypt PGP files. +# Possible values: true, false (Default: false) +sendfile=false + +# Automatically import received PGP keys. +# Possible values: true, false (Default: false) +pgp.pubkey.autoimport=false + + +# ============================================================================== +# [omemo] - OMEMO Encryption (XEP-0384) +# ============================================================================== +[omemo] +# OMEMO conversation logging behavior. +# Possible values: on, redact, off (Default: on) +log=on + +# OMEMO usage policy. +# Possible values: manual, automatic, always (Default: automatic) +policy=automatic + +# Security device/key verification trust policy. +# Possible values: manual, first-use, blind (Default: manual) +trustmode=manual + + +# ============================================================================== +# [ox] - OpenPGP for XMPP Encryption (XEP-0373) +# ============================================================================== +[ox] +# OX conversation logging behavior. +# Possible values: on, redact, off (Default: on) +log=on + +# Automatically encrypt files sent during an OX session. +# Possible values: true, false (Default: true if OMEMO enabled, else false) +encryptfile=true + + +# ============================================================================== +# [spellcheck] - Interactive Spell Checking +# ============================================================================== +[spellcheck] +# Enable live terminal typing spell check checking. +# Possible values: true, false (Default: false) +enabled=false + +# Dictionary locale used for spell checks. +# Possible values: Language code, e.g., en_US, en_GB, de_DE (Default: en_US) +lang=en_US + + +# ============================================================================== +# [executables] - Executables +# ============================================================================== +[executables] +# Command used to open clicked or selected links. +# Possible values: Shell command with %u placeholder (Default: "xdg-open %u") +url.open.cmd=xdg-open %u + +# Command used to download/save incoming files/URLs. +# Possible values: Shell command with placeholders, or empty for built-in downloader. (Default: empty) +url.save.cmd= + +# Command used to display contact avatar image files. +# Possible values: Shell command with %p placeholder (Default: "xdg-open %p") +avatar.cmd=xdg-open %p + +# Command used to open vCard profile photos. +# Possible values: Shell command with %p placeholder (Default: "xdg-open %p") +vcard.photo.cmd=xdg-open %p + + +# ============================================================================== +# [alias] - Custom Command Shortcuts +# ============================================================================== +[alias] +colour=/color +topic=/subject +friends=/who online friends +bob=/msg bob@server.org hey wassup? diff --git a/scripts/changelog-helper.py b/scripts/changelog-helper.py index 9a97c3ef8..ff9550680 100755 --- a/scripts/changelog-helper.py +++ b/scripts/changelog-helper.py @@ -44,13 +44,31 @@ def get_last_tag(): return output[0] if output else None def get_commits(revision_range): - """Get list of (hash, subject) tuples.""" - lines = git_run(["log", revision_range, "--format=%H %s"]) + """Get list of (hash, subject, body) tuples.""" + try: + result = subprocess.run( + ["git", "log", revision_range, "--format=%H%n%s%n%b%n@@@COMMIT-DELIMITER@@@"], + capture_output=True, text=True, check=True + ) + raw_output = result.stdout.strip() + except subprocess.CalledProcessError: + return [] + commits = [] - for line in lines: - parts = line.split(' ', 1) - if len(parts) == 2: - commits.append(parts) + if not raw_output: + return commits + + raw_commits = raw_output.split("@@@COMMIT-DELIMITER@@@") + for raw_c in raw_commits: + raw_c = raw_c.strip() + if not raw_c: + continue + parts = raw_c.split('\n', 2) + if len(parts) >= 2: + c_hash = parts[0].strip() + c_subject = parts[1].strip() + c_body = parts[2].strip() if len(parts) == 3 else "" + commits.append((c_hash, c_subject, c_body)) return commits def get_pr_mappings(revision_range): @@ -97,14 +115,15 @@ def format_description(description): def main(): parser = argparse.ArgumentParser(description="Generate a sorted changelog from git commits.") parser.add_argument("--pr", action="store_true", help="Append PR number to each commit.") + parser.add_argument("--issue", action="store_true", help="Append issue number from Fixes: to each commit.") args = parser.parse_args() last_tag = get_last_tag() if not last_tag: print("No tags found in the repository.", file=sys.stderr) - + revision_range = f"{last_tag}..HEAD" if last_tag else "HEAD" - + commits = get_commits(revision_range) if not commits: print(f"No commits found since {last_tag if last_tag else 'the beginning'}.") @@ -114,25 +133,38 @@ def main(): # Conventional Commit regex: type(scope): description commit_re = re.compile(r'^(\w+)(?:\(([^)]+)\))?:\s*(.*)$') + # Regex to extract issue/PR number from Fixes: tags in body + fixes_re = re.compile(r'^[Ff]ixes:\s*(?:https?://\S+/(?:issues|pull)/|#)(\d+)', re.MULTILINE) grouped = defaultdict(list) others = [] - for c_hash, c_subject in commits: + for c_hash, c_subject, c_body in commits: # Skip merge commits in the output if c_subject.startswith(("Merge pull request", "Merge branch")): continue - pr_suffix = f" (#{pr_map[c_hash]})" if c_hash in pr_map else "" + suffix = "" + assoc_numbers = [] + if args.pr and c_hash in pr_map: + assoc_numbers.append(pr_map[c_hash]) + if args.issue: + fixes_numbers = fixes_re.findall(c_body) + for num in fixes_numbers: + if num not in assoc_numbers: + assoc_numbers.append(num) + if assoc_numbers: + suffix = f" (#" + ", #".join(assoc_numbers) + ")" + match = commit_re.match(c_subject) if match: ctype = match.group(1).lower() ctype = CORRECTIONS.get(ctype, ctype) description = format_description(match.group(3)) - grouped[ctype].append(f"{description}{pr_suffix}") + grouped[ctype].append(f"{description}{suffix}") else: - others.append(f"{c_subject}{pr_suffix}") + others.append(f"{c_subject}{suffix}") # Output sections in ordered priority all_types = TYPE_ORDER + sorted([t for t in grouped if t not in TYPE_ORDER]) diff --git a/src/chatlog.c b/src/chatlog.c index 5e83ff49a..38e7a11ca 100644 --- a/src/chatlog.c +++ b/src/chatlog.c @@ -417,10 +417,7 @@ static void _free_chat_log(struct dated_chat_log* dated_log) { if (dated_log) { - if (dated_log->filename) { - g_free(dated_log->filename); - dated_log->filename = NULL; - } + GFREE_SET_NULL(dated_log->filename); if (dated_log->date) { g_date_time_unref(dated_log->date); dated_log->date = NULL; diff --git a/src/command/cmd_ac.c b/src/command/cmd_ac.c index 29c119ac1..cd8d51b88 100644 --- a/src/command/cmd_ac.c +++ b/src/command/cmd_ac.c @@ -3605,14 +3605,6 @@ static char* _win_autocomplete(ProfWin* window, const char* const input, gboolean previous) { char* result = autocomplete_param_with_func(input, "/win", win_autocomplete, previous, NULL); - - if (result) { - return result; - } - - char* unquoted = strip_arg_quotes(input); - result = autocomplete_param_with_func(unquoted, "/win", roster_contact_autocomplete, previous, NULL); - free(unquoted); return result; } diff --git a/src/command/cmd_funcs.c b/src/command/cmd_funcs.c index 826599511..56a134a57 100644 --- a/src/command/cmd_funcs.c +++ b/src/command/cmd_funcs.c @@ -1188,6 +1188,11 @@ cmd_sub(ProfWin* window, const char* const command, gchar** args) jid = chatwin->barejid; } + if (!jid_is_valid_user_jid(jid)) { + cons_show_error("Not a valid JID: %s", jid); + return TRUE; + } + auto_jid Jid* jidp = jid_create(jid); if (!jidp) { cons_bad_cmd_usage(command); @@ -2864,6 +2869,8 @@ cmd_roster(ProfWin* window, const char* const command, gchar** args) gchar* jid = args[1]; if (jid == NULL) { cons_bad_cmd_usage(command); + } else if (!jid_is_valid_user_jid(jid)) { + cons_show_error("Not a valid JID: %s", jid); } else { gchar* name = args[2]; roster_send_add_new(jid, name); @@ -3011,6 +3018,11 @@ cmd_blocked(ProfWin* window, const char* const command, gchar** args) return TRUE; } + if (!jid_is_valid(jid)) { + cons_show_error("Not a valid JID: %s", jid); + return TRUE; + } + gboolean res = blocked_add(jid, br, NULL); if (!res) { cons_show("User %s already blocked.", jid); @@ -3025,6 +3037,11 @@ cmd_blocked(ProfWin* window, const char* const command, gchar** args) return TRUE; } + if (!jid_is_valid(args[1])) { + cons_show_error("Not a valid JID: %s", args[1]); + return TRUE; + } + gboolean res = blocked_remove(args[1]); if (!res) { cons_show("User %s is not currently blocked.", args[1]); @@ -3045,6 +3062,11 @@ cmd_blocked(ProfWin* window, const char* const command, gchar** args) return TRUE; } + if (!jid_is_valid(jid)) { + cons_show_error("Not a valid JID: %s", jid); + return TRUE; + } + if (argn >= 3) { msg = args[2]; } @@ -4745,7 +4767,8 @@ cmd_bookmark(ProfWin* window, const char* const command, gchar** args) cons_alert(NULL); return TRUE; } - if (strchr(jid, '@') == NULL) { + auto_jid Jid* jidp = jid_create(jid); + if (!jid_is_valid_user_jid(jid) || jidp->resourcepart != NULL) { cons_show("Invalid room, must be of the form room@domain.tld"); cons_show(""); cons_alert(NULL); diff --git a/src/config/account.c b/src/config/account.c index 76f96bfb2..8bb9137a3 100644 --- a/src/config/account.c +++ b/src/config/account.c @@ -160,8 +160,7 @@ account_eval_password(ProfAccount* account) g_strstrip(account->password); if (account->password[0] == '\0') { log_error("Empty password returned by `eval_password` command."); - g_free(account->password); - account->password = NULL; + GFREE_SET_NULL(account->password); return FALSE; } return TRUE; diff --git a/src/config/accounts.c b/src/config/accounts.c index 3938f4a3f..f0f7e4e32 100644 --- a/src/config/accounts.c +++ b/src/config/accounts.c @@ -824,12 +824,10 @@ accounts_set_last_activity(const char* const account_name) auto_gchar gchar* sanitized_account_name = _sanitize_account_name(account_name); if (_accounts_has_group(sanitized_account_name)) { GDateTime* nowdt = g_date_time_new_now_utc(); - GTimeVal nowtv; - gboolean res = g_date_time_to_timeval(nowdt, &nowtv); + auto_gchar gchar* timestr = prof_date_time_format_iso8601(nowdt); g_date_time_unref(nowdt); - if (res) { - auto_char char* timestr = g_time_val_to_iso8601(&nowtv); + if (timestr) { g_key_file_set_string(accounts, sanitized_account_name, "last.activity", timestr); _accounts_save(account_name); } diff --git a/src/config/cafile.c b/src/config/cafile.c index 8a43ba26b..a315a3cb3 100644 --- a/src/config/cafile.c +++ b/src/config/cafile.c @@ -74,8 +74,7 @@ cafile_get_name(void) * of servers you're trying to connect to are in your OS trust-store */ log_debug("[CAfile] file %s not created yet", cafile); - g_free(cafile); - cafile = NULL; + GFREE_SET_NULL(cafile); } return cafile; } diff --git a/src/database.c b/src/database.c index 41443e4f6..b9b39eef6 100644 --- a/src/database.c +++ b/src/database.c @@ -32,6 +32,7 @@ static sqlite3* g_chatlog_database; static gboolean _add_to_db(ProfMessage* message, char* type, const Jid* const from_jid, const Jid* const to_jid); static char* _get_db_filename(ProfAccount* account); static prof_msg_type_t _get_message_type_type(const char* const type); +static const char* _get_message_type_str(prof_msg_type_t type); static prof_enc_t _get_message_enc_type(const char* const encstr); static int _get_db_version(void); static gboolean _migrate_to_v2(void); @@ -256,31 +257,31 @@ log_database_add_outgoing_muc_pm(const char* const id, const char* const barejid _log_database_add_outgoing("mucpm", id, barejid, message, replace_id, enc); } -// Get info (timestamp and stanza_id) of the first or last message in db (personal chats) -ProfMessage* -log_database_get_limits_info(const gchar* const contact_barejid, gboolean is_last) +static ProfMessage* +_db_get_limits_info(prof_msg_type_t type, const char* const jid, gboolean is_last) { sqlite3_stmt* stmt = NULL; const Jid* myjid = connection_get_jid(); if (!myjid->str) return NULL; + const char* type_str = _get_message_type_str(type); const char* order = is_last ? "DESC" : "ASC"; auto_sqlite char* query = sqlite3_mprintf("SELECT `archive_id`, `timestamp` FROM `ChatLogs` WHERE " - "`type` = 'chat' AND (" + "`type` = %Q AND (" "(`from_jid` = %Q AND `to_jid` = %Q) OR " "(`from_jid` = %Q AND `to_jid` = %Q)) " "ORDER BY `timestamp` %s LIMIT 1;", - contact_barejid, myjid->barejid, myjid->barejid, contact_barejid, order); + type_str, jid, myjid->barejid, myjid->barejid, jid, order); if (!query) { - log_error("Could not allocate memory for SQL query in log_database_get_limits_info()"); + log_error("Could not allocate memory for SQL query in _db_get_limits_info()"); return NULL; } int rc = sqlite3_prepare_v2(g_chatlog_database, query, -1, &stmt, NULL); if (rc != SQLITE_OK) { - log_error("Unknown SQLite error in log_database_get_last_info()."); + log_error("Unknown SQLite error in _db_get_limits_info()."); return NULL; } @@ -301,49 +302,49 @@ log_database_get_limits_info(const gchar* const contact_barejid, gboolean is_las return msg; } +// Get info (timestamp and stanza_id) of the first or last message in db (personal chats) +ProfMessage* +log_database_get_limits_info(const gchar* const contact_barejid, gboolean is_last) +{ + return _db_get_limits_info(PROF_MSG_TYPE_CHAT, contact_barejid, is_last); +} + // Get info (timestamp and stanza_id) of the first or last message in db (MUCs) ProfMessage* log_database_get_limits_info_muc(const gchar* const room_jid, gboolean is_last) { - sqlite3_stmt* stmt = NULL; - const Jid* myjid = connection_get_jid(); - if (!myjid->str) - return NULL; - - const char* order = is_last ? "DESC" : "ASC"; - auto_sqlite char* query = sqlite3_mprintf("SELECT `archive_id`, `timestamp` FROM `ChatLogs` WHERE " - "`type` = 'muc' AND (" - "(`from_jid` = %Q AND `to_jid` = %Q) OR " - "(`from_jid` = %Q AND `to_jid` = %Q)) " - "ORDER BY `timestamp` %s LIMIT 1;", - room_jid, myjid->barejid, myjid->barejid, room_jid, order); - - if (!query) { - log_error("Could not allocate memory for SQL query in log_database_get_limits_info_muc()"); - return NULL; - } - - int rc = sqlite3_prepare_v2(g_chatlog_database, query, -1, &stmt, NULL); - if (rc != SQLITE_OK) { - log_error("Unknown SQLite error in log_database_get_limits_info_muc()."); - return NULL; - } + return _db_get_limits_info(PROF_MSG_TYPE_MUC, room_jid, is_last); +} - ProfMessage* msg = message_init(); +static GSList* +_db_parse_history_messages(sqlite3_stmt* stmt) +{ + GSList* history = NULL; - if (sqlite3_step(stmt) == SQLITE_ROW) { - char* archive_id = (char*)sqlite3_column_text(stmt, 0); + while (sqlite3_step(stmt) == SQLITE_ROW) { + char* message = (char*)sqlite3_column_text(stmt, 0); char* date = (char*)sqlite3_column_text(stmt, 1); + char* from_jid = (char*)sqlite3_column_text(stmt, 2); + char* from_resource = (char*)sqlite3_column_text(stmt, 3); + char* to_jid = (char*)sqlite3_column_text(stmt, 4); + char* to_resource = (char*)sqlite3_column_text(stmt, 5); + char* type = (char*)sqlite3_column_text(stmt, 6); + char* encryption = (char*)sqlite3_column_text(stmt, 7); + char* id = (char*)sqlite3_column_text(stmt, 8); - msg->stanzaid = _db_strdup(archive_id); + ProfMessage* msg = message_init(); + msg->id = id ? strdup(id) : NULL; + msg->from_jid = jid_create_from_bare_and_resource(from_jid, from_resource); + msg->to_jid = jid_create_from_bare_and_resource(to_jid, to_resource); + msg->plain = strdup(message ?: ""); msg->timestamp = g_date_time_new_from_iso8601(date, NULL); - } else { - message_free(msg); - msg = NULL; + msg->type = _get_message_type_type(type); + msg->enc = _get_message_enc_type(encryption); + + history = g_slist_append(history, msg); } - sqlite3_finalize(stmt); - return msg; + return history; } // Query previous chats, constraints start_time and end_time. If end_time is @@ -385,30 +386,7 @@ log_database_get_previous_chat(const gchar* const contact_barejid, const char* s return NULL; } - GSList* history = NULL; - - while (sqlite3_step(stmt) == SQLITE_ROW) { - char* message = (char*)sqlite3_column_text(stmt, 0); - char* date = (char*)sqlite3_column_text(stmt, 1); - char* from_jid = (char*)sqlite3_column_text(stmt, 2); - char* from_resource = (char*)sqlite3_column_text(stmt, 3); - char* to_jid = (char*)sqlite3_column_text(stmt, 4); - char* to_resource = (char*)sqlite3_column_text(stmt, 5); - char* type = (char*)sqlite3_column_text(stmt, 6); - char* encryption = (char*)sqlite3_column_text(stmt, 7); - char* id = (char*)sqlite3_column_text(stmt, 8); - - ProfMessage* msg = message_init(); - msg->id = id ? strdup(id) : NULL; - msg->from_jid = jid_create_from_bare_and_resource(from_jid, from_resource); - msg->to_jid = jid_create_from_bare_and_resource(to_jid, to_resource); - msg->plain = strdup(message ?: ""); - msg->timestamp = g_date_time_new_from_iso8601(date, NULL); - msg->type = _get_message_type_type(type); - msg->enc = _get_message_enc_type(encryption); - - history = g_slist_append(history, msg); - } + GSList* history = _db_parse_history_messages(stmt); sqlite3_finalize(stmt); return history; @@ -453,39 +431,30 @@ log_database_get_previous_muc(const gchar* const room_jid, const char* start_tim return NULL; } - GSList* history = NULL; - - while (sqlite3_step(stmt) == SQLITE_ROW) { - char* message = (char*)sqlite3_column_text(stmt, 0); - char* date = (char*)sqlite3_column_text(stmt, 1); - char* from_jid = (char*)sqlite3_column_text(stmt, 2); - char* from_resource = (char*)sqlite3_column_text(stmt, 3); - char* to_jid = (char*)sqlite3_column_text(stmt, 4); - char* to_resource = (char*)sqlite3_column_text(stmt, 5); - char* type = (char*)sqlite3_column_text(stmt, 6); - char* encryption = (char*)sqlite3_column_text(stmt, 7); - char* id = (char*)sqlite3_column_text(stmt, 8); + GSList* history = _db_parse_history_messages(stmt); + sqlite3_finalize(stmt); - ProfMessage* msg = message_init(); - msg->id = id ? strdup(id) : NULL; - msg->from_jid = jid_create_from_bare_and_resource(from_jid, from_resource); - msg->to_jid = jid_create_from_bare_and_resource(to_jid, to_resource); - msg->plain = strdup(message ?: ""); - msg->timestamp = g_date_time_new_from_iso8601(date, NULL); - msg->type = _get_message_type_type(type); - msg->enc = _get_message_enc_type(encryption); + return history; +} - history = g_slist_append(history, msg); +static int +_db_get_int_result(const char* query) +{ + sqlite3_stmt* stmt = NULL; + int count = 0; + int rc = sqlite3_prepare_v2(g_chatlog_database, query, -1, &stmt, NULL); + if (rc == SQLITE_OK) { + if (sqlite3_step(stmt) == SQLITE_ROW) { + count = sqlite3_column_int(stmt, 0); + } } sqlite3_finalize(stmt); - - return history; + return count; } int log_database_get_chat_count(const gchar* const contact_barejid, const char* start_time, const char* end_time) { - sqlite3_stmt* stmt = NULL; const Jid* myjid = connection_get_jid(); if (!myjid->str) return 0; @@ -497,21 +466,12 @@ log_database_get_chat_count(const gchar* const contact_barejid, const char* star "AND `timestamp` >= %Q AND `timestamp` <= %Q;", contact_barejid, myjid->barejid, myjid->barejid, contact_barejid, start_time, end_time); - int count = 0; - int rc = sqlite3_prepare_v2(g_chatlog_database, query, -1, &stmt, NULL); - if (rc == SQLITE_OK) { - if (sqlite3_step(stmt) == SQLITE_ROW) { - count = sqlite3_column_int(stmt, 0); - } - } - sqlite3_finalize(stmt); - return count; + return _db_get_int_result(query); } int log_database_get_muc_count(const gchar* const room_jid, const char* start_time, const char* end_time) { - sqlite3_stmt* stmt = NULL; const Jid* myjid = connection_get_jid(); if (!myjid->str) return 0; @@ -522,15 +482,7 @@ log_database_get_muc_count(const gchar* const room_jid, const char* start_time, "AND `timestamp` >= %Q AND `timestamp` <= %Q;", room_jid, room_jid, start_time, end_time); - int count = 0; - int rc = sqlite3_prepare_v2(g_chatlog_database, query, -1, &stmt, NULL); - if (rc == SQLITE_OK) { - if (sqlite3_step(stmt) == SQLITE_ROW) { - count = sqlite3_column_int(stmt, 0); - } - } - sqlite3_finalize(stmt); - return count; + return _db_get_int_result(query); } static const char* diff --git a/src/event/server_events.c b/src/event/server_events.c index 6ff8b153f..03d73ecf3 100644 --- a/src/event/server_events.c +++ b/src/event/server_events.c @@ -159,12 +159,10 @@ sv_ev_roster_received(void) int diff_secs = 0; if (prefs_get_boolean(PREF_LASTACTIVITY) && last_activity_str) { - GTimeVal lasttv; GDateTime* nowdt = g_date_time_new_now_utc(); - gboolean res = g_time_val_from_iso8601(last_activity_str, &lasttv); + GDateTime* lastdt = g_date_time_new_from_iso8601(last_activity_str, NULL); - if (res) { - GDateTime* lastdt = g_date_time_new_from_timeval_utc(&lasttv); + if (lastdt) { GTimeSpan diff_micros = g_date_time_difference(nowdt, lastdt); diff_secs = (diff_micros / 1000) / 1000; @@ -268,10 +266,9 @@ sv_ev_room_history(ProfMessage* message) int msg_is_new = 0; if (last_activity) { - GTimeVal lasttv; + GDateTime* lastdt = g_date_time_new_from_iso8601(last_activity, NULL); - if (g_time_val_from_iso8601(last_activity, &lasttv)) { - GDateTime* lastdt = g_date_time_new_from_timeval_utc(&lasttv); + if (lastdt) { GDateTime* msgdt = message->timestamp; GTimeSpan time_diff = g_date_time_difference(msgdt, lastdt); diff --git a/src/log.c b/src/log.c index 3eeeef371..757074e74 100644 --- a/src/log.c +++ b/src/log.c @@ -194,8 +194,7 @@ log_get_filter(void) void log_close(void) { - g_free(mainlogfile); - mainlogfile = NULL; + GFREE_SET_NULL(mainlogfile); if (logp) { fclose(logp); } diff --git a/src/omemo/omemo.c b/src/omemo/omemo.c index cca8d1410..c3d1335f4 100644 --- a/src/omemo/omemo.c +++ b/src/omemo/omemo.c @@ -2010,9 +2010,7 @@ static void _generate_signed_pre_key(void) { session_signed_pre_key* signed_pre_key; - struct timeval tv; - gettimeofday(&tv, NULL); - unsigned long long timestamp = (unsigned long long)(tv.tv_sec) * 1000 + (unsigned long long)(tv.tv_usec) / 1000; + unsigned long long timestamp = g_get_real_time() / 1000; omemo_ctx.signed_pre_key_id = 1; signal_protocol_key_helper_generate_signed_pre_key(&signed_pre_key, omemo_ctx.identity_key_pair, omemo_ctx.signed_pre_key_id, timestamp, omemo_ctx.signal); diff --git a/src/pgp/ox.c b/src/pgp/ox.c index 4d698a7d4..aa6374216 100644 --- a/src/pgp/ox.c +++ b/src/pgp/ox.c @@ -203,13 +203,8 @@ p_ox_gpg_signcrypt(const char* const sender_barejid, const char* const recipient gpgme_set_offline(ctx, 1); gpgme_set_keylist_mode(ctx, GPGME_KEYLIST_MODE_LOCAL); - char* xmpp_jid_me = alloca((strlen(sender_barejid) + 6) * sizeof(char)); - char* xmpp_jid_recipient = alloca((strlen(recipient_barejid) + 6) * sizeof(char)); - - strcpy(xmpp_jid_me, "xmpp:"); - strcpy(xmpp_jid_recipient, "xmpp:"); - strcat(xmpp_jid_me, sender_barejid); - strcat(xmpp_jid_recipient, recipient_barejid); + gchar* xmpp_jid_me = g_strconcat("xmpp:", sender_barejid, NULL); + gchar* xmpp_jid_recipient = g_strconcat("xmpp:", recipient_barejid, NULL); gpgme_signers_clear(ctx); @@ -269,6 +264,8 @@ p_ox_gpg_signcrypt(const char* const sender_barejid, const char* const recipient result = g_base64_encode((unsigned char*)cipher_str, len); cleanup: + g_free(xmpp_jid_me); + g_free(xmpp_jid_recipient); if (cipher_str) gpgme_free(cipher_str); if (plain) diff --git a/src/plugins/plugins.c b/src/plugins/plugins.c index ed6428380..fd9065d09 100644 --- a/src/plugins/plugins.c +++ b/src/plugins/plugins.c @@ -658,12 +658,7 @@ void plugins_on_room_history_message(const char* const barejid, const char* const nick, const char* const message, GDateTime* timestamp) { - char* timestamp_str = NULL; - GTimeVal timestamp_tv; - gboolean res = g_date_time_to_timeval(timestamp, ×tamp_tv); - if (res) { - timestamp_str = g_time_val_to_iso8601(×tamp_tv); - } + auto_gchar gchar* timestamp_str = prof_date_time_format_iso8601(timestamp); GList* values = g_hash_table_get_values(plugins); GList* curr = values; @@ -673,8 +668,6 @@ plugins_on_room_history_message(const char* const barejid, const char* const nic curr = g_list_next(curr); } g_list_free(values); - - g_free(timestamp_str); } char* diff --git a/src/ui/chatwin.c b/src/ui/chatwin.c index 47ead9b49..142f48e3d 100644 --- a/src/ui/chatwin.c +++ b/src/ui/chatwin.c @@ -14,6 +14,7 @@ #include #include +#include "common.h" #include "xmpp/chat_session.h" #include "window_list.h" #include "xmpp/roster_list.h" @@ -504,10 +505,7 @@ chatwin_set_enctext(ProfChatWin* chatwin, const char* const enctext) void chatwin_unset_enctext(ProfChatWin* chatwin) { - if (chatwin->enctext) { - free(chatwin->enctext); - chatwin->enctext = NULL; - } + FREE_SET_NULL(chatwin->enctext); } void @@ -522,10 +520,7 @@ chatwin_set_incoming_char(ProfChatWin* chatwin, const char* const ch) void chatwin_unset_incoming_char(ProfChatWin* chatwin) { - if (chatwin->incoming_char) { - free(chatwin->incoming_char); - chatwin->incoming_char = NULL; - } + FREE_SET_NULL(chatwin->incoming_char); } void @@ -540,10 +535,7 @@ chatwin_set_outgoing_char(ProfChatWin* chatwin, const char* const ch) void chatwin_unset_outgoing_char(ProfChatWin* chatwin) { - if (chatwin->outgoing_char) { - free(chatwin->outgoing_char); - chatwin->outgoing_char = NULL; - } + FREE_SET_NULL(chatwin->outgoing_char); } static void diff --git a/src/ui/console.c b/src/ui/console.c index 24ef80129..53a478cfd 100644 --- a/src/ui/console.c +++ b/src/ui/console.c @@ -342,6 +342,7 @@ cons_about(void) win_println(console, THEME_DEFAULT, "-", "This is free software; you are free to change and redistribute it."); win_println(console, THEME_DEFAULT, "-", "There is NO WARRANTY, to the extent permitted by law."); win_println(console, THEME_DEFAULT, "-", ""); + win_println(console, THEME_DEFAULT, "-", "Please consider supporting our development: "); win_println(console, THEME_DEFAULT, "-", "Type '/help' to show complete help."); win_println(console, THEME_DEFAULT, "-", ""); diff --git a/src/ui/statusbar.c b/src/ui/statusbar.c index 82f774bbd..ecc80cecd 100644 --- a/src/ui/statusbar.c +++ b/src/ui/statusbar.c @@ -24,6 +24,7 @@ #include "config/theme.h" #include "config/preferences.h" +#include "common.h" #include "ui/ui.h" #include "ui/statusbar.h" #include "ui/inputwin.h" @@ -471,10 +472,7 @@ _status_bar_draw_time(guint pos) return pos; } - if (statusbar->time) { - g_free(statusbar->time); - statusbar->time = NULL; - } + GFREE_SET_NULL(statusbar->time); GDateTime* datetime = g_date_time_new_now(tz); statusbar->time = g_date_time_format(datetime, time_pref); diff --git a/src/xmpp/iq.c b/src/xmpp/iq.c index 5d62361ef..04810657f 100644 --- a/src/xmpp/iq.c +++ b/src/xmpp/iq.c @@ -17,6 +17,7 @@ #include #include "profanity.h" +#include "common.h" #include "log.h" #include "config/preferences.h" #include "event/server_events.h" @@ -3034,20 +3035,20 @@ _mam_rsm_id_handler(xmpp_stanza_t* const stanza, void* const userdata) buffer_remove_entry(window->layout->buffer, 0); - auto_char char* start_str = NULL; + auto_gchar gchar* start_str = NULL; if (data->start_datestr) { - start_str = strdup(data->start_datestr); - // Convert to iso8601 - if (start_str && strlen(start_str) >= 3) { - start_str[strlen(start_str) - 3] = '\0'; + GDateTime* dt = g_date_time_new_from_iso8601(data->start_datestr, NULL); + if (dt) { + start_str = prof_date_time_format_iso8601(dt); + g_date_time_unref(dt); } } - auto_char char* end_str = NULL; + auto_gchar gchar* end_str = NULL; if (data->end_datestr) { - end_str = strdup(data->end_datestr); - // Convert to iso8601 - if (end_str && strlen(end_str) >= 3) { - end_str[strlen(end_str) - 3] = '\0'; + GDateTime* dt = g_date_time_new_from_iso8601(data->end_datestr, NULL); + if (dt) { + end_str = prof_date_time_format_iso8601(dt); + g_date_time_unref(dt); } } diff --git a/src/xmpp/jid.c b/src/xmpp/jid.c index 42cad031f..081db7754 100644 --- a/src/xmpp/jid.c +++ b/src/xmpp/jid.c @@ -231,26 +231,6 @@ create_fulljid(const gchar* const barejid, const gchar* const resource) } } -/* - * Get the nickname part of the full JID, e.g. - * Full JID = "test@conference.server/person" - * returns "person" - */ -gchar* -get_nick_from_full_jid(const gchar* const full_room_jid) -{ - auto_gcharv gchar** tokens = g_strsplit(full_room_jid, "/", 0); - gchar* nick_part = NULL; - - if (tokens) { - if (tokens[0] && tokens[1]) { - nick_part = strdup(tokens[1]); - } - } - - return nick_part; -} - /* * get the fulljid, fall back to the barejid */ diff --git a/src/xmpp/jid.h b/src/xmpp/jid.h index 6d7a70a80..fecb0adc2 100644 --- a/src/xmpp/jid.h +++ b/src/xmpp/jid.h @@ -37,8 +37,6 @@ void jid_auto_destroy(Jid** str); gboolean jid_is_valid_room_form(Jid* jid); gchar* create_fulljid(const gchar* const barejid, const gchar* const resource); -gchar* get_nick_from_full_jid(const gchar* const full_room_jid); - const gchar* jid_fulljid_or_barejid(Jid* jid); gchar* jid_random_resource(void); diff --git a/src/xmpp/message.c b/src/xmpp/message.c index f01d22497..d739486a2 100644 --- a/src/xmpp/message.c +++ b/src/xmpp/message.c @@ -1742,10 +1742,9 @@ message_is_sent_by_us(const ProfMessage* const message, bool checkOID) static xmpp_stanza_t* _ox_openpgp_signcrypt(xmpp_ctx_t* ctx, const char* const to, const char* const text) { - time_t now = time(NULL); - struct tm* tm = localtime(&now); - char buf[255]; - strftime(buf, sizeof(buf), "%FT%T%z", tm); + GDateTime* nowdt = g_date_time_new_now_local(); + auto_gchar gchar* buf = g_date_time_format(nowdt, "%FT%T%z"); + g_date_time_unref(nowdt); // build rpad int randnr = (rand() % 100) + 1; diff --git a/src/xmpp/stanza.c b/src/xmpp/stanza.c index c14e067dc..33f5beca3 100644 --- a/src/xmpp/stanza.c +++ b/src/xmpp/stanza.c @@ -1131,42 +1131,21 @@ stanza_get_delay(xmpp_stanza_t* const stanza) } static GDateTime* -_stanza_get_delay_timestamp_xep0203(xmpp_stanza_t* const delay_stanza) +_stanza_get_delay_timestamp(xmpp_stanza_t* const delay_stanza, const char* const expected_xmlns) { - GTimeVal utc_stamp; const char* xmlns = xmpp_stanza_get_attribute(delay_stanza, STANZA_ATTR_XMLNS); - if (xmlns && (g_strcmp0(xmlns, "urn:xmpp:delay") == 0)) { + if (xmlns && (g_strcmp0(xmlns, expected_xmlns) == 0)) { const char* stamp = xmpp_stanza_get_attribute(delay_stanza, STANZA_ATTR_STAMP); - if (stamp && (g_time_val_from_iso8601(stamp, &utc_stamp))) { - - GDateTime* datetime = g_date_time_new_from_iso8601(stamp, NULL); - GDateTime* local_datetime = g_date_time_to_local(datetime); - g_date_time_unref(datetime); - - return local_datetime; - } - } - - return NULL; -} - -static GDateTime* -_stanza_get_delay_timestamp_xep0091(xmpp_stanza_t* const x_stanza) -{ - GTimeVal utc_stamp; - const char* xmlns = xmpp_stanza_get_attribute(x_stanza, STANZA_ATTR_XMLNS); - - if (xmlns && (g_strcmp0(xmlns, "jabber:x:delay") == 0)) { - const char* stamp = xmpp_stanza_get_attribute(x_stanza, STANZA_ATTR_STAMP); - if (stamp && (g_time_val_from_iso8601(stamp, &utc_stamp))) { - + if (stamp) { GDateTime* datetime = g_date_time_new_from_iso8601(stamp, NULL); - GDateTime* local_datetime = g_date_time_to_local(datetime); - g_date_time_unref(datetime); + if (datetime) { + GDateTime* local_datetime = g_date_time_to_local(datetime); + g_date_time_unref(datetime); - return local_datetime; + return local_datetime; + } } } @@ -1186,7 +1165,7 @@ stanza_get_delay_from(xmpp_stanza_t* const stanza, gchar* from) } if (delay) { - return _stanza_get_delay_timestamp_xep0203(delay); + return _stanza_get_delay_timestamp(delay, STANZA_NS_DELAY); } // otherwise check for XEP-0091 legacy delayed delivery @@ -1198,7 +1177,7 @@ stanza_get_delay_from(xmpp_stanza_t* const stanza, gchar* from) } if (delay) { - return _stanza_get_delay_timestamp_xep0091(delay); + return _stanza_get_delay_timestamp(delay, STANZA_NS_X_DELAY); } return NULL; @@ -1216,7 +1195,7 @@ stanza_get_oldest_delay(xmpp_stanza_t* const stanza) child_name = xmpp_stanza_get_name(child); if (child_name && g_strcmp0(child_name, STANZA_NAME_DELAY) == 0) { - GDateTime* tmp = _stanza_get_delay_timestamp_xep0203(child); + GDateTime* tmp = _stanza_get_delay_timestamp(child, STANZA_NS_DELAY); if (oldest == NULL) { oldest = tmp; @@ -1229,7 +1208,7 @@ stanza_get_oldest_delay(xmpp_stanza_t* const stanza) } if (child_name && g_strcmp0(child_name, STANZA_NAME_X) == 0) { - GDateTime* tmp = _stanza_get_delay_timestamp_xep0091(child); + GDateTime* tmp = _stanza_get_delay_timestamp(child, STANZA_NS_X_DELAY); if (oldest == NULL) { oldest = tmp; diff --git a/src/xmpp/stanza.h b/src/xmpp/stanza.h index 466f61c8a..8d1130bee 100644 --- a/src/xmpp/stanza.h +++ b/src/xmpp/stanza.h @@ -203,6 +203,8 @@ #define STANZA_NS_RECEIPTS "urn:xmpp:receipts" #define STANZA_NS_SIGNED "jabber:x:signed" #define STANZA_NS_ENCRYPTED "jabber:x:encrypted" +#define STANZA_NS_DELAY "urn:xmpp:delay" +#define STANZA_NS_X_DELAY "jabber:x:delay" // XEP-0373: OpenPGP for XMPP #define STANZA_NS_OPENPGP_0 "urn:xmpp:openpgp:0" #define STANZA_NS_OPENPGP_0_PUBLIC_KEYS "urn:xmpp:openpgp:0:public-keys"