├── .appveyor.yml ├── .gitignore ├── .gitmodules ├── CHANGELOG.md ├── LICENSE ├── Makefile ├── README.md ├── headers └── jabber │ ├── adhoccommands.h │ ├── auth.h │ ├── auth_digest_md5.h │ ├── auth_scram.h │ ├── bosh.h │ ├── buddy.h │ ├── caps.h │ ├── chat.h │ ├── data.h │ ├── disco.h │ ├── facebook_roster.h │ ├── ibb.h │ ├── iq.h │ ├── jabber.h │ ├── jutil.h │ ├── message.h │ ├── namespaces.h │ ├── oob.h │ ├── parser.h │ ├── pep.h │ ├── ping.h │ ├── presence.h │ ├── roster.h │ ├── si.h │ ├── useravatar.h │ ├── usermood.h │ ├── usernick.h │ ├── usertune.h │ └── xdata.h ├── src ├── lurch.c ├── lurch.h ├── lurch_addr.c ├── lurch_addr.h ├── lurch_api.c ├── lurch_api.h ├── lurch_api_internal.h ├── lurch_cmd_ui.c ├── lurch_cmd_ui.h ├── lurch_crypto.c ├── lurch_crypto.h ├── lurch_util.c └── lurch_util.h └── test ├── test_lurch_api.c ├── test_lurch_crypto.c └── test_lurch_util.c /.appveyor.yml: -------------------------------------------------------------------------------- 1 | version: 0.7.0-{build} 2 | 3 | image: Ubuntu1804 4 | 5 | install: 6 | - sh: sudo apt-get update 7 | - sh: sudo apt-get install -y git cmake libpurple-dev libmxml-dev libxml2-dev libsqlite3-dev libgcrypt20-dev build-essential libglib2.0-dev libcmocka-dev --no-install-recommends 8 | - sh: git submodule update --init --recursive 9 | 10 | build_script: 11 | - sh: make 12 | 13 | test_script: 14 | - sh: CMOCKA_MESSAGE_OUTPUT=XML CMOCKA_XML_FILE=build/cmocka_results_%g.xml make test --ignore-errors 15 | 16 | after_test: 17 | - sh: find build/ -type f -name *.xml -exec curl -v -F "file=@$APPVEYOR_BUILD_FOLDER/{}" "https://ci.appveyor.com/api/testresults/junit/$APPVEYOR_JOB_ID" \; 18 | - sh: bash <(curl -s https://codecov.io/bash) -g test/ -B $APPVEYOR_REPO_BRANCH -b $APPVEYOR_BUILD_VERSION 19 | 20 | artifacts: 21 | - path: build/lurch.so 22 | name: lurch-$APPVEYOR_BUILD_VERSION-$APPVEYOR_REPO_COMMIT.so 23 | -------------------------------------------------------------------------------- /.gitignore: -------------------------------------------------------------------------------- 1 | build/ 2 | .vscode/ 3 | -------------------------------------------------------------------------------- /.gitmodules: -------------------------------------------------------------------------------- 1 | [submodule "lib/axc"] 2 | path = lib/axc 3 | url = ../axc 4 | [submodule "lib/libomemo"] 5 | path = lib/libomemo 6 | url = ../libomemo 7 | -------------------------------------------------------------------------------- /CHANGELOG.md: -------------------------------------------------------------------------------- 1 | # Changelog 2 | All notable changes to this project will be documented in this file. 3 | 4 | The format is based on [Keep a Changelog](https://keepachangelog.com/en/1.0.0/), 5 | and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0.html). 6 | 7 | ## [Unreleased] 8 | 9 | ### Fixed 10 | - Expose `lurch_util_axc_log_func()` in the `.h` file so its unit tests can properly access it, fixing a new compile error. 11 | - Remove unused variable from `lurch_bundle_request_cb()`. 12 | 13 | 14 | ## [0.7.0] - 2021-02-12 15 | ### Added 16 | - This file. 17 | - An API reachable through _libpurple_ signals. See `lurch_api.h` for details and usage. 18 | - Testing setup using _cmocka_ and tests for new modules. 19 | - CI setup running the tests in _appveyor_ and reporting coverage results to _codecov_. 20 | - The possibility to dynamically link against the submodule libaries. ([#151](https://github.com/gkdr/lurch/pull/151)) (thanks, [@fortysixandtwo](https://github.com/fortysixandtwo)!) 21 | 22 | ### Changed 23 | - A new `/command` handler using the API, replacing the old implementation. The commands are a bit different and some are new. 24 | - Updated _libomemo_ submodule to 0.7.1. See the [changelog](https://github.com/gkdr/libomemo/blob/master/CHANGELOG.md) for details. 25 | - Updated _axc_ submodule to 0.3.4. See the [changelog](https://github.com/gkdr/axc/blob/master/CHANGELOG.md) for details. 26 | 27 | ### Removed 28 | - The `lurch_initialised` setting in the `accounts.xml`. 29 | 30 | ### Fixed 31 | - Warnings are no longer displayed at level "error". 32 | - Use constants instead of magic numbers for conversation type checks. 33 | - Some forgotten module name parameters for libpurple logging functions. 34 | - Report skipped messages in conversation window and not just the debug log. ([#150](https://github.com/gkdr/lurch/pull/150)) (thanks, [@agx](https://github.com/agx)!) 35 | - Failing tests on certain platforms. ([#153](https://github.com/gkdr/lurch/pull/153)) (thanks, [@agx](https://github.com/agx)!) 36 | - Format the DB filenames in the README as code so they're not rendered as `mailto:` links. ([#130](https://github.com/gkdr/lurch/pull/130)) (thanks, [@msiism](https://github.com/msiism)!) 37 | - Some memory handling improvements. ([#160](https://github.com/gkdr/lurch/pull/160), [#161](https://github.com/gkdr/lurch/pull/161)) (thanks, [@root-hardenedvault](https://github.com/root-hardenedvault)!) 38 | 39 | ## [0.6.8] and below 40 | Lost to git commit logs and GitHub releases page. 41 | -------------------------------------------------------------------------------- /Makefile: -------------------------------------------------------------------------------- 1 | ### toolchain 2 | # 3 | CC ?= gcc 4 | 5 | PKG_CONFIG ?= pkg-config 6 | XML2_CONFIG ?= xml2-config 7 | LIBGCRYPT_CONFIG ?= libgcrypt-config 8 | 9 | MKDIR = mkdir 10 | MKDIR_P = mkdir -p 11 | INSTALL = install 12 | INSTALL_LIB = $(INSTALL) -m 755 13 | INSTALL_DIR = $(INSTALL) -d -m 755 14 | RM = rm 15 | RM_RF = $(RM) -rf 16 | CMAKE ?= cmake 17 | CMAKE_FLAGS = -DCMAKE_BUILD_TYPE=Debug -DCMAKE_C_FLAGS=-fPIC 18 | 19 | ### flags 20 | # 21 | GLIB_CFLAGS ?= $(shell $(PKG_CONFIG) --cflags glib-2.0) 22 | GLIB_LDFLAGS ?= $(shell $(PKG_CONFIG) --libs glib-2.0) 23 | 24 | LIBPURPLE_CFLAGS=$(shell $(PKG_CONFIG) --cflags purple) 25 | PURPLE_DIR=$(shell $(PKG_CONFIG) --variable=plugindir purple) 26 | LIBPURPLE_LDFLAGS=$(shell $(PKG_CONFIG) --cflags purple) \ 27 | -L$(PURPLE_DIR) 28 | 29 | LIBOMEMO_CFLAGS = $(shell $(PKG_CONFIG) --cflags libomemo) 30 | LIBOMEMO_LDFLAGS = $(shell $(PKG_CONFIG) --libs libomemo) 31 | 32 | LIBAXC_CFLAGS = $(shell $(PKG_CONFIG) --cflags libaxc) 33 | LIBAXC_LDFLAGS = $(shell $(PKG_CONFIG) --libs libaxc) 34 | 35 | LIBSIGNAL_PROTOCOL_CFLAGS = $(shell $(PKG_CONFIG) --cflags libsignal-protocol-c) 36 | LIBSIGNAL_PROTOCOL_LDFLAGS = $(shell $(PKG_CONFIG) --cflags libsignal-protocol-c) 37 | 38 | XML2_CFLAGS ?= $(shell $(XML2_CONFIG) --cflags) 39 | XML2_LDFLAGS ?= $(shell $(XML2_CONFIG) --libs) 40 | 41 | LIBGCRYPT_LDFLAGS ?= $(shell $(LIBGCRYPT_CONFIG) --libs) 42 | 43 | USE_DYNAMIC_LIBS=libsignal-protocol-c libaxc libomemo 44 | USE_DYNAMIC_LIBS:=$(shell pkg-config --exists $(USE_DYNAMIC_LIBS) && \ 45 | echo '$(USE_DYNAMIC_LIBS)') 46 | 47 | PKGCFG_C=$(GLIB_CFLAGS) \ 48 | $(LIBPURPLE_CFLAGS) \ 49 | $(XML2_CFLAGS) 50 | 51 | ifneq ($(USE_DYNAMIC_LIBS),) 52 | PKGCFG_C+=$(LIBOMEMO_CFLAGS) \ 53 | $(LIBAXC_CFLAGS) \ 54 | $(LIBSIGNAL_PROTOCOL_CFLAGS) 55 | endif 56 | 57 | 58 | PKGCFG_L=$(shell $(PKG_CONFIG) --libs sqlite3 mxml) \ 59 | $(GLIB_LDFLAGS) \ 60 | $(LIBPURPLE_LDFLAGS) \ 61 | $(XML2_LDFLAGS) \ 62 | $(LIBGCRYPT_LDFLAGS) 63 | 64 | ifneq ($(USE_DYNAMIC_LIBS),) 65 | PKGCFG_L+=$(LIBOMEMO_LDFLAGS) \ 66 | $(LIBAXC_LDFLAGS) \ 67 | $(LIBSIGNAL_PROTOCOL_LDFLAGS) 68 | endif 69 | 70 | 71 | ifneq ("$(wildcard /etc/redhat-release)","") 72 | LJABBER= -lxmpp 73 | else 74 | ifneq ("$(wildcard /etc/SuSE-release)","") 75 | LJABBER= -lxmpp 76 | else 77 | LJABBER= -ljabber 78 | endif 79 | endif 80 | 81 | ifeq ($(USE_DYNAMIC_LIBS),) 82 | HEADERS=-I$(HDIR)/jabber -I$(LOMEMO_SRC) -I$(AXC_SRC) -I$(AX_DIR)/src 83 | else 84 | HEADERS=-I$(HDIR)/jabber 85 | endif 86 | CFLAGS += -std=c11 -Wall -g -Wstrict-overflow $(PKGCFG_C) $(HEADERS) 87 | PLUGIN_CPPFLAGS=-DPURPLE_PLUGINS 88 | # -D_BSD_SOURCE can be removed once nobody uses glibc <= 2.18 any more 89 | CPPFLAGS += -D_XOPEN_SOURCE=700 -D_BSD_SOURCE -D_DEFAULT_SOURCE 90 | LDFLAGS += -ldl -lm $(PKGCFG_L) $(LJABBER) -Wl,-rpath,$(PURPLE_PLUGIN_DIR) 91 | LDFLAGS_T=$(LDFLAGS) -lpurple -lcmocka 92 | 93 | ### directories 94 | # 95 | PURPLE_HOME_PLUGIN_DIR=$(HOME)/.purple/plugins 96 | PURPLE_PLUGIN_DIR = $(shell $(PKG_CONFIG) --variable=plugindir purple) 97 | 98 | LDIR=./lib 99 | BDIR=./build 100 | SDIR=./src 101 | HDIR=./headers 102 | TDIR=./test 103 | 104 | LOMEMO_DIR=$(LDIR)/libomemo 105 | LOMEMO_SRC=$(LOMEMO_DIR)/src 106 | LOMEMO_BUILD=$(LOMEMO_DIR)/build 107 | LOMEMO_PATH=$(LOMEMO_BUILD)/libomemo-conversations.a 108 | 109 | AXC_DIR=$(LDIR)/axc 110 | AXC_SRC=$(AXC_DIR)/src 111 | AXC_BUILD=$(AXC_DIR)/build 112 | AXC_PATH=$(AXC_BUILD)/libaxc-nt.a 113 | 114 | AX_DIR=$(AXC_DIR)/lib/libsignal-protocol-c 115 | AX_PATH=$(AX_DIR)/build/src/libsignal-protocol-c.a 116 | 117 | SOURCES := $(sort $(wildcard $(SDIR)/*.c)) 118 | OBJECTS := $(patsubst $(SDIR)/%.c, $(BDIR)/%.o, $(SOURCES)) 119 | OBJECTS_W_COVERAGE := $(patsubst $(SDIR)/%.c, $(BDIR)/%_w_coverage.o, $(SOURCES)) 120 | TEST_SOURCES := $(sort $(wildcard $(TDIR)/test_*.c)) 121 | TEST_OBJECTS := $(patsubst $(TDIR)/test_%.c, $(BDIR)/test_%.o, $(TEST_SOURCES)) 122 | TEST_TARGETS := $(patsubst $(TDIR)/test_%.c, $(BDIR)/test_%, $(TEST_SOURCES)) 123 | ifeq ($(USE_DYNAMIC_LIBS),) 124 | VENDOR_LIBS=$(LOMEMO_PATH) $(AXC_PATH) $(AX_PATH) 125 | endif 126 | 127 | ### make rules 128 | # 129 | all: $(BDIR)/lurch.so 130 | 131 | $(BDIR): 132 | $(MKDIR_P) build 133 | 134 | $(AX_PATH): 135 | cd $(AX_DIR)/ && \ 136 | $(MKDIR_P) build && \ 137 | cd build && \ 138 | $(CMAKE) $(CMAKE_FLAGS) .. \ 139 | && $(MAKE) 140 | 141 | $(AXC_PATH): 142 | $(MAKE) -C "$(AXC_DIR)" build/libaxc-nt.a 143 | 144 | $(LOMEMO_PATH): 145 | $(MAKE) -C "$(LOMEMO_DIR)" build/libomemo-conversations.a 146 | 147 | $(BDIR)/%.o: $(SDIR)/%.c | $(BDIR) 148 | $(CC) -fPIC $(CFLAGS) $(CPPFLAGS) $(PLUGIN_CPPFLAGS) -c $(SDIR)/$*.c -o $@ 149 | 150 | $(BDIR)/%_w_coverage.o: $(SDIR)/%.c | $(BDIR) 151 | $(CC) -O0 --coverage $(CFLAGS) $(CPPFLAGS) $(PLUGIN_CPPFLAGS) -c $(SDIR)/$*.c -o $@ 152 | 153 | $(BDIR)/test_%.o: $(TDIR)/test_%.c | $(BDIR) 154 | $(CC) $(CFLAGS) -O0 -c $(TDIR)/test_$*.c -o $@ 155 | 156 | $(BDIR)/lurch.so: $(OBJECTS) $(VENDOR_LIBS) 157 | $(CC) -fPIC -shared $(CFLAGS) $(CPPFLAGS) $(PLUGIN_CPPFLAGS) \ 158 | $^ \ 159 | -o $@ $(LDFLAGS) 160 | $(BDIR)/lurch.a: $(BDIR)/lurch.o $(VENDOR_LIBS) 161 | $(AR) rcs $@ $^ 162 | 163 | install: $(BDIR)/lurch.so 164 | [ -e "$(DESTDIR)/$(PURPLE_PLUGIN_DIR)" ] || \ 165 | $(INSTALL_DIR) "$(DESTDIR)/$(PURPLE_PLUGIN_DIR)" 166 | $(INSTALL_LIB) "$(BDIR)/lurch.so" "$(DESTDIR)/$(PURPLE_PLUGIN_DIR)/lurch.so" 167 | 168 | install-home: $(BDIR)/lurch.so 169 | [ -e "$(PURPLE_HOME_PLUGIN_DIR)" ] || \ 170 | $(INSTALL_DIR) "$(PURPLE_HOME_PLUGIN_DIR)" 171 | $(INSTALL_LIB) "$(BDIR)/lurch.so" "$(PURPLE_HOME_PLUGIN_DIR)/lurch.so" 172 | 173 | 174 | LURCH_VERSION ?= 0.0.0 175 | TARBALL_DIR_NAME=lurch-$(LURCH_VERSION) 176 | TARBALL_FILE_NAME=$(TARBALL_DIR_NAME)-src.tar.gz 177 | 178 | tarball: | clean-all $(BDIR) 179 | $(MKDIR) $(TARBALL_DIR_NAME) 180 | rsync -av --progress . $(TARBALL_DIR_NAME)/ --exclude $(TARBALL_DIR_NAME)/ --exclude-from=.gitignore 181 | -find $(TARBALL_DIR_NAME)/ -name "*.git*" -exec rm -rf "{}" \; 182 | tar czf $(TARBALL_FILE_NAME) $(TARBALL_DIR_NAME)/ 183 | mv $(TARBALL_FILE_NAME) $(TARBALL_DIR_NAME)/ 184 | mv $(TARBALL_DIR_NAME) $(BDIR)/ 185 | 186 | $(BDIR)/test_lurch_util: $(OBJECTS_W_COVERAGE) $(VENDOR_LIBS) $(BDIR)/test_lurch_util.o 187 | $(CC) $(CFLAGS) $(CPPFLAGS) -O0 --coverage $^ $(PURPLE_DIR)/libjabber.so.0 -o $@ $(LDFLAGS_T) \ 188 | -Wl,--wrap=purple_user_dir \ 189 | -Wl,--wrap=purple_prefs_get_bool \ 190 | -Wl,--wrap=purple_prefs_get_int \ 191 | -Wl,--wrap=purple_debug_error \ 192 | -Wl,--wrap=purple_debug_info \ 193 | -Wl,--wrap=purple_debug_misc \ 194 | -Wl,--wrap=purple_base16_encode_chunked 195 | sh -c "set -o pipefail; $@ 2>&1 | grep -Ev ".*CRITICAL.*" | tr -s '\n'" # filter annoying and irrelevant glib output 196 | 197 | $(BDIR)/test_lurch_api: $(OBJECTS_W_COVERAGE) $(VENDOR_LIBS) $(BDIR)/test_lurch_api.o 198 | $(CC) $(CFLAGS) $(CPPFLAGS) -O0 --coverage $^ $(PURPLE_DIR)/libjabber.so.0 -o $@ $(LDFLAGS_T) \ 199 | -Wl,--wrap=purple_account_get_username \ 200 | -Wl,--wrap=purple_account_get_connection \ 201 | -Wl,--wrap=purple_connection_get_protocol_data \ 202 | -Wl,--wrap=purple_signal_register \ 203 | -Wl,--wrap=purple_signal_unregister \ 204 | -Wl,--wrap=purple_signal_connect \ 205 | -Wl,--wrap=purple_signal_disconnect \ 206 | -Wl,--wrap=purple_find_conversation_with_account \ 207 | -Wl,--wrap=jabber_pep_publish \ 208 | -Wl,--wrap=jabber_chat_find_by_conv \ 209 | -Wl,--wrap=jabber_iq_send \ 210 | -Wl,--wrap=axc_get_device_id \ 211 | -Wl,--wrap=axc_key_load_public_own \ 212 | -Wl,--wrap=axc_key_load_public_addr \ 213 | -Wl,--wrap=axc_session_exists_any \ 214 | -Wl,--wrap=omemo_storage_user_devicelist_retrieve \ 215 | -Wl,--wrap=omemo_storage_chatlist_delete \ 216 | -Wl,--wrap=omemo_storage_chatlist_save \ 217 | -Wl,--wrap=omemo_storage_chatlist_exists \ 218 | -Wl,--wrap=omemo_storage_user_devicelist_retrieve \ 219 | -Wl,--wrap=lurch_util_fp_get_printable 220 | sh -c "set -o pipefail; $@ 2>&1 | grep -Ev ".*CRITICAL.*" | tr -s '\n'" # filter annoying and irrelevant glib output 221 | 222 | $(BDIR)/test_lurch_crypto: $(OBJECTS_W_COVERAGE) $(VENDOR_LIBS) $(BDIR)/test_lurch_crypto.o 223 | $(CC) $(CFLAGS) $(CPPFLAGS) -O0 --coverage $^ $(PURPLE_DIR)/libjabber.so.0 -o $@ $(LDFLAGS_T) \ 224 | -Wl,--wrap=axc_message_encrypt_and_serialize \ 225 | -Wl,--wrap=axc_session_exists_initiated 226 | sh -c "set -o pipefail; $@ 2>&1 | grep -Ev ".*CRITICAL.*" | tr -s '\n'" # filter annoying and irrelevant glib output 227 | 228 | test: $(OBJECTS_W_COVERAGE) $(VENDOR_LIBS) $(TEST_TARGETS) 229 | 230 | coverage: test 231 | gcovr -r . --html --html-details -o build/coverage.html 232 | gcovr -r . -s 233 | 234 | clean: 235 | $(RM_RF) "$(BDIR)" 236 | 237 | clean-all: clean 238 | $(MAKE) -C "$(LOMEMO_DIR)" clean 239 | $(MAKE) -C "$(AXC_DIR)" clean-all 240 | 241 | .PHONY: clean clean-all install install-home tarball test coverage 242 | 243 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | # lurch 0.7.0 2 | /lʊʁç/. In German, an Axolotl is a type of Lurch, which simply means 'amphibian'. This plugin brings _Axolotl_, by now renamed to _double ratchet_, to _libpurple_ applications such as [Pidgin](https://www.pidgin.im/) by implementing the [XEP-0384: OMEMO Encryption](https://xmpp.org/extensions/xep-0384.html). For a higher-level overview, see [the official OMEMO homepage](https://conversations.im/omemo/). 3 | 4 | (Plus I thought the word sounds funny.) 5 | 6 | ## News 7 | This section is now the [changelog](https://github.com/gkdr/lurch/blob/master/CHANGELOG.md). 8 | 9 | ## Table of Contents 10 | 1. [Installation](#installation) 11 | 1. [Linux](#linux) 12 | 1. [Windows](#windows) 13 | 1. [MacOS](#macos) 14 | 1. [Additional plugins](#additional-plugins) 15 | 1. [Usage](#usage) 16 | 1. [General](#general) 17 | 1. [Group Chats](#group-chats) 18 | 1. [Uninstallation](#uninstallation) 19 | 1. [Troubleshooting](#troubleshooting) 20 | 1. [FAQ](#faq) 21 | 1. [Caveats](#caveats) 22 | 23 | ## Installation 24 | ### Linux 25 | ##### 1. Install the (submodules') dependencies 26 | Below you can find the command to install the dependencies for popular distribution families. Make sure that you use at least version 2.7 of _mxml_, and 2.10.10 of _libpurple_. 27 | 28 | __Debian, Ubuntu__ 29 | ``` bash 30 | sudo apt install git cmake libpurple-dev libmxml-dev libxml2-dev libsqlite3-dev libgcrypt20-dev build-essential 31 | ``` 32 | __ArchLinux, Parabola__ 33 | ``` bash 34 | sudo pacman -S base-devel git cmake pidgin libpurple mxml libxml2 sqlite libgcrypt 35 | ``` 36 | __Fedora__ 37 | ``` bash 38 | sudo dnf install git cmake libpurple-devel mxml-devel libxml2-devel libsqlite3x-devel libgcrypt-devel 39 | ``` 40 | 41 | ##### 2A. EITHER: Build and install from source 42 | ``` bash 43 | git clone https://github.com/gkdr/lurch/ 44 | cd lurch 45 | git submodule update --init --recursive 46 | make install-home 47 | ``` 48 | If you just pull a newer version (`git pull`), remember to also update the submodules as they might have changed! 49 | 50 | The last command compiles the whole thing and copies the plugin into your local _libpurple_ plugin directory. 51 | 52 | The next time you start Pidgin, or another libpurple client, you should be able to activate it in the "Plugins" window. 53 | 54 | ##### 2B. OR: Install from a community repo 55 | * Arch - AUR: https://aur.archlinux.org/packages/libpurple-lurch-git/ 56 | * Fedora - COPR: https://copr.fedorainfracloud.org/coprs/treba/pidgin-lurch/ 57 | 58 | 59 | ### Windows 60 | Thanks to [EionRobb](https://github.com/EionRobb), Windows users can use the dlls he compiled and provides here: https://eion.robbmob.com/lurch/ 61 | 62 | 1. Download the plugin (_lurch.dll_) and put it in the `Program Files (x86)\Pidgin\plugins` directory. 63 | 2. Download _libgcrypt-20.dll_ and _libgpg-error-0.dll_ and put them in the `Program Files (x86)\Pidgin` directory. 64 | 65 | These instructions can also be found at the provided link. 66 | 67 | ### MacOS 68 | Homebrew should have all dependencies: 69 | 70 | ``` 71 | brew install cmake pidgin glib libxml2 libmxml sqlite libgcrypt 72 | ``` 73 | This should work on newer versions of MacOS, but if you run into problems check out [#8](https://github.com/gkdr/lurch/issues/8#issuecomment-285937828) for some hints. Complete instructions on how to get this running with Pidgin appreciated! 74 | 75 | Alternatively, if you use Adium, you should definitely check out [shtrom](https://github.com/shtrom)'s [Lurch4Adium](https://github.com/shtrom/Lurch4Adium)! 76 | 77 | ### Additional plugins 78 | The current version of _libpurple_'s _XMPP_ protocol plugin does not support many _XEPs_ by itself. For more features and compatibility with other clients such as _Conversations_ you can install the pulgins below. 79 | 80 | #### carbons 81 | If you have multiple devices and want messages sent and received by one device show up on all others, [XEP-0280: Message Carbons](https://xmpp.org/extensions/xep-0280.html) is what you are looking for. 82 | 83 | You can find my plugin for it here: https://github.com/gkdr/carbons 84 | 85 | #### pidgin-xmpp-receipts 86 | In order to support the checkmarks for delivered messages, you could install this plugin implementing [XEP-0184: Message Delivery Receipts](https://xmpp.org/extensions/xep-0184.html): 87 | 88 | https://app.assembla.com/spaces/pidgin-xmpp-receipts/git/source 89 | 90 | ## Usage 91 | ### General 92 | The first thing you can do to check if this plugin works is enter the `/lurch help` command in any conversation window. You will receive a list of the other commands you can use. I know this is a bit clunky, but using the command interface for interactions makes the plugin usable in clients that do not have a GUI. 93 | 94 | After you have made sure it was installed correctly, you do not have to activate it specifically for each conversation partner you want to use it with, unlike with e.g. _OTR_. If it detects that the other side is using _OMEMO_ (by the existence of an _OMEMO_ devicelist), the conversation will be encrypted automatically. If you do not want this, you can blacklist the user by typing `/lurch blacklist add` in the conversation window. 95 | 96 | This plugin will set the window title to notify the user if encryption is enabled or not. If it is, it will generally not send plaintext messages. If a plaintext message is received in a chat that is supposed to be encrypted, the user will be warned. 97 | 98 | ### Group Chats 99 | Group chats (via [XEP-0045: Multi-User Chat](https://xmpp.org/extensions/xep-0045.html) aka MUCs) are __not__ part of the _OMEMO_ specification, but can work under specific circumstances as outlined on the [_Conversations_ README](https://github.com/siacs/Conversations/blob/master/README.md#omemo). These are: 100 | * The MUC has to be non-anonymous so the real JID of each participant is visible. The channel owner has to set this property. In Pidgin you can get there by typing `/config`. 101 | * Every participant has to be in every other participant's contact list! This is why this really only makes sense for member-only MUCs. 102 | 103 | Once you have confirmed these conditions are met, every member has to activate _OMEMO_ him- or herself. Using this plugin it works by typing `/lurch enable`. Warning messages are displayed if it does not work for every user in the conference, hopefully helping to fix the issue. 104 | 105 | It is __recommended__ you confirm the fingerprints look the same on each device, including among your own. To do this, you can e.g. display all fingerprints participating in a conversation using `/lurch show fp conv`. 106 | 107 | ## Uninstallation 108 | In order to uninstall this plugin, you should call the `/lurch uninstall` command. 109 | It will remove this client from the _OMEMO_ device list, notifying other _OMEMO_ clients that they do not need to encrypt messages for it any longer. 110 | 111 | Afterwards, you can just deactivate the plugin in the _Tools > Plugins_ window. 112 | 113 | In order to completely remove all data related to this plugin, e.g. for a fresh installation, you will need to delete the following files from your _.purple_ directory: 114 | * `your@xmpp.account_omemo_db.sqlite` to delete the device list cache 115 | * `your@xmpp.account_axc_db.sqlite` to delete your keypair and all sessions with other devices 116 | * and finally, `lurch.[so|dll]` found in the _plugins_ directory 117 | 118 | 119 | ## Troubleshooting 120 | If something does not work as expected, don't hesitate to open an issue. 121 | You can also reach me on the Pidgin IRC channel (#pidgin on freenode) as `riba`, or send me an email. 122 | 123 | It will usually be helpful (i.e. I will probably ask for it anyway) if you provide me with some information from the debug log, which you can find at _Help > Debug Window_ in Pidgin. 124 | There, you will see a scary error from the XML parser every time you receive a message, which you can safely ignore. It is due to the nonstandard namespace used by _OMEMO_ and looks something like this: `jabber: XML parser error for JabberStream 0x5631ed678670: Domain 3, code 100, level 1: xmlns: URI eu.siacs.conversations.axolotl is not absolute`. 125 | 126 | In addition to just reading logs, you can get a bit more active, as again I will probably ask for this anyway. 127 | Pidgin comes with an XMPP console, but you have to activate the plugin first (_Tools > Plugins_). 128 | Afterwards you can find it at _Tools > XMPP Console_ and send queries to the server. 129 | 130 | If you are having trouble sending or receiving messages, you should look up if you can find the device in the _device list_. You can do so by pasting the following into the XMPP console, replacing the `to` attribute with the device's owner's JID: 131 | ```XML 132 | 135 | 136 | 137 | 138 | 139 | ``` 140 | 141 | Sometimes, a device might be on the list, but it did not publish a _bundle_, which is necessary to establish a session. In this case, you can query this bundle by pasting the following, replacing the `to` attribute as well as the `DEVICE_ID` suffix of the `bundles` node: 142 | 143 | ```XML 144 | 147 | 148 | 149 | 150 | 151 | ``` 152 | 153 | Finally, in case it is more serious and Pidgin crashes, I will have to ask you for a backtrace. 154 | You can obtain it in the following way: 155 | * Open Pidgin in gdb: `gdb pidgin` 156 | * Run it: `run` 157 | * Do whatever you were doing to make it crash 158 | * When it does crash, type `bt` (or `backtrace`) 159 | * Copy the whole thing 160 | 161 | ## FAQ 162 | ### Can it talk to other OMEMO clients? 163 | __Yes__, it was (briefly) tested with: 164 | * [Conversations](https://conversations.im/) (Android) 165 | * [The gajim OMEMO plugin](https://dev.gajim.org/gajim/gajim-plugins/wikis/OmemoGajimPlugin) (Desktop OSs) 166 | * [ChatSecure](https://chatsecure.org/) (iOS) 167 | 168 | See https://omemo.top/ for additional clients. 169 | 170 | ### Does it work with Finch? 171 | It should, but I only tried it briefly. 172 | 173 | ## Caveats 174 | _OMEMO_ is not 'whatever Conversations can do', but a very specific _XEP_. 175 | 176 | For instance, if you don't install the additional plugin mentioned above, this is probably not the right thing to use if you have multiple clients running at the same time, as there is no message carbons support in libpurple as of now. 177 | 178 | At the moment, there is no [XEP-0313: Message Archive Management](https://xmpp.org/extensions/xep-0313.html) aka _MAM_ support in _libpurple_, so there are no 'offline messages'. 179 | 180 | Finally, I can't stress this enough: This plugin is _highly experimental_, so you __should not trust your life on it__. 181 | -------------------------------------------------------------------------------- /headers/jabber/adhoccommands.h: -------------------------------------------------------------------------------- 1 | /* 2 | * purple - Jabber Protocol Plugin 3 | * 4 | * Purple is the legal property of its developers, whose names are too numerous 5 | * to list here. Please refer to the COPYRIGHT file distributed with this 6 | * source distribution. 7 | * 8 | * This program is free software; you can redistribute it and/or modify 9 | * it under the terms of the GNU General Public License as published by 10 | * the Free Software Foundation; either version 2 of the License, or 11 | * (at your option) any later version. 12 | * 13 | * This program is distributed in the hope that it will be useful, 14 | * but WITHOUT ANY WARRANTY; without even the implied warranty of 15 | * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the 16 | * GNU General Public License for more details. 17 | * 18 | * You should have received a copy of the GNU General Public License 19 | * along with this program; if not, write to the Free Software 20 | * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02111-1301 USA 21 | * 22 | */ 23 | 24 | #ifndef PURPLE_JABBER_ADHOCCOMMANDS_H_ 25 | #define PURPLE_JABBER_ADHOCCOMMANDS_H_ 26 | 27 | #include "jabber.h" 28 | 29 | /* Implementation of XEP-0050 */ 30 | 31 | void jabber_adhoc_disco_result_cb(JabberStream *js, const char *from, 32 | JabberIqType type, const char *id, 33 | xmlnode *packet, gpointer data); 34 | 35 | void jabber_adhoc_execute(JabberStream *js, JabberAdHocCommands *cmd); 36 | 37 | void jabber_adhoc_execute_action(PurpleBlistNode *node, gpointer data); 38 | 39 | void jabber_adhoc_got_list(JabberStream *js, const char *from, xmlnode *query); 40 | 41 | void jabber_adhoc_server_get_list(JabberStream *js); 42 | 43 | void jabber_adhoc_init_server_commands(JabberStream *js, GList **m); 44 | 45 | #endif /* PURPLE_JABBER_ADHOCCOMMANDS_H_ */ 46 | -------------------------------------------------------------------------------- /headers/jabber/auth.h: -------------------------------------------------------------------------------- 1 | /** 2 | * @file auth.h Authentication routines 3 | * 4 | * purple 5 | * 6 | * Purple is the legal property of its developers, whose names are too numerous 7 | * to list here. Please refer to the COPYRIGHT file distributed with this 8 | * source distribution. 9 | * 10 | * This program is free software; you can redistribute it and/or modify 11 | * it under the terms of the GNU General Public License as published by 12 | * the Free Software Foundation; either version 2 of the License, or 13 | * (at your option) any later version. 14 | * 15 | * This program is distributed in the hope that it will be useful, 16 | * but WITHOUT ANY WARRANTY; without even the implied warranty of 17 | * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the 18 | * GNU General Public License for more details. 19 | * 20 | * You should have received a copy of the GNU General Public License 21 | * along with this program; if not, write to the Free Software 22 | * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02111-1301 USA 23 | */ 24 | #ifndef PURPLE_JABBER_AUTH_H_ 25 | #define PURPLE_JABBER_AUTH_H_ 26 | 27 | typedef struct _JabberSaslMech JabberSaslMech; 28 | 29 | #include "jabber.h" 30 | #include "xmlnode.h" 31 | 32 | typedef enum { 33 | JABBER_SASL_STATE_FAIL = -1, /* Abort, Retry, Fail? */ 34 | JABBER_SASL_STATE_OK = 0, /* Hooray! */ 35 | JABBER_SASL_STATE_CONTINUE = 1 /* More authentication required */ 36 | } JabberSaslState; 37 | 38 | struct _JabberSaslMech { 39 | gint8 priority; /* Higher priority will be tried before lower priority */ 40 | const gchar *name; 41 | JabberSaslState (*start)(JabberStream *js, xmlnode *mechanisms, xmlnode **reply, char **msg); 42 | JabberSaslState (*handle_challenge)(JabberStream *js, xmlnode *packet, xmlnode **reply, char **msg); 43 | JabberSaslState (*handle_success)(JabberStream *js, xmlnode *packet, char **msg); 44 | JabberSaslState (*handle_failure)(JabberStream *js, xmlnode *packet, xmlnode **reply, char **msg); 45 | void (*dispose)(JabberStream *js); 46 | }; 47 | 48 | void jabber_auth_start(JabberStream *js, xmlnode *packet); 49 | void jabber_auth_start_old(JabberStream *js); 50 | void jabber_auth_handle_challenge(JabberStream *js, xmlnode *packet); 51 | void jabber_auth_handle_success(JabberStream *js, xmlnode *packet); 52 | void jabber_auth_handle_failure(JabberStream *js, xmlnode *packet); 53 | 54 | JabberSaslMech *jabber_auth_get_plain_mech(void); 55 | JabberSaslMech *jabber_auth_get_digest_md5_mech(void); 56 | JabberSaslMech **jabber_auth_get_scram_mechs(gint *count); 57 | #ifdef HAVE_CYRUS_SASL 58 | JabberSaslMech *jabber_auth_get_cyrus_mech(void); 59 | #endif 60 | 61 | void jabber_auth_add_mech(JabberSaslMech *); 62 | void jabber_auth_remove_mech(JabberSaslMech *); 63 | 64 | void jabber_auth_init(void); 65 | void jabber_auth_uninit(void); 66 | 67 | #endif /* PURPLE_JABBER_AUTH_H_ */ 68 | -------------------------------------------------------------------------------- /headers/jabber/auth_digest_md5.h: -------------------------------------------------------------------------------- 1 | /** 2 | * @file auth_digest_md5.h Implementation of SASL DIGEST-MD5 authentication 3 | * 4 | * purple 5 | * 6 | * Purple is the legal property of its developers, whose names are too numerous 7 | * to list here. Please refer to the COPYRIGHT file distributed with this 8 | * source distribution. 9 | * 10 | * This program is free software; you can redistribute it and/or modify 11 | * it under the terms of the GNU General Public License as published by 12 | * the Free Software Foundation; either version 2 of the License, or 13 | * (at your option) any later version. 14 | * 15 | * This program is distributed in the hope that it will be useful, 16 | * but WITHOUT ANY WARRANTY; without even the implied warranty of 17 | * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the 18 | * GNU General Public License for more details. 19 | * 20 | * You should have received a copy of the GNU General Public License 21 | * along with this program; if not, write to the Free Software 22 | * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02111-1301 USA 23 | */ 24 | #ifndef PURPLE_JABBER_AUTH_DIGEST_MD5_H_ 25 | #define PURPLE_JABBER_AUTH_DIGEST_MD5_H_ 26 | 27 | #include "internal.h" 28 | 29 | /* 30 | * Every function in this file is ONLY exposed for tests. 31 | * DO NOT USE ANYTHING HERE OR YOU WILL BE SENT TO THE PIT OF DESPAIR. 32 | */ 33 | 34 | /* 35 | * Parse a DIGEST-MD5 challenge. 36 | */ 37 | GHashTable *jabber_auth_digest_md5_parse(const char *challenge); 38 | 39 | #endif /* PURPLE_JABBER_AUTH_DIGEST_MD5_H_ */ 40 | -------------------------------------------------------------------------------- /headers/jabber/auth_scram.h: -------------------------------------------------------------------------------- 1 | /** 2 | * @file auth_scram.h Implementation of SASL-SCRAM authentication 3 | * 4 | * purple 5 | * 6 | * Purple is the legal property of its developers, whose names are too numerous 7 | * to list here. Please refer to the COPYRIGHT file distributed with this 8 | * source distribution. 9 | * 10 | * This program is free software; you can redistribute it and/or modify 11 | * it under the terms of the GNU General Public License as published by 12 | * the Free Software Foundation; either version 2 of the License, or 13 | * (at your option) any later version. 14 | * 15 | * This program is distributed in the hope that it will be useful, 16 | * but WITHOUT ANY WARRANTY; without even the implied warranty of 17 | * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the 18 | * GNU General Public License for more details. 19 | * 20 | * You should have received a copy of the GNU General Public License 21 | * along with this program; if not, write to the Free Software 22 | * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02111-1301 USA 23 | */ 24 | #ifndef PURPLE_JABBER_AUTH_SCRAM_H_ 25 | #define PURPLE_JABBER_AUTH_SCRAM_H_ 26 | 27 | /* 28 | * Every function in this file is ONLY exposed for tests. 29 | * DO NOT USE ANYTHING HERE OR YOU WILL BE SENT TO THE PIT OF DESPAIR. 30 | */ 31 | 32 | /* Per-connection state stored between messages. 33 | * This is stored in js->auth_data_mech. 34 | */ 35 | typedef struct { 36 | const char *mech_substr; 37 | const char *name; 38 | guint size; 39 | } JabberScramHash; 40 | 41 | typedef struct { 42 | const JabberScramHash *hash; 43 | char *cnonce; 44 | GString *auth_message; 45 | 46 | GString *client_proof; 47 | GString *server_signature; 48 | 49 | gchar *password; 50 | gboolean channel_binding; 51 | int step; 52 | } JabberScramData; 53 | 54 | #include "auth.h" 55 | 56 | /** 57 | * Implements the Hi() function as described in the SASL-SCRAM I-D. 58 | * 59 | * @param hash The struct corresponding to the hash function to be used. 60 | * @param str The string to perform the PBKDF2 operation on. 61 | * @param salt The salt. 62 | * @param iterations The number of iterations to perform. 63 | * 64 | * @returns A newly allocated string containing the result. The string is 65 | * NOT null-terminated and its length is the length of the binary 66 | * output of the hash function in-use. 67 | */ 68 | guchar *jabber_scram_hi(const JabberScramHash *hash, const GString *str, 69 | GString *salt, guint iterations); 70 | 71 | /** 72 | * Calculates the proofs as described in Section 3 of the SASL-SCRAM I-D. 73 | * 74 | * @param data A JabberScramData structure. hash and auth_message must be 75 | * set. client_proof and server_signature will be set as a result 76 | * of this function. 77 | * @param salt The salt (as specified by the server) 78 | * @param iterations The number of iterations to perform. 79 | * 80 | * @returns TRUE if the proofs were successfully calculated. FALSE otherwise. 81 | */ 82 | gboolean jabber_scram_calc_proofs(JabberScramData *data, GString *salt, 83 | guint iterations); 84 | 85 | /** 86 | * Feed the algorithm with the data from the server. 87 | */ 88 | gboolean jabber_scram_feed_parser(JabberScramData *data, gchar *in, gchar **out); 89 | 90 | /** 91 | * Clean up and destroy the data struct 92 | */ 93 | void jabber_scram_data_destroy(JabberScramData *data); 94 | 95 | #endif /* PURPLE_JABBER_AUTH_SCRAM_H_ */ 96 | -------------------------------------------------------------------------------- /headers/jabber/bosh.h: -------------------------------------------------------------------------------- 1 | /** 2 | * @file bosh.h Bidirectional-streams over Synchronous HTTP (BOSH) (XEP-0124 and XEP-0206) 3 | * 4 | * purple 5 | * 6 | * Purple is the legal property of its developers, whose names are too numerous 7 | * to list here. Please refer to the COPYRIGHT file distributed with this 8 | * source distribution. 9 | * 10 | * This program is free software; you can redistribute it and/or modify 11 | * it under the terms of the GNU General Public License as published by 12 | * the Free Software Foundation; either version 2 of the License, or 13 | * (at your option) any later version. 14 | * 15 | * This program is distributed in the hope that it will be useful, 16 | * but WITHOUT ANY WARRANTY; without even the implied warranty of 17 | * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the 18 | * GNU General Public License for more details. 19 | * 20 | * You should have received a copy of the GNU General Public License 21 | * along with this program; if not, write to the Free Software 22 | * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02111-1301 USA 23 | */ 24 | #ifndef PURPLE_JABBER_BOSH_H_ 25 | #define PURPLE_JABBER_BOSH_H_ 26 | 27 | typedef struct _PurpleBOSHConnection PurpleBOSHConnection; 28 | 29 | #include "jabber.h" 30 | 31 | void jabber_bosh_init(void); 32 | void jabber_bosh_uninit(void); 33 | 34 | PurpleBOSHConnection* jabber_bosh_connection_init(JabberStream *js, const char *url); 35 | void jabber_bosh_connection_destroy(PurpleBOSHConnection *conn); 36 | 37 | gboolean jabber_bosh_connection_is_ssl(PurpleBOSHConnection *conn); 38 | void jabber_bosh_connection_send_keepalive(PurpleBOSHConnection *conn); 39 | 40 | void jabber_bosh_connection_connect(PurpleBOSHConnection *conn); 41 | void jabber_bosh_connection_close(PurpleBOSHConnection *conn); 42 | void jabber_bosh_connection_send_raw(PurpleBOSHConnection *conn, const char *data); 43 | #endif /* PURPLE_JABBER_BOSH_H_ */ 44 | -------------------------------------------------------------------------------- /headers/jabber/buddy.h: -------------------------------------------------------------------------------- 1 | /** 2 | * @file buddy.h Buddy handlers 3 | * 4 | * purple 5 | * 6 | * Purple is the legal property of its developers, whose names are too numerous 7 | * to list here. Please refer to the COPYRIGHT file distributed with this 8 | * source distribution. 9 | * 10 | * This program is free software; you can redistribute it and/or modify 11 | * it under the terms of the GNU General Public License as published by 12 | * the Free Software Foundation; either version 2 of the License, or 13 | * (at your option) any later version. 14 | * 15 | * This program is distributed in the hope that it will be useful, 16 | * but WITHOUT ANY WARRANTY; without even the implied warranty of 17 | * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the 18 | * GNU General Public License for more details. 19 | * 20 | * You should have received a copy of the GNU General Public License 21 | * along with this program; if not, write to the Free Software 22 | * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02111-1301 USA 23 | */ 24 | #ifndef PURPLE_JABBER_BUDDY_H_ 25 | #define PURPLE_JABBER_BUDDY_H_ 26 | 27 | typedef struct _JabberBuddy JabberBuddy; 28 | 29 | #include "jabber.h" 30 | #include "caps.h" 31 | #include "jutil.h" 32 | 33 | struct _JabberBuddy { 34 | /** 35 | * A sorted list of resources in priority descending order. 36 | * This means that the first resource in the list is the 37 | * "most available" (see resource_compare_cb in buddy.c for 38 | * details). Don't play with this yourself, let 39 | * jabber_buddy_track_resource and jabber_buddy_remove_resource do it. 40 | */ 41 | GList *resources; 42 | char *error_msg; 43 | enum { 44 | JABBER_INVISIBLE_NONE = 0, 45 | JABBER_INVISIBLE_SERVER = 1 << 1, 46 | JABBER_INVIS_BUDDY = 1 << 2 47 | } invisible; 48 | enum { 49 | JABBER_SUB_NONE = 0, 50 | JABBER_SUB_PENDING = 1 << 1, 51 | JABBER_SUB_TO = 1 << 2, 52 | JABBER_SUB_FROM = 1 << 3, 53 | JABBER_SUB_BOTH = (JABBER_SUB_TO | JABBER_SUB_FROM), 54 | JABBER_SUB_REMOVE = 1 << 4 55 | } subscription; 56 | }; 57 | 58 | typedef struct _JabberAdHocCommands { 59 | char *jid; 60 | char *node; 61 | char *name; 62 | } JabberAdHocCommands; 63 | 64 | typedef struct _JabberBuddyResource { 65 | JabberBuddy *jb; 66 | char *name; 67 | int priority; 68 | JabberBuddyState state; 69 | char *status; 70 | time_t idle; 71 | JabberCapabilities capabilities; 72 | char *thread_id; 73 | enum { 74 | JABBER_CHAT_STATES_UNKNOWN, 75 | JABBER_CHAT_STATES_UNSUPPORTED, 76 | JABBER_CHAT_STATES_SUPPORTED 77 | } chat_states; 78 | struct { 79 | char *version; 80 | char *name; 81 | char *os; 82 | } client; 83 | /* tz_off == PURPLE_NO_TZ_OFF when unset */ 84 | long tz_off; 85 | struct { 86 | JabberCapsClientInfo *info; 87 | GList *exts; 88 | } caps; 89 | GList *commands; 90 | gboolean commands_fetched; 91 | } JabberBuddyResource; 92 | 93 | void jabber_buddy_free(JabberBuddy *jb); 94 | JabberBuddy *jabber_buddy_find(JabberStream *js, const char *name, 95 | gboolean create); 96 | JabberBuddyResource *jabber_buddy_find_resource(JabberBuddy *jb, 97 | const char *resource); 98 | JabberBuddyResource *jabber_buddy_track_resource(JabberBuddy *jb, const char *resource, 99 | int priority, JabberBuddyState state, const char *status); 100 | void jabber_buddy_remove_resource(JabberBuddy *jb, const char *resource); 101 | void jabber_buddy_get_info(PurpleConnection *gc, const char *who); 102 | 103 | GList *jabber_blist_node_menu(PurpleBlistNode *node); 104 | 105 | void jabber_set_info(PurpleConnection *gc, const char *info); 106 | void jabber_setup_set_info(PurplePluginAction *action); 107 | void jabber_set_buddy_icon(PurpleConnection *gc, PurpleStoredImage *img); 108 | 109 | void jabber_user_search(JabberStream *js, const char *directory); 110 | void jabber_user_search_begin(PurplePluginAction *); 111 | 112 | void jabber_buddy_remove_all_pending_buddy_info_requests(JabberStream *js); 113 | 114 | void jabber_vcard_fetch_mine(JabberStream *js); 115 | 116 | gboolean jabber_resource_know_capabilities(const JabberBuddyResource *jbr); 117 | gboolean jabber_resource_has_capability(const JabberBuddyResource *jbr, 118 | const gchar *cap); 119 | gboolean jabber_buddy_has_capability(const JabberBuddy *jb, const gchar *cap); 120 | 121 | const gchar * 122 | jabber_resource_get_identity_category_type(const JabberBuddyResource *jbr, 123 | const gchar *category); 124 | 125 | #endif /* PURPLE_JABBER_BUDDY_H_ */ 126 | -------------------------------------------------------------------------------- /headers/jabber/caps.h: -------------------------------------------------------------------------------- 1 | /* 2 | * purple - Jabber Protocol Plugin 3 | * 4 | * Purple is the legal property of its developers, whose names are too numerous 5 | * to list here. Please refer to the COPYRIGHT file distributed with this 6 | * source distribution. 7 | * 8 | * This program is free software; you can redistribute it and/or modify 9 | * it under the terms of the GNU General Public License as published by 10 | * the Free Software Foundation; either version 2 of the License, or 11 | * (at your option) any later version. 12 | * 13 | * This program is distributed in the hope that it will be useful, 14 | * but WITHOUT ANY WARRANTY; without even the implied warranty of 15 | * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the 16 | * GNU General Public License for more details. 17 | * 18 | * You should have received a copy of the GNU General Public License 19 | * along with this program; if not, write to the Free Software 20 | * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02111-1301 USA 21 | * 22 | */ 23 | 24 | #ifndef PURPLE_JABBER_CAPS_H_ 25 | #define PURPLE_JABBER_CAPS_H_ 26 | 27 | typedef struct _JabberCapsClientInfo JabberCapsClientInfo; 28 | 29 | #include "jabber.h" 30 | 31 | /* Implementation of XEP-0115 - Entity Capabilities */ 32 | 33 | typedef struct _JabberCapsNodeExts JabberCapsNodeExts; 34 | 35 | typedef struct _JabberCapsTuple { 36 | const char *node; 37 | const char *ver; 38 | const char *hash; 39 | } JabberCapsTuple; 40 | 41 | struct _JabberCapsClientInfo { 42 | GList *identities; /* JabberIdentity */ 43 | GList *features; /* char * */ 44 | GList *forms; /* xmlnode * */ 45 | JabberCapsNodeExts *exts; 46 | 47 | const JabberCapsTuple tuple; 48 | }; 49 | 50 | /* 51 | * This stores a set of exts "known" for a specific node (which indicates 52 | * a specific client -- for reference, Pidgin, Finch, Meebo, et al share one 53 | * node.) In XEP-0115 v1.3, exts are used for features that may or may not be 54 | * present at a given time (PEP things, buzz might be disabled, etc). 55 | * 56 | * This structure is shared among all JabberCapsClientInfo instances matching 57 | * a specific node (if the capstable key->hash == NULL, which indicates that 58 | * the ClientInfo is using v1.3 caps as opposed to v1.5 caps). 59 | * 60 | * It's only exposed so that jabber_resource_has_capability can use it. 61 | * Everyone else, STAY AWAY! 62 | */ 63 | struct _JabberCapsNodeExts { 64 | guint ref; 65 | GHashTable *exts; /* char *ext_name -> GList *features */ 66 | }; 67 | 68 | typedef void (*jabber_caps_get_info_cb)(JabberCapsClientInfo *info, GList *exts, gpointer user_data); 69 | 70 | void jabber_caps_init(void); 71 | void jabber_caps_uninit(void); 72 | 73 | /** 74 | * Check whether all of the exts in a char* array are known to the given info. 75 | */ 76 | gboolean jabber_caps_exts_known(const JabberCapsClientInfo *info, char **exts); 77 | 78 | /** 79 | * Main entity capabilites function to get the capabilities of a contact. 80 | * 81 | * The callback will be called synchronously if we already have the 82 | * capabilities for the specified (node,ver,hash) (and, if exts are specified, 83 | * if we know what each means) 84 | * 85 | * @param exts A g_strsplit'd (NULL-terminated) array of strings. This 86 | * function is responsible for freeing it. 87 | */ 88 | void jabber_caps_get_info(JabberStream *js, const char *who, const char *node, 89 | const char *ver, const char *hash, 90 | char **exts, jabber_caps_get_info_cb cb, 91 | gpointer user_data); 92 | 93 | /** 94 | * Takes a JabberCapsClientInfo pointer and returns the caps hash according to 95 | * XEP-0115 Version 1.5. 96 | * 97 | * @param info A JabberCapsClientInfo pointer. 98 | * @param hash Hash cipher to be used. Either sha-1 or md5. 99 | * @return The base64 encoded SHA-1 hash; must be freed by caller 100 | */ 101 | gchar *jabber_caps_calculate_hash(JabberCapsClientInfo *info, const char *hash); 102 | 103 | /** 104 | * Calculate SHA1 hash for own featureset. 105 | */ 106 | void jabber_caps_calculate_own_hash(JabberStream *js); 107 | 108 | /** Get the current caps hash. 109 | * @ret hash 110 | **/ 111 | const gchar* jabber_caps_get_own_hash(JabberStream *js); 112 | 113 | /** 114 | * Broadcast a new calculated hash using a stanza. 115 | */ 116 | void jabber_caps_broadcast_change(void); 117 | 118 | /** 119 | * Parse the element from an IQ stanza into a JabberCapsClientInfo 120 | * struct. 121 | * 122 | * Exposed for tests 123 | * 124 | * @param query The 'query' element from an IQ reply stanza. 125 | * @returns A JabberCapsClientInfo struct, or NULL on error 126 | */ 127 | JabberCapsClientInfo *jabber_caps_parse_client_info(xmlnode *query); 128 | 129 | #endif /* PURPLE_JABBER_CAPS_H_ */ 130 | -------------------------------------------------------------------------------- /headers/jabber/chat.h: -------------------------------------------------------------------------------- 1 | /** 2 | * @file chat.h Chat stuff 3 | * 4 | * purple 5 | * 6 | * Purple is the legal property of its developers, whose names are too numerous 7 | * to list here. Please refer to the COPYRIGHT file distributed with this 8 | * source distribution. 9 | * 10 | * This program is free software; you can redistribute it and/or modify 11 | * it under the terms of the GNU General Public License as published by 12 | * the Free Software Foundation; either version 2 of the License, or 13 | * (at your option) any later version. 14 | * 15 | * This program is distributed in the hope that it will be useful, 16 | * but WITHOUT ANY WARRANTY; without even the implied warranty of 17 | * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the 18 | * GNU General Public License for more details. 19 | * 20 | * You should have received a copy of the GNU General Public License 21 | * along with this program; if not, write to the Free Software 22 | * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02111-1301 USA 23 | */ 24 | #ifndef PURPLE_JABBER_CHAT_H_ 25 | #define PURPLE_JABBER_CHAT_H_ 26 | 27 | //#include "internal.h" 28 | #include "connection.h" 29 | #include "conversation.h" 30 | #include "request.h" 31 | #include "roomlist.h" 32 | 33 | #include "jabber.h" 34 | 35 | typedef struct _JabberChatMember { 36 | char *handle; 37 | char *jid; 38 | } JabberChatMember; 39 | 40 | 41 | typedef struct _JabberChat { 42 | JabberStream *js; 43 | char *room; 44 | char *server; 45 | char *handle; 46 | GHashTable *components; 47 | int id; 48 | PurpleConversation *conv; 49 | gboolean muc; 50 | gboolean xhtml; 51 | PurpleRequestType config_dialog_type; 52 | void *config_dialog_handle; 53 | GHashTable *members; 54 | gboolean left; 55 | time_t joined; 56 | } JabberChat; 57 | 58 | GList *jabber_chat_info(PurpleConnection *gc); 59 | GHashTable *jabber_chat_info_defaults(PurpleConnection *gc, const char *chat_name); 60 | char *jabber_get_chat_name(GHashTable *data); 61 | 62 | /** 63 | * in-prpl function for joining a chat room. Doesn't require sticking goop 64 | * into a hash table. 65 | * 66 | * @param room The room to join. This MUST be normalized already. 67 | * @param server The server the room is on. This MUST be normalized already. 68 | * @param password The password (if required) to join the room. May be NULL. 69 | * @param data The chat hash table. May be NULL (it will be generated 70 | * for current core<>prpl API interface.) 71 | */ 72 | JabberChat *jabber_join_chat(JabberStream *js, const char *room, 73 | const char *server, const char *handle, 74 | const char *password, GHashTable *data); 75 | 76 | void jabber_chat_join(PurpleConnection *gc, GHashTable *data); 77 | JabberChat *jabber_chat_find(JabberStream *js, const char *room, 78 | const char *server); 79 | JabberChat *jabber_chat_find_by_id(JabberStream *js, int id); 80 | JabberChat *jabber_chat_find_by_conv(PurpleConversation *conv); 81 | void jabber_chat_destroy(JabberChat *chat); 82 | void jabber_chat_free(JabberChat *chat); 83 | gboolean jabber_chat_find_buddy(PurpleConversation *conv, const char *name); 84 | void jabber_chat_invite(PurpleConnection *gc, int id, const char *message, 85 | const char *name); 86 | void jabber_chat_leave(PurpleConnection *gc, int id); 87 | char *jabber_chat_buddy_real_name(PurpleConnection *gc, int id, const char *who); 88 | void jabber_chat_request_room_configure(JabberChat *chat); 89 | void jabber_chat_create_instant_room(JabberChat *chat); 90 | void jabber_chat_register(JabberChat *chat); 91 | void jabber_chat_change_topic(JabberChat *chat, const char *topic); 92 | void jabber_chat_set_topic(PurpleConnection *gc, int id, const char *topic); 93 | gboolean jabber_chat_change_nick(JabberChat *chat, const char *nick); 94 | void jabber_chat_part(JabberChat *chat, const char *msg); 95 | void jabber_chat_track_handle(JabberChat *chat, const char *handle, 96 | const char *jid, const char *affiliation, const char *role); 97 | void jabber_chat_remove_handle(JabberChat *chat, const char *handle); 98 | gboolean jabber_chat_ban_user(JabberChat *chat, const char *who, 99 | const char *why); 100 | gboolean jabber_chat_affiliate_user(JabberChat *chat, const char *who, 101 | const char *affiliation); 102 | gboolean jabber_chat_affiliation_list(JabberChat *chat, const char *affiliation); 103 | gboolean jabber_chat_role_user(JabberChat *chat, const char *who, 104 | const char *role, const char *why); 105 | gboolean jabber_chat_role_list(JabberChat *chat, const char *role); 106 | 107 | PurpleRoomlist *jabber_roomlist_get_list(PurpleConnection *gc); 108 | void jabber_roomlist_cancel(PurpleRoomlist *list); 109 | 110 | void jabber_chat_disco_traffic(JabberChat *chat); 111 | 112 | char *jabber_roomlist_room_serialize(PurpleRoomlistRoom *room); 113 | 114 | gboolean jabber_chat_all_participants_have_capability(const JabberChat *chat, 115 | const gchar *cap); 116 | guint jabber_chat_get_num_participants(const JabberChat *chat); 117 | 118 | #endif /* PURPLE_JABBER_CHAT_H_ */ 119 | -------------------------------------------------------------------------------- /headers/jabber/data.h: -------------------------------------------------------------------------------- 1 | /* 2 | * purple - Jabber Service Discovery 3 | * 4 | * Purple is the legal property of its developers, whose names are too numerous 5 | * to list here. Please refer to the COPYRIGHT file distributed with this 6 | * source distribution. 7 | * 8 | * This program is free software; you can redistribute it and/or modify 9 | * it under the terms of the GNU General Public License as published by 10 | * the Free Software Foundation; either version 2 of the License, or 11 | * (at your option) any later version. 12 | * 13 | * This program is distributed in the hope that it will be useful, 14 | * but WITHOUT ANY WARRANTY; without even the implied warranty of 15 | * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the 16 | * GNU General Public License for more details. 17 | * 18 | * You should have received a copy of the GNU General Public License 19 | * along with this program; if not, write to the Free Software 20 | * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02111-1301 USA 21 | * 22 | */ 23 | 24 | #ifndef PURPLE_JABBER_DATA_H 25 | #define PURPLE_JABBER_DATA_H 26 | 27 | #include "xmlnode.h" 28 | #include "jabber.h" 29 | 30 | #include 31 | 32 | #define JABBER_DATA_MAX_SIZE 8192 33 | 34 | 35 | typedef struct { 36 | char *cid; 37 | char *type; 38 | gsize size; 39 | gpointer data; 40 | gboolean ephemeral; 41 | } JabberData; 42 | 43 | typedef void (JabberDataRequestCallback)(JabberData *data, gchar *alt, 44 | gpointer userdata); 45 | 46 | 47 | /* creates a JabberData instance from raw data */ 48 | JabberData *jabber_data_create_from_data(gconstpointer data, gsize size, 49 | const char *type, gboolean ephemeral, JabberStream *js); 50 | 51 | /* create a JabberData instance from an XML "data" element (as defined by 52 | XEP 0231 */ 53 | JabberData *jabber_data_create_from_xml(xmlnode *tag); 54 | 55 | /* destroy a JabberData instance, NOT to be used on data that has been 56 | associated, since they get "owned" */ 57 | void jabber_data_destroy(JabberData *data); 58 | 59 | const char *jabber_data_get_cid(const JabberData *data); 60 | const char *jabber_data_get_type(const JabberData *data); 61 | 62 | gsize jabber_data_get_size(const JabberData *data); 63 | gpointer jabber_data_get_data(const JabberData *data); 64 | 65 | /* returns the XML definition for the data element */ 66 | xmlnode *jabber_data_get_xml_definition(const JabberData *data); 67 | 68 | /* returns an XHTML-IM "img" tag given a data instance */ 69 | xmlnode *jabber_data_get_xhtml_im(const JabberData *data, const gchar *alt); 70 | 71 | void jabber_data_request(JabberStream *js, const gchar *cid, const gchar *who, 72 | gchar *alt, gboolean ephemeral, JabberDataRequestCallback cb, 73 | gpointer userdata); 74 | 75 | /* lookup functions */ 76 | const JabberData *jabber_data_find_local_by_alt(const gchar *alt); 77 | const JabberData *jabber_data_find_local_by_cid(const gchar *cid); 78 | const JabberData *jabber_data_find_remote_by_cid(JabberStream *js, 79 | const gchar *who, const gchar *cid); 80 | 81 | /* store data objects */ 82 | void jabber_data_associate_local(JabberData *data, const gchar *alt); 83 | void jabber_data_associate_remote(JabberStream *js, const gchar *who, 84 | JabberData *data); 85 | 86 | /* handles iq requests */ 87 | void jabber_data_parse(JabberStream *js, const char *who, JabberIqType type, 88 | const char *id, xmlnode *data_node); 89 | 90 | void jabber_data_init(void); 91 | void jabber_data_uninit(void); 92 | 93 | #endif /* PURPLE_JABBER_DATA_H */ 94 | -------------------------------------------------------------------------------- /headers/jabber/disco.h: -------------------------------------------------------------------------------- 1 | /** 2 | * @file disco.h Jabber Service Discovery 3 | * 4 | * purple 5 | * 6 | * Purple is the legal property of its developers, whose names are too numerous 7 | * to list here. Please refer to the COPYRIGHT file distributed with this 8 | * source distribution. 9 | * 10 | * This program is free software; you can redistribute it and/or modify 11 | * it under the terms of the GNU General Public License as published by 12 | * the Free Software Foundation; either version 2 of the License, or 13 | * (at your option) any later version. 14 | * 15 | * This program is distributed in the hope that it will be useful, 16 | * but WITHOUT ANY WARRANTY; without even the implied warranty of 17 | * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the 18 | * GNU General Public License for more details. 19 | * 20 | * You should have received a copy of the GNU General Public License 21 | * along with this program; if not, write to the Free Software 22 | * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02111-1301 USA 23 | */ 24 | #ifndef PURPLE_JABBER_DISCO_H_ 25 | #define PURPLE_JABBER_DISCO_H_ 26 | 27 | #include "jabber.h" 28 | 29 | typedef struct _JabberDiscoItem { 30 | const char *jid; /* MUST */ 31 | const char *node; /* SHOULD */ 32 | const char *name; /* MAY */ 33 | } JabberDiscoItem; 34 | 35 | typedef void (JabberDiscoInfoCallback)(JabberStream *js, const char *who, 36 | JabberCapabilities capabilities, gpointer data); 37 | 38 | typedef void (JabberDiscoItemsCallback)(JabberStream *js, const char *jid, 39 | const char *node, GSList *items, gpointer data); 40 | 41 | void jabber_disco_info_parse(JabberStream *js, const char *from, 42 | JabberIqType type, const char *id, xmlnode *in_query); 43 | void jabber_disco_items_parse(JabberStream *js, const char *from, 44 | JabberIqType type, const char *id, xmlnode *query); 45 | 46 | void jabber_disco_items_server(JabberStream *js); 47 | 48 | void jabber_disco_info_do(JabberStream *js, const char *who, 49 | JabberDiscoInfoCallback *callback, gpointer data); 50 | 51 | #endif /* PURPLE_JABBER_DISCO_H_ */ 52 | -------------------------------------------------------------------------------- /headers/jabber/facebook_roster.h: -------------------------------------------------------------------------------- 1 | /** 2 | * purple 3 | * 4 | * Purple is the legal property of its developers, whose names are too numerous 5 | * to list here. Please refer to the COPYRIGHT file distributed with this 6 | * source distribution. 7 | * 8 | * This program is free software; you can redistribute it and/or modify 9 | * it under the terms of the GNU General Public License as published by 10 | * the Free Software Foundation; either version 2 of the License, or 11 | * (at your option) any later version. 12 | * 13 | * This program is distributed in the hope that it will be useful, 14 | * but WITHOUT ANY WARRANTY; without even the implied warranty of 15 | * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the 16 | * GNU General Public License for more details. 17 | * 18 | * You should have received a copy of the GNU General Public License 19 | * along with this program; if not, write to the Free Software 20 | * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02111-1301 USA 21 | */ 22 | #ifndef PURPLE_XMPP_FACEBOOK_ROSTER_H_ 23 | #define PURPLE_XMPP_FACEBOOK_ROSTER_H_ 24 | 25 | #include "jabber.h" 26 | 27 | /* removes deleted buddies from the list */ 28 | void 29 | jabber_facebook_roster_cleanup(JabberStream *js, xmlnode *query); 30 | 31 | /* ignores facebook roster quirks */ 32 | gboolean 33 | jabber_facebook_roster_incoming(JabberStream *js, xmlnode *item); 34 | 35 | #endif /* PURPLE_XMPP_FACEBOOK_ROSTER_H_ */ 36 | -------------------------------------------------------------------------------- /headers/jabber/ibb.h: -------------------------------------------------------------------------------- 1 | /* 2 | * purple - Jabber Service Discovery 3 | * 4 | * Purple is the legal property of its developers, whose names are too numerous 5 | * to list here. Please refer to the COPYRIGHT file distributed with this 6 | * source distribution. 7 | * 8 | * This program is free software; you can redistribute it and/or modify 9 | * it under the terms of the GNU General Public License as published by 10 | * the Free Software Foundation; either version 2 of the License, or 11 | * (at your option) any later version. 12 | * 13 | * This program is distributed in the hope that it will be useful, 14 | * but WITHOUT ANY WARRANTY; without even the implied warranty of 15 | * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the 16 | * GNU General Public License for more details. 17 | * 18 | * You should have received a copy of the GNU General Public License 19 | * along with this program; if not, write to the Free Software 20 | * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02111-1301 USA 21 | * 22 | */ 23 | 24 | #ifndef PURPLE_JABBER_IBB_H_ 25 | #define PURPLE_JABBER_IBB_H_ 26 | 27 | #include "jabber.h" 28 | #include "iq.h" 29 | 30 | typedef struct _JabberIBBSession JabberIBBSession; 31 | 32 | typedef void 33 | (JabberIBBDataCallback)(JabberIBBSession *, const gpointer data, gsize size); 34 | 35 | typedef void (JabberIBBOpenedCallback)(JabberIBBSession *); 36 | typedef void (JabberIBBClosedCallback)(JabberIBBSession *); 37 | typedef void (JabberIBBErrorCallback)(JabberIBBSession *); 38 | typedef void (JabberIBBSentCallback)(JabberIBBSession *); 39 | 40 | typedef gboolean (JabberIBBOpenHandler)(JabberStream *js, const char *from, 41 | const char *id, xmlnode *open); 42 | 43 | typedef enum { 44 | JABBER_IBB_SESSION_NOT_OPENED, 45 | JABBER_IBB_SESSION_OPENED, 46 | JABBER_IBB_SESSION_CLOSED, 47 | JABBER_IBB_SESSION_ERROR 48 | } JabberIBBSessionState; 49 | 50 | struct _JabberIBBSession { 51 | JabberStream *js; 52 | gchar *who; 53 | gchar *sid; 54 | gchar *id; 55 | guint16 send_seq; 56 | guint16 recv_seq; 57 | gsize block_size; 58 | 59 | /* session state */ 60 | JabberIBBSessionState state; 61 | 62 | /* user data (f.ex. a handle to a PurpleXfer) */ 63 | gpointer user_data; 64 | 65 | /* callbacks */ 66 | JabberIBBOpenedCallback *opened_cb; 67 | JabberIBBSentCallback *data_sent_cb; 68 | JabberIBBClosedCallback *closed_cb; 69 | /* callback for receiving data */ 70 | JabberIBBDataCallback *data_received_cb; 71 | JabberIBBErrorCallback *error_cb; 72 | 73 | /* store the last sent IQ (to permit cancel of callback) */ 74 | gchar *last_iq_id; 75 | }; 76 | 77 | JabberIBBSession *jabber_ibb_session_create(JabberStream *js, const gchar *sid, 78 | const gchar *who, gpointer user_data); 79 | JabberIBBSession *jabber_ibb_session_create_from_xmlnode(JabberStream *js, 80 | const gchar *from, const gchar *id, xmlnode *open, gpointer user_data); 81 | 82 | void jabber_ibb_session_destroy(JabberIBBSession *sess); 83 | 84 | void jabber_ibb_session_set_opened_callback(JabberIBBSession *sess, 85 | JabberIBBOpenedCallback *cb); 86 | void jabber_ibb_session_set_data_sent_callback(JabberIBBSession *sess, 87 | JabberIBBSentCallback *cb); 88 | void jabber_ibb_session_set_closed_callback(JabberIBBSession *sess, 89 | JabberIBBClosedCallback *cb); 90 | void jabber_ibb_session_set_data_received_callback(JabberIBBSession *sess, 91 | JabberIBBDataCallback *cb); 92 | void jabber_ibb_session_set_error_callback(JabberIBBSession *sess, 93 | JabberIBBErrorCallback *cb); 94 | 95 | void jabber_ibb_session_open(JabberIBBSession *sess); 96 | void jabber_ibb_session_close(JabberIBBSession *sess); 97 | void jabber_ibb_session_accept(JabberIBBSession *sess); 98 | void jabber_ibb_session_send_data(JabberIBBSession *sess, gconstpointer data, 99 | gsize size); 100 | 101 | const gchar *jabber_ibb_session_get_sid(const JabberIBBSession *sess); 102 | JabberStream *jabber_ibb_session_get_js(JabberIBBSession *sess); 103 | const gchar *jabber_ibb_session_get_who(const JabberIBBSession *sess); 104 | 105 | guint16 jabber_ibb_session_get_send_seq(const JabberIBBSession *sess); 106 | guint16 jabber_ibb_session_get_recv_seq(const JabberIBBSession *sess); 107 | 108 | JabberIBBSessionState jabber_ibb_session_get_state(const JabberIBBSession *sess); 109 | 110 | gsize jabber_ibb_session_get_block_size(const JabberIBBSession *sess); 111 | void jabber_ibb_session_set_block_size(JabberIBBSession *sess, gsize size); 112 | 113 | /* get maximum size data block to send (in bytes) 114 | (before encoded to BASE64) */ 115 | gsize jabber_ibb_session_get_max_data_size(const JabberIBBSession *sess); 116 | 117 | gpointer jabber_ibb_session_get_user_data(JabberIBBSession *sess); 118 | 119 | /* handle incoming packet */ 120 | void jabber_ibb_parse(JabberStream *js, const char *who, JabberIqType type, 121 | const char *id, xmlnode *child); 122 | 123 | /* add a handler for open session */ 124 | void jabber_ibb_register_open_handler(JabberIBBOpenHandler *cb); 125 | void jabber_ibb_unregister_open_handler(JabberIBBOpenHandler *cb); 126 | 127 | void jabber_ibb_init(void); 128 | void jabber_ibb_uninit(void); 129 | 130 | #endif /* PURPLE_JABBER_IBB_H_ */ 131 | -------------------------------------------------------------------------------- /headers/jabber/iq.h: -------------------------------------------------------------------------------- 1 | /** 2 | * @file iq.h JabberID handlers 3 | * 4 | * purple 5 | * 6 | * Purple is the legal property of its developers, whose names are too numerous 7 | * to list here. Please refer to the COPYRIGHT file distributed with this 8 | * source distribution. 9 | * 10 | * This program is free software; you can redistribute it and/or modify 11 | * it under the terms of the GNU General Public License as published by 12 | * the Free Software Foundation; either version 2 of the License, or 13 | * (at your option) any later version. 14 | * 15 | * This program is distributed in the hope that it will be useful, 16 | * but WITHOUT ANY WARRANTY; without even the implied warranty of 17 | * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the 18 | * GNU General Public License for more details. 19 | * 20 | * You should have received a copy of the GNU General Public License 21 | * along with this program; if not, write to the Free Software 22 | * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02111-1301 USA 23 | */ 24 | #ifndef PURPLE_JABBER_IQ_H_ 25 | #define PURPLE_JABBER_IQ_H_ 26 | 27 | typedef enum { 28 | JABBER_IQ_SET, 29 | JABBER_IQ_GET, 30 | JABBER_IQ_RESULT, 31 | JABBER_IQ_ERROR, 32 | JABBER_IQ_NONE 33 | } JabberIqType; 34 | 35 | #include "jabber.h" 36 | #include "connection.h" 37 | 38 | typedef struct _JabberIq JabberIq; 39 | typedef struct _JabberIqCallbackData JabberIqCallbackData; 40 | 41 | /** 42 | * A JabberIqHandler is called to process an incoming IQ stanza. 43 | * Handlers typically process unsolicited incoming GETs or SETs for their 44 | * registered namespace, but may be called to handle the results of a 45 | * GET or SET that we generated if no JabberIqCallback was generated 46 | * The handler may be called for the results of a GET or SET (RESULT or ERROR) 47 | * that we generated 48 | * if the generating function did not register a JabberIqCallback. 49 | * 50 | * @param js The JabberStream object. 51 | * @param from The remote entity (the from attribute on the stanza) 52 | * @param type The IQ type. 53 | * @param id The IQ id (the id attribute on the stanza) 54 | * @param child The child element of the stanza that matches the name 55 | * and namespace registered with jabber_iq_register_handler. 56 | * 57 | * @see jabber_iq_register_handler() 58 | * @see JabberIqCallback 59 | */ 60 | typedef void (JabberIqHandler)(JabberStream *js, const char *from, 61 | JabberIqType type, const char *id, 62 | xmlnode *child); 63 | 64 | /** 65 | * A JabberIqCallback is called to process the results of a GET or SET that 66 | * we send to a remote entity. The callback is matched based on the id 67 | * of the incoming stanza (which matches the one on the initial stanza). 68 | * 69 | * @param js The JabberStream object. 70 | * @param from The remote entity (the from attribute on the stanza) 71 | * @param type The IQ type. The only possible values are JABBER_IQ_RESULT 72 | * and JABBER_IQ_ERROR. 73 | * @param id The IQ id (the id attribute on the stanza) 74 | * @param packet The stanza 75 | * @param data The callback data passed to jabber_iq_set_callback() 76 | * 77 | * @see jabber_iq_set_callback() 78 | */ 79 | typedef void (JabberIqCallback)(JabberStream *js, const char *from, 80 | JabberIqType type, const char *id, 81 | xmlnode *packet, gpointer data); 82 | 83 | struct _JabberIq { 84 | JabberIqType type; 85 | char *id; 86 | xmlnode *node; 87 | 88 | JabberIqCallback *callback; 89 | gpointer callback_data; 90 | 91 | JabberStream *js; 92 | }; 93 | 94 | JabberIq *jabber_iq_new(JabberStream *js, JabberIqType type); 95 | JabberIq *jabber_iq_new_query(JabberStream *js, JabberIqType type, 96 | const char *xmlns); 97 | 98 | void jabber_iq_parse(JabberStream *js, xmlnode *packet); 99 | 100 | void jabber_iq_callbackdata_free(JabberIqCallbackData *jcd); 101 | void jabber_iq_remove_callback_by_id(JabberStream *js, const char *id); 102 | void jabber_iq_set_callback(JabberIq *iq, JabberIqCallback *cb, gpointer data); 103 | void jabber_iq_set_id(JabberIq *iq, const char *id); 104 | 105 | void jabber_iq_send(JabberIq *iq); 106 | void jabber_iq_free(JabberIq *iq); 107 | 108 | void jabber_iq_init(void); 109 | void jabber_iq_uninit(void); 110 | 111 | void jabber_iq_register_handler(const char *node, const char *xmlns, 112 | JabberIqHandler *func); 113 | 114 | /* Connected to namespace-handler registration signals */ 115 | void jabber_iq_signal_register(const gchar *node, const gchar *xmlns); 116 | void jabber_iq_signal_unregister(const gchar *node, const gchar *xmlns); 117 | 118 | #endif /* PURPLE_JABBER_IQ_H_ */ 119 | -------------------------------------------------------------------------------- /headers/jabber/jabber.h: -------------------------------------------------------------------------------- 1 | /** 2 | * @file jabber.h 3 | * 4 | * purple 5 | * 6 | * Purple is the legal property of its developers, whose names are too numerous 7 | * to list here. Please refer to the COPYRIGHT file distributed with this 8 | * source distribution. 9 | * 10 | * This program is free software; you can redistribute it and/or modify 11 | * it under the terms of the GNU General Public License as published by 12 | * the Free Software Foundation; either version 2 of the License, or 13 | * (at your option) any later version. 14 | * 15 | * This program is distributed in the hope that it will be useful, 16 | * but WITHOUT ANY WARRANTY; without even the implied warranty of 17 | * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the 18 | * GNU General Public License for more details. 19 | * 20 | * You should have received a copy of the GNU General Public License 21 | * along with this program; if not, write to the Free Software 22 | * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02111-1301 USA 23 | */ 24 | #ifndef PURPLE_JABBER_H_ 25 | #define PURPLE_JABBER_H_ 26 | 27 | typedef enum { 28 | JABBER_CAP_NONE = 0, 29 | /* JABBER_CAP_XHTML = 1 << 0, */ 30 | /* JABBER_CAP_COMPOSING = 1 << 1, */ 31 | JABBER_CAP_SI = 1 << 2, 32 | JABBER_CAP_SI_FILE_XFER = 1 << 3, 33 | JABBER_CAP_BYTESTREAMS = 1 << 4, 34 | JABBER_CAP_IBB = 1 << 5, 35 | JABBER_CAP_CHAT_STATES = 1 << 6, 36 | JABBER_CAP_IQ_SEARCH = 1 << 7, 37 | JABBER_CAP_IQ_REGISTER = 1 << 8, 38 | 39 | /* Google Talk extensions: 40 | * http://code.google.com/apis/talk/jep_extensions/extensions.html 41 | */ 42 | JABBER_CAP_GMAIL_NOTIFY = 1 << 9, 43 | JABBER_CAP_GOOGLE_ROSTER = 1 << 10, 44 | 45 | JABBER_CAP_PING = 1 << 11, 46 | JABBER_CAP_ADHOC = 1 << 12, 47 | JABBER_CAP_BLOCKING = 1 << 13, 48 | 49 | JABBER_CAP_ITEMS = 1 << 14, 50 | JABBER_CAP_ROSTER_VERSIONING = 1 << 15, 51 | 52 | JABBER_CAP_FACEBOOK = 1 << 16, 53 | 54 | JABBER_CAP_RETRIEVED = 1 << 31 55 | } JabberCapabilities; 56 | 57 | typedef struct _JabberStream JabberStream; 58 | 59 | #include 60 | #include 61 | #include "circbuffer.h" 62 | #include "connection.h" 63 | #include "dnsquery.h" 64 | #include "dnssrv.h" 65 | #include "media.h" 66 | #include "mediamanager.h" 67 | #include "roomlist.h" 68 | #include "sslconn.h" 69 | 70 | #include "namespaces.h" 71 | 72 | #include "auth.h" 73 | #include "iq.h" 74 | #include "jutil.h" 75 | #include "xmlnode.h" 76 | #include "buddy.h" 77 | #include "bosh.h" 78 | 79 | #ifdef HAVE_CYRUS_SASL 80 | #include 81 | #endif 82 | 83 | #define CAPS0115_NODE "http://pidgin.im/" 84 | 85 | #define JABBER_DEFAULT_REQUIRE_TLS "require_starttls" 86 | #define JABBER_DEFAULT_FT_PROXIES "proxy.eu.jabber.org" 87 | 88 | /* Index into attention_types list */ 89 | #define JABBER_BUZZ 0 90 | 91 | typedef enum { 92 | JABBER_STREAM_OFFLINE, 93 | JABBER_STREAM_CONNECTING, 94 | JABBER_STREAM_INITIALIZING, 95 | JABBER_STREAM_INITIALIZING_ENCRYPTION, 96 | JABBER_STREAM_AUTHENTICATING, 97 | JABBER_STREAM_POST_AUTH, 98 | JABBER_STREAM_CONNECTED 99 | } JabberStreamState; 100 | 101 | struct _JabberStream 102 | { 103 | int fd; 104 | 105 | PurpleSrvTxtQueryData *srv_query_data; 106 | 107 | xmlParserCtxt *context; 108 | xmlnode *current; 109 | 110 | struct { 111 | guint8 major; 112 | guint8 minor; 113 | } protocol_version; 114 | 115 | JabberSaslMech *auth_mech; 116 | gpointer auth_mech_data; 117 | 118 | /** 119 | * The header from the opening tag. This being NULL is treated 120 | * as a special condition in the parsing code (signifying the next 121 | * stanza started is an opening stream tag), and its being missing on 122 | * the stream header is treated as a fatal error. 123 | */ 124 | char *stream_id; 125 | JabberStreamState state; 126 | 127 | GHashTable *buddies; 128 | 129 | /* 130 | * This boolean was added to eliminate a heinous bug where we would 131 | * get into a loop with the server and move a buddy back and forth 132 | * from one group to another. 133 | * 134 | * The sequence goes something like this: 135 | * 1. Our resource and another resource both approve an authorization 136 | * request at the exact same time. We put the buddy in group A and 137 | * the other resource put the buddy in group B. 138 | * 2. The server receives the roster add for group B and sends us a 139 | * roster push. 140 | * 3. We receive this roster push and modify our local blist. This 141 | * triggers us to send a roster add for group B. 142 | * 4. The server recieves our earlier roster add for group A and sends 143 | * us a roster push. 144 | * 5. We receive this roster push and modify our local blist. This 145 | * triggers us to send a roster add for group A. 146 | * 6. The server receives our earlier roster add for group B and sends 147 | * us a roster push. 148 | * (repeat steps 3 through 6 ad infinitum) 149 | * 150 | * This boolean is used to short-circuit the sending of a roster add 151 | * when we receive a roster push. 152 | * 153 | * See these bug reports: 154 | * http://trac.adiumx.com/ticket/8834 155 | * http://developer.pidgin.im/ticket/5484 156 | * http://developer.pidgin.im/ticket/6188 157 | */ 158 | gboolean currently_parsing_roster_push; 159 | 160 | GHashTable *chats; 161 | GList *chat_servers; 162 | PurpleRoomlist *roomlist; 163 | GList *user_directories; 164 | 165 | GHashTable *iq_callbacks; 166 | int next_id; 167 | 168 | GList *bs_proxies; 169 | GList *oob_file_transfers; 170 | GList *file_transfers; 171 | 172 | time_t idle; 173 | time_t old_idle; 174 | 175 | /** When we last pinged the server, so we don't ping more 176 | * often than once every minute. 177 | */ 178 | time_t last_ping; 179 | 180 | JabberID *user; 181 | JabberBuddy *user_jb; 182 | 183 | PurpleConnection *gc; 184 | PurpleSslConnection *gsc; 185 | 186 | gboolean registration; 187 | 188 | char *initial_avatar_hash; 189 | char *avatar_hash; 190 | GSList *pending_avatar_requests; 191 | 192 | GSList *pending_buddy_info_requests; 193 | 194 | PurpleCircBuffer *write_buffer; 195 | guint writeh; 196 | 197 | gboolean reinit; 198 | 199 | JabberCapabilities server_caps; 200 | gboolean googletalk; 201 | char *server_name; 202 | 203 | char *gmail_last_time; 204 | char *gmail_last_tid; 205 | 206 | char *serverFQDN; 207 | 208 | #ifdef HAVE_CYRUS_SASL 209 | sasl_conn_t *sasl; 210 | sasl_callback_t *sasl_cb; 211 | sasl_secret_t *sasl_secret; 212 | const char *current_mech; 213 | int auth_fail_count; 214 | 215 | int sasl_state; 216 | int sasl_maxbuf; 217 | GString *sasl_mechs; 218 | #endif 219 | 220 | gboolean unregistration; 221 | PurpleAccountUnregistrationCb unregistration_cb; 222 | void *unregistration_user_data; 223 | 224 | gboolean vcard_fetched; 225 | /* Timer at login to push updated avatar */ 226 | guint vcard_timer; 227 | 228 | /* Entity Capabilities hash */ 229 | char *caps_hash; 230 | 231 | /* does the local server support PEP? */ 232 | gboolean pep; 233 | 234 | /* Is Buzz enabled? */ 235 | gboolean allowBuzz; 236 | 237 | /* A list of JabberAdHocCommands supported by the server */ 238 | GList *commands; 239 | 240 | /* last presence update to check for differences */ 241 | JabberBuddyState old_state; 242 | char *old_msg; 243 | int old_priority; 244 | char *old_avatarhash; 245 | 246 | /* same for user tune */ 247 | char *old_artist; 248 | char *old_title; 249 | char *old_source; 250 | char *old_uri; 251 | int old_length; 252 | char *old_track; 253 | 254 | char *certificate_CN; 255 | 256 | /* A purple timeout tag for the keepalive */ 257 | guint keepalive_timeout; 258 | guint max_inactivity; 259 | guint inactivity_timer; 260 | 261 | PurpleSrvResponse *srv_rec; 262 | guint srv_rec_idx; 263 | guint max_srv_rec_idx; 264 | 265 | /* BOSH stuff */ 266 | PurpleBOSHConnection *bosh; 267 | 268 | /** 269 | * This linked list contains PurpleUtilFetchUrlData structs 270 | * for when we lookup buddy icons from a url 271 | */ 272 | GSList *url_datas; 273 | 274 | /* keep a hash table of JingleSessions */ 275 | GHashTable *sessions; 276 | 277 | /* maybe this should only be present when USE_VV? */ 278 | gchar *stun_ip; 279 | int stun_port; 280 | PurpleDnsQueryData *stun_query; 281 | 282 | /* stuff for Google's relay handling */ 283 | gchar *google_relay_token; 284 | gchar *google_relay_host; 285 | GList *google_relay_requests; /* the HTTP requests to get */ 286 | /* relay info */ 287 | 288 | /* facebook quirks */ 289 | gboolean facebook_roster_cleanup_performed; 290 | }; 291 | 292 | typedef gboolean (JabberFeatureEnabled)(JabberStream *js, const gchar *namespace); 293 | 294 | typedef struct _JabberFeature 295 | { 296 | gchar *namespace; 297 | JabberFeatureEnabled *is_enabled; 298 | } JabberFeature; 299 | 300 | typedef struct _JabberIdentity 301 | { 302 | gchar *category; 303 | gchar *type; 304 | gchar *name; 305 | gchar *lang; 306 | } JabberIdentity; 307 | 308 | typedef struct _JabberBytestreamsStreamhost { 309 | char *jid; 310 | char *host; 311 | int port; 312 | char *zeroconf; 313 | } JabberBytestreamsStreamhost; 314 | 315 | /* what kind of additional features as returned from disco#info are supported? */ 316 | extern GList *jabber_features; 317 | /* A sorted list of identities advertised. Use jabber_add_identity to add 318 | * so it remains sorted. 319 | */ 320 | extern GList *jabber_identities; 321 | 322 | void jabber_stream_features_parse(JabberStream *js, xmlnode *packet); 323 | void jabber_process_packet(JabberStream *js, xmlnode **packet); 324 | void jabber_send(JabberStream *js, xmlnode *data); 325 | void jabber_send_raw(JabberStream *js, const char *data, int len); 326 | void jabber_send_signal_cb(PurpleConnection *pc, xmlnode **packet, 327 | gpointer unused); 328 | 329 | void jabber_stream_set_state(JabberStream *js, JabberStreamState state); 330 | 331 | void jabber_register_parse(JabberStream *js, const char *from, 332 | JabberIqType type, const char *id, xmlnode *query); 333 | void jabber_register_start(JabberStream *js); 334 | 335 | char *jabber_get_next_id(JabberStream *js); 336 | 337 | /** Parse an error into a human-readable string and optionally a disconnect 338 | * reason. 339 | * @param js the stream on which the error occurred. 340 | * @param packet the error packet 341 | * @param reason where to store the disconnection reason, or @c NULL if you 342 | * don't care or you don't intend to close the connection. 343 | */ 344 | char *jabber_parse_error(JabberStream *js, xmlnode *packet, PurpleConnectionError *reason); 345 | 346 | /** 347 | * Add a feature to the list of features advertised via disco#info. If you 348 | * call this while accounts are connected, Bad Things(TM) will happen because 349 | * the Entity Caps hash will be out-of-date (which should be fixed :/) 350 | * 351 | * @param namespace The namespace of the feature 352 | * @param cb A callback determining whether or not this feature 353 | * will advertised; may be NULL. 354 | */ 355 | void jabber_add_feature(const gchar *namespace, JabberFeatureEnabled cb); 356 | void jabber_remove_feature(const gchar *namespace); 357 | 358 | /** Adds an identity to this jabber library instance. For list of valid values 359 | * visit the website of the XMPP Registrar 360 | * (http://www.xmpp.org/registrar/disco-categories.html#client). 361 | * 362 | * Like with jabber_add_feature, if you call this while accounts are connected, 363 | * Bad Things will happen. 364 | * 365 | * @param category the category of the identity. 366 | * @param type the type of the identity. 367 | * @param language the language localization of the name. Can be NULL. 368 | * @param name the name of the identity. 369 | */ 370 | void jabber_add_identity(const gchar *category, const gchar *type, const gchar *lang, const gchar *name); 371 | 372 | /** 373 | * GCompareFunc for JabberIdentity structs. 374 | */ 375 | gint jabber_identity_compare(gconstpointer a, gconstpointer b); 376 | 377 | /** 378 | * Returns true if this connection is over a secure (SSL) stream. Use this 379 | * instead of checking js->gsc because BOSH stores its PurpleSslConnection 380 | * members in its own data structure. 381 | */ 382 | gboolean jabber_stream_is_ssl(JabberStream *js); 383 | 384 | /** 385 | * Restart the "we haven't sent anything in a while and should send 386 | * something or the server will kick us off" timer (obviously 387 | * called when sending something. It's exposed for BOSH.) 388 | */ 389 | void jabber_stream_restart_inactivity_timer(JabberStream *js); 390 | 391 | /** PRPL functions */ 392 | const char *jabber_list_icon(PurpleAccount *a, PurpleBuddy *b); 393 | const char* jabber_list_emblem(PurpleBuddy *b); 394 | char *jabber_status_text(PurpleBuddy *b); 395 | void jabber_tooltip_text(PurpleBuddy *b, PurpleNotifyUserInfo *user_info, gboolean full); 396 | GList *jabber_status_types(PurpleAccount *account); 397 | void jabber_login(PurpleAccount *account); 398 | void jabber_close(PurpleConnection *gc); 399 | void jabber_idle_set(PurpleConnection *gc, int idle); 400 | void jabber_blocklist_parse_push(JabberStream *js, const char *from, 401 | JabberIqType type, const char *id, 402 | xmlnode *child); 403 | void jabber_request_block_list(JabberStream *js); 404 | void jabber_add_deny(PurpleConnection *gc, const char *who); 405 | void jabber_rem_deny(PurpleConnection *gc, const char *who); 406 | void jabber_keepalive(PurpleConnection *gc); 407 | void jabber_register_gateway(JabberStream *js, const char *gateway); 408 | void jabber_register_account(PurpleAccount *account); 409 | void jabber_unregister_account(PurpleAccount *account, PurpleAccountUnregistrationCb cb, void *user_data); 410 | gboolean jabber_send_attention(PurpleConnection *gc, const char *username, guint code); 411 | GList *jabber_attention_types(PurpleAccount *account); 412 | void jabber_convo_closed(PurpleConnection *gc, const char *who); 413 | PurpleChat *jabber_find_blist_chat(PurpleAccount *account, const char *name); 414 | gboolean jabber_offline_message(const PurpleBuddy *buddy); 415 | int jabber_prpl_send_raw(PurpleConnection *gc, const char *buf, int len); 416 | GList *jabber_actions(PurplePlugin *plugin, gpointer context); 417 | 418 | gboolean jabber_audio_enabled(JabberStream *js, const char *unused); 419 | gboolean jabber_video_enabled(JabberStream *js, const char *unused); 420 | gboolean jabber_initiate_media(PurpleAccount *account, const char *who, 421 | PurpleMediaSessionType type); 422 | PurpleMediaCaps jabber_get_media_caps(PurpleAccount *account, const char *who); 423 | gboolean jabber_can_receive_file(PurpleConnection *gc, const gchar *who); 424 | 425 | void jabber_plugin_init(PurplePlugin *plugin); 426 | void jabber_plugin_uninit(PurplePlugin *plugin); 427 | 428 | #endif /* PURPLE_JABBER_H_ */ 429 | -------------------------------------------------------------------------------- /headers/jabber/jutil.h: -------------------------------------------------------------------------------- 1 | /** 2 | * @file jutil.h utility functions 3 | * 4 | * purple 5 | * 6 | * Purple is the legal property of its developers, whose names are too numerous 7 | * to list here. Please refer to the COPYRIGHT file distributed with this 8 | * source distribution. 9 | * 10 | * This program is free software; you can redistribute it and/or modify 11 | * it under the terms of the GNU General Public License as published by 12 | * the Free Software Foundation; either version 2 of the License, or 13 | * (at your option) any later version. 14 | * 15 | * This program is distributed in the hope that it will be useful, 16 | * but WITHOUT ANY WARRANTY; without even the implied warranty of 17 | * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the 18 | * GNU General Public License for more details. 19 | * 20 | * You should have received a copy of the GNU General Public License 21 | * along with this program; if not, write to the Free Software 22 | * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02111-1301 USA 23 | */ 24 | #ifndef PURPLE_JABBER_JUTIL_H_ 25 | #define PURPLE_JABBER_JUTIL_H_ 26 | 27 | typedef struct _JabberID { 28 | char *node; 29 | char *domain; 30 | char *resource; 31 | } JabberID; 32 | 33 | typedef enum { 34 | JABBER_BUDDY_STATE_UNKNOWN = -2, 35 | JABBER_BUDDY_STATE_ERROR = -1, 36 | JABBER_BUDDY_STATE_UNAVAILABLE = 0, 37 | JABBER_BUDDY_STATE_ONLINE, 38 | JABBER_BUDDY_STATE_CHAT, 39 | JABBER_BUDDY_STATE_AWAY, 40 | JABBER_BUDDY_STATE_XA, 41 | JABBER_BUDDY_STATE_DND 42 | } JabberBuddyState; 43 | 44 | #include "jabber.h" 45 | 46 | JabberID* jabber_id_new(const char *str); 47 | 48 | /** 49 | * Compare two JIDs for equality. In addition to the node and domain, 50 | * the resources of the two JIDs must also be equal (or both absent). 51 | */ 52 | gboolean jabber_id_equal(const JabberID *jid1, const JabberID *jid2); 53 | 54 | void jabber_id_free(JabberID *jid); 55 | 56 | char *jabber_get_domain(const char *jid); 57 | char *jabber_get_resource(const char *jid); 58 | char *jabber_get_bare_jid(const char *jid); 59 | char *jabber_id_get_bare_jid(const JabberID *jid); 60 | char *jabber_id_get_full_jid(const JabberID *jid); 61 | JabberID *jabber_id_to_bare_jid(const JabberID *jid); 62 | 63 | gboolean jabber_jid_is_domain(const char *jid); 64 | 65 | const char *jabber_normalize(const PurpleAccount *account, const char *in); 66 | 67 | /* Returns true if JID is the bare JID of our server. */ 68 | gboolean jabber_is_own_server(JabberStream *js, const char *jid); 69 | 70 | /* Returns true if JID is the bare JID of our account. */ 71 | gboolean jabber_is_own_account(JabberStream *js, const char *jid); 72 | 73 | gboolean jabber_nodeprep_validate(const char *); 74 | gboolean jabber_domain_validate(const char *); 75 | gboolean jabber_resourceprep_validate(const char *); 76 | 77 | /** 78 | * Apply the SASLprep profile of stringprep to the string passed in. 79 | * 80 | * @returns A newly allocated string containing the normalized version 81 | * of the input, or NULL if an error occurred (the string could 82 | * not be normalized) 83 | */ 84 | char *jabber_saslprep(const char *); 85 | 86 | /* state -> readable name */ 87 | const char *jabber_buddy_state_get_name(JabberBuddyState state); 88 | /* state -> core id */ 89 | const char *jabber_buddy_state_get_status_id(JabberBuddyState state); 90 | /* state -> show attr (for presence stanza) */ 91 | const char *jabber_buddy_state_get_show(JabberBuddyState state); 92 | /* core id -> state */ 93 | JabberBuddyState jabber_buddy_status_id_get_state(const char *id); 94 | /* show attr (presence stanza) -> state */ 95 | JabberBuddyState jabber_buddy_show_get_state(const char *id); 96 | 97 | char *jabber_calculate_data_hash(gconstpointer data, size_t len, 98 | const gchar *hash_algo); 99 | #endif /* PURPLE_JABBER_JUTIL_H_ */ 100 | -------------------------------------------------------------------------------- /headers/jabber/message.h: -------------------------------------------------------------------------------- 1 | /** 2 | * @file message.h Message handlers 3 | * 4 | * purple 5 | * 6 | * Purple is the legal property of its developers, whose names are too numerous 7 | * to list here. Please refer to the COPYRIGHT file distributed with this 8 | * source distribution. 9 | * 10 | * This program is free software; you can redistribute it and/or modify 11 | * it under the terms of the GNU General Public License as published by 12 | * the Free Software Foundation; either version 2 of the License, or 13 | * (at your option) any later version. 14 | * 15 | * This program is distributed in the hope that it will be useful, 16 | * but WITHOUT ANY WARRANTY; without even the implied warranty of 17 | * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the 18 | * GNU General Public License for more details. 19 | * 20 | * You should have received a copy of the GNU General Public License 21 | * along with this program; if not, write to the Free Software 22 | * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02111-1301 USA 23 | */ 24 | #ifndef PURPLE_JABBER_MESSAGE_H_ 25 | #define PURPLE_JABBER_MESSAGE_H_ 26 | 27 | #include "buddy.h" 28 | #include "jabber.h" 29 | #include "xmlnode.h" 30 | 31 | typedef struct _JabberMessage { 32 | JabberStream *js; 33 | enum { 34 | JABBER_MESSAGE_NORMAL, 35 | JABBER_MESSAGE_CHAT, 36 | JABBER_MESSAGE_GROUPCHAT, 37 | JABBER_MESSAGE_HEADLINE, 38 | JABBER_MESSAGE_ERROR, 39 | JABBER_MESSAGE_GROUPCHAT_INVITE, 40 | JABBER_MESSAGE_EVENT, 41 | JABBER_MESSAGE_OTHER 42 | } type; 43 | time_t sent; 44 | gboolean delayed; 45 | gboolean hasBuzz; 46 | char *id; 47 | char *from; 48 | char *to; 49 | char *subject; 50 | char *body; 51 | char *xhtml; 52 | char *password; 53 | char *error; 54 | char *thread_id; 55 | enum { 56 | JM_STATE_NONE, 57 | JM_STATE_ACTIVE, 58 | JM_STATE_COMPOSING, 59 | JM_STATE_PAUSED, 60 | JM_STATE_INACTIVE, 61 | JM_STATE_GONE 62 | } chat_state; 63 | GList *etc; 64 | GList *eventitems; 65 | } JabberMessage; 66 | 67 | void jabber_message_free(JabberMessage *jm); 68 | 69 | void jabber_message_send(JabberMessage *jm); 70 | 71 | void jabber_message_parse(JabberStream *js, xmlnode *packet); 72 | int jabber_message_send_im(PurpleConnection *gc, const char *who, const char *msg, 73 | PurpleMessageFlags flags); 74 | int jabber_message_send_chat(PurpleConnection *gc, int id, const char *message, PurpleMessageFlags flags); 75 | 76 | unsigned int jabber_send_typing(PurpleConnection *gc, const char *who, PurpleTypingState state); 77 | 78 | gboolean jabber_buzz_isenabled(JabberStream *js, const gchar *namespace); 79 | 80 | gboolean jabber_custom_smileys_isenabled(JabberStream *js, const gchar *namespace); 81 | 82 | #endif /* PURPLE_JABBER_MESSAGE_H_ */ 83 | -------------------------------------------------------------------------------- /headers/jabber/namespaces.h: -------------------------------------------------------------------------------- 1 | /* 2 | * purple - Jabber Protocol Plugin 3 | * 4 | * Purple is the legal property of its developers, whose names are too numerous 5 | * to list here. Please refer to the COPYRIGHT file distributed with this 6 | * source distribution. 7 | * 8 | * This program is free software; you can redistribute it and/or modify 9 | * it under the terms of the GNU General Public License as published by 10 | * the Free Software Foundation; either version 2 of the License, or 11 | * (at your option) any later version. 12 | * 13 | * This program is distributed in the hope that it will be useful, 14 | * but WITHOUT ANY WARRANTY; without even the implied warranty of 15 | * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the 16 | * GNU General Public License for more details. 17 | * 18 | * You should have received a copy of the GNU General Public License 19 | * along with this program; if not, write to the Free Software 20 | * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02111-1301 USA 21 | * 22 | */ 23 | 24 | #ifndef PURPLE_JABBER_NAMESPACES_H_ 25 | #define PURPLE_JABBER_NAMESPACES_H_ 26 | 27 | #define NS_XMPP_BIND "urn:ietf:params:xml:ns:xmpp-bind" 28 | #define NS_XMPP_CLIENT "jabber:client" 29 | #define NS_XMPP_SASL "urn:ietf:params:xml:ns:xmpp-sasl" 30 | #define NS_XMPP_SESSION "urn:ietf:params:xml:ns:xmpp-session" 31 | #define NS_XMPP_STANZAS "urn:ietf:params:xml:ns:xmpp-stanzas" 32 | #define NS_XMPP_STREAMS "http://etherx.jabber.org/streams" 33 | #define NS_XMPP_TLS "urn:ietf:params:xml:ns:xmpp-tls" 34 | 35 | /* XEP-0012 Last Activity (and XEP-0256 Last Activity in Presence) */ 36 | #define NS_LAST_ACTIVITY "jabber:iq:last" 37 | 38 | /* XEP-0030 Service Discovery */ 39 | #define NS_DISCO_INFO "http://jabber.org/protocol/disco#info" 40 | #define NS_DISCO_ITEMS "http://jabber.org/protocol/disco#items" 41 | 42 | /* XEP-0047 IBB (In-band bytestreams) */ 43 | #define NS_IBB "http://jabber.org/protocol/ibb" 44 | 45 | /* XEP-0065 SOCKS5 Bytestreams */ 46 | #define NS_BYTESTREAMS "http://jabber.org/protocol/bytestreams" 47 | 48 | /* XEP-0066 Out of Band Data (OOB) */ 49 | #define NS_OOB_IQ_DATA "jabber:iq:oob" 50 | #define NS_OOB_X_DATA "jabber:x:oob" 51 | 52 | /* XEP-0071 XHTML-IM (rich-text messages) */ 53 | #define NS_XHTML_IM "http://jabber.org/protocol/xhtml-im" 54 | #define NS_XHTML "http://www.w3.org/1999/xhtml" 55 | 56 | /* XEP-0084 v0.12 User Avatar */ 57 | #define NS_AVATAR_0_12_DATA "http://www.xmpp.org/extensions/xep-0084.html#ns-data" 58 | #define NS_AVATAR_0_12_METADATA "http://www.xmpp.org/extensions/xep-0084.html#ns-metadata" 59 | 60 | /* XEP-0084 v1.1 User Avatar */ 61 | #define NS_AVATAR_1_1_DATA "urn:xmpp:avatar:data" 62 | #define NS_AVATAR_1_1_METADATA "urn:xmpp:avatar:metadata" 63 | 64 | /* XEP-0096 SI File Transfer */ 65 | #define NS_SI_FILE_TRANSFER "http://jabber.org/protocol/si/profile/file-transfer" 66 | 67 | /* XEP-0124 Bidirectional-streams Over Synchronous HTTP (BOSH) */ 68 | #define NS_BOSH "http://jabber.org/protocol/httpbind" 69 | 70 | /* XEP-0191 Simple Communications Blocking */ 71 | #define NS_SIMPLE_BLOCKING "urn:xmpp:blocking" 72 | 73 | /* XEP-0199 Ping */ 74 | #define NS_PING "urn:xmpp:ping" 75 | 76 | /* XEP-0202 Entity Time */ 77 | #define NS_ENTITY_TIME "urn:xmpp:time" 78 | 79 | /* XEP-0203 Delayed Delivery (and legacy delayed delivery) */ 80 | #define NS_DELAYED_DELIVERY "urn:xmpp:delay" 81 | #define NS_DELAYED_DELIVERY_LEGACY "jabber:x:delay" 82 | 83 | /* XEP-0206 XMPP over BOSH */ 84 | #define NS_XMPP_BOSH "urn:xmpp:xbosh" 85 | 86 | /* XEP-0224 Attention */ 87 | #define NS_ATTENTION "urn:xmpp:attention:0" 88 | 89 | /* XEP-0231 BoB (Bits of Binary) */ 90 | #define NS_BOB "urn:xmpp:bob" 91 | 92 | /* XEP-0237 Roster Versioning */ 93 | #define NS_ROSTER_VERSIONING "urn:xmpp:features:rosterver" 94 | 95 | /* XEP-0264 File Transfer Thumbnails (Thumbs) */ 96 | #define NS_THUMBS "urn:xmpp:thumbs:0" 97 | 98 | /* Google extensions */ 99 | #define NS_GOOGLE_CAMERA "http://www.google.com/xmpp/protocol/camera/v1" 100 | #define NS_GOOGLE_VIDEO "http://www.google.com/xmpp/protocol/video/v1" 101 | #define NS_GOOGLE_VOICE "http://www.google.com/xmpp/protocol/voice/v1" 102 | #define NS_GOOGLE_JINGLE_INFO "google:jingleinfo" 103 | 104 | #define NS_GOOGLE_MAIL_NOTIFY "google:mail:notify" 105 | #define NS_GOOGLE_ROSTER "google:roster" 106 | 107 | #define NS_GOOGLE_PROTOCOL_SESSION "http://www.google.com/xmpp/protocol/session" 108 | #define NS_GOOGLE_SESSION "http://www.google.com/session" 109 | #define NS_GOOGLE_SESSION_PHONE "http://www.google.com/session/phone" 110 | #define NS_GOOGLE_SESSION_VIDEO "http://www.google.com/session/video" 111 | 112 | #endif /* PURPLE_JABBER_NAMESPACES_H_ */ 113 | -------------------------------------------------------------------------------- /headers/jabber/oob.h: -------------------------------------------------------------------------------- 1 | /** 2 | * @file oob.h out-of-band transfer functions 3 | * 4 | * purple 5 | * 6 | * Purple is the legal property of its developers, whose names are too numerous 7 | * to list here. Please refer to the COPYRIGHT file distributed with this 8 | * source distribution. 9 | * 10 | * This program is free software; you can redistribute it and/or modify 11 | * it under the terms of the GNU General Public License as published by 12 | * the Free Software Foundation; either version 2 of the License, or 13 | * (at your option) any later version. 14 | * 15 | * This program is distributed in the hope that it will be useful, 16 | * but WITHOUT ANY WARRANTY; without even the implied warranty of 17 | * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the 18 | * GNU General Public License for more details. 19 | * 20 | * You should have received a copy of the GNU General Public License 21 | * along with this program; if not, write to the Free Software 22 | * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02111-1301 USA 23 | */ 24 | #ifndef PURPLE_JABBER_OOB_H_ 25 | #define PURPLE_JABBER_OOB_H_ 26 | 27 | #include "jabber.h" 28 | 29 | void jabber_oob_parse(JabberStream *js, const char *from, JabberIqType type, 30 | const char *id, xmlnode *querynode); 31 | 32 | #endif /* PURPLE_JABBER_OOB_H_ */ 33 | -------------------------------------------------------------------------------- /headers/jabber/parser.h: -------------------------------------------------------------------------------- 1 | /** 2 | * @file parser.h XML parser functions 3 | * 4 | * purple 5 | * 6 | * Purple is the legal property of its developers, whose names are too numerous 7 | * to list here. Please refer to the COPYRIGHT file distributed with this 8 | * source distribution. 9 | * 10 | * This program is free software; you can redistribute it and/or modify 11 | * it under the terms of the GNU General Public License as published by 12 | * the Free Software Foundation; either version 2 of the License, or 13 | * (at your option) any later version. 14 | * 15 | * This program is distributed in the hope that it will be useful, 16 | * but WITHOUT ANY WARRANTY; without even the implied warranty of 17 | * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the 18 | * GNU General Public License for more details. 19 | * 20 | * You should have received a copy of the GNU General Public License 21 | * along with this program; if not, write to the Free Software 22 | * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02111-1301 USA 23 | */ 24 | #ifndef PURPLE_JABBER_PARSER_H_ 25 | #define PURPLE_JABBER_PARSER_H_ 26 | 27 | #include "jabber.h" 28 | 29 | void jabber_parser_setup(JabberStream *js); 30 | void jabber_parser_free(JabberStream *js); 31 | void jabber_parser_process(JabberStream *js, const char *buf, int len); 32 | 33 | #endif /* PURPLE_JABBER_PARSER_H_ */ 34 | -------------------------------------------------------------------------------- /headers/jabber/pep.h: -------------------------------------------------------------------------------- 1 | /* 2 | * purple - Jabber Protocol Plugin 3 | * 4 | * Purple is the legal property of its developers, whose names are too numerous 5 | * to list here. Please refer to the COPYRIGHT file distributed with this 6 | * source distribution. 7 | * 8 | * This program is free software; you can redistribute it and/or modify 9 | * it under the terms of the GNU General Public License as published by 10 | * the Free Software Foundation; either version 2 of the License, or 11 | * (at your option) any later version. 12 | * 13 | * This program is distributed in the hope that it will be useful, 14 | * but WITHOUT ANY WARRANTY; without even the implied warranty of 15 | * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the 16 | * GNU General Public License for more details. 17 | * 18 | * You should have received a copy of the GNU General Public License 19 | * along with this program; if not, write to the Free Software 20 | * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02111-1301 USA 21 | * 22 | */ 23 | 24 | #ifndef PURPLE_JABBER_PEP_H_ 25 | #define PURPLE_JABBER_PEP_H_ 26 | 27 | #include "jabber.h" 28 | #include "message.h" 29 | #include "buddy.h" 30 | 31 | void jabber_pep_init(void); 32 | void jabber_pep_uninit(void); 33 | 34 | void jabber_pep_init_actions(GList **m); 35 | 36 | /* 37 | * Callback for receiving PEP events. 38 | * 39 | * @parameter js The JabberStream this item was received on 40 | * @parameter items The <items/>-tag with the <item/>-children 41 | */ 42 | typedef void (JabberPEPHandler)(JabberStream *js, const char *from, xmlnode *items); 43 | 44 | /* 45 | * Registers a callback for PEP events. Also automatically announces this receiving capability via disco#info. 46 | * Don't forget to use jabber_add_feature when supporting the sending of PEP events of this type. 47 | * 48 | * @parameter xmlns The namespace for this event 49 | * @parameter handlerfunc The callback to be used when receiving an event with this namespace 50 | */ 51 | void jabber_pep_register_handler(const char *xmlns, JabberPEPHandler handlerfunc); 52 | 53 | /* 54 | * Request a specific item from another PEP node. 55 | * 56 | * @parameter js The JabberStream that should be used 57 | * @parameter to The target PEP node 58 | * @parameter node The node name of the item that is requested 59 | * @parameter id The item id of the requested item (may be NULL) 60 | * @parameter cb The callback to be used when this item is received 61 | * 62 | * The items element passed to the callback will be NULL if any error occurred (like a permission error, node doesn't exist etc.) 63 | */ 64 | void jabber_pep_request_item(JabberStream *js, const char *to, const char *node, const char *id, JabberPEPHandler cb); 65 | 66 | /* 67 | * Default callback that can be used for namespaces which should only be enabled when PEP is supported 68 | * 69 | * @parameter js The JabberStream struct for this connection 70 | * @parameter namespace The namespace that's queried, ignored. 71 | * 72 | * @returns TRUE when PEP is enabled, FALSE otherwise 73 | */ 74 | gboolean jabber_pep_namespace_only_when_pep_enabled_cb(JabberStream *js, const gchar *namespace); 75 | 76 | void jabber_handle_event(JabberMessage *jm); 77 | 78 | /** 79 | * Delete the specified PEP node. 80 | */ 81 | void jabber_pep_delete_node(JabberStream *js, const gchar *node); 82 | 83 | /* 84 | * Publishes PEP item(s) 85 | * 86 | * @parameter js The JabberStream associated with the connection this event should be published 87 | * @parameter publish The publish node. This could be for example <publish node='http://jabber.org/protocol/tune'/> with an <item/> as subnode 88 | */ 89 | void jabber_pep_publish(JabberStream *js, xmlnode *publish); 90 | 91 | #endif /* PURPLE_JABBER_PEP_H_ */ 92 | -------------------------------------------------------------------------------- /headers/jabber/ping.h: -------------------------------------------------------------------------------- 1 | /** 2 | * @file ping.h ping functions 3 | * 4 | * purple 5 | * 6 | * Purple is the legal property of its developers, whose names are too numerous 7 | * to list here. Please refer to the COPYRIGHT file distributed with this 8 | * source distribution. 9 | * 10 | * This program is free software; you can redistribute it and/or modify 11 | * it under the terms of the GNU General Public License as published by 12 | * the Free Software Foundation; either version 2 of the License, or 13 | * (at your option) any later version. 14 | * 15 | * This program is distributed in the hope that it will be useful, 16 | * but WITHOUT ANY WARRANTY; without even the implied warranty of 17 | * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the 18 | * GNU General Public License for more details. 19 | * 20 | * You should have received a copy of the GNU General Public License 21 | * along with this program; if not, write to the Free Software 22 | * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02111-1301 USA 23 | */ 24 | #ifndef PURPLE_JABBER_PING_H_ 25 | #define PURPLE_JABBER_PING_H_ 26 | 27 | #include "jabber.h" 28 | #include "iq.h" 29 | #include "xmlnode.h" 30 | 31 | void jabber_ping_parse(JabberStream *js, const char *from, 32 | JabberIqType, const char *id, xmlnode *child); 33 | gboolean jabber_ping_jid(JabberStream *js, const char *jid); 34 | void jabber_keepalive_ping(JabberStream *js); 35 | 36 | #endif /* PURPLE_JABBER_PING_H_ */ 37 | -------------------------------------------------------------------------------- /headers/jabber/presence.h: -------------------------------------------------------------------------------- 1 | /** 2 | * @file presence.h Presence 3 | * 4 | * purple 5 | * 6 | * Purple is the legal property of its developers, whose names are too numerous 7 | * to list here. Please refer to the COPYRIGHT file distributed with this 8 | * source distribution. 9 | * 10 | * This program is free software; you can redistribute it and/or modify 11 | * it under the terms of the GNU General Public License as published by 12 | * the Free Software Foundation; either version 2 of the License, or 13 | * (at your option) any later version. 14 | * 15 | * This program is distributed in the hope that it will be useful, 16 | * but WITHOUT ANY WARRANTY; without even the implied warranty of 17 | * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the 18 | * GNU General Public License for more details. 19 | * 20 | * You should have received a copy of the GNU General Public License 21 | * along with this program; if not, write to the Free Software 22 | * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02111-1301 USA 23 | */ 24 | #ifndef PURPLE_JABBER_PRESENCE_H_ 25 | #define PURPLE_JABBER_PRESENCE_H_ 26 | 27 | typedef enum { 28 | JABBER_PRESENCE_ERROR = -2, 29 | JABBER_PRESENCE_PROBE = -1, 30 | JABBER_PRESENCE_AVAILABLE, 31 | JABBER_PRESENCE_UNAVAILABLE, 32 | JABBER_PRESENCE_SUBSCRIBE, 33 | JABBER_PRESENCE_SUBSCRIBED, 34 | JABBER_PRESENCE_UNSUBSCRIBE, 35 | JABBER_PRESENCE_UNSUBSCRIBED 36 | } JabberPresenceType; 37 | 38 | typedef struct _JabberPresenceChatInfo JabberPresenceChatInfo; 39 | typedef struct _JabberPresence JabberPresence; 40 | 41 | #include "buddy.h" 42 | #include "chat.h" 43 | #include "jabber.h" 44 | #include "jutil.h" 45 | #include "xmlnode.h" 46 | 47 | struct _JabberPresenceChatInfo { 48 | GSList *codes; 49 | xmlnode *item; 50 | }; 51 | 52 | struct _JabberPresence { 53 | JabberPresenceType type; 54 | JabberID *jid_from; 55 | const char *from; 56 | const char *to; 57 | const char *id; 58 | 59 | JabberBuddy *jb; 60 | JabberChat *chat; 61 | JabberPresenceChatInfo chat_info; 62 | xmlnode *caps; /* TODO: Temporary, see presence.c:parse_caps */ 63 | 64 | JabberBuddyState state; 65 | gchar *status; 66 | int priority; 67 | 68 | char *vcard_avatar_hash; 69 | char *nickname; 70 | 71 | gboolean delayed; 72 | time_t sent; 73 | int idle; 74 | }; 75 | 76 | typedef void (JabberPresenceHandler)(JabberStream *js, JabberPresence *presence, 77 | xmlnode *child); 78 | void jabber_presence_register_handler(const char *node, const char *xmlns, 79 | JabberPresenceHandler *handler); 80 | 81 | void jabber_presence_init(void); 82 | void jabber_presence_uninit(void); 83 | 84 | void jabber_set_status(PurpleAccount *account, PurpleStatus *status); 85 | 86 | /** 87 | * Send a full presence stanza. 88 | * 89 | * @param js A JabberStream object. 90 | * @param force Force sending the presence stanza, irrespective of whether 91 | * the contents seem to have changed. 92 | */ 93 | void jabber_presence_send(JabberStream *js, gboolean force); 94 | 95 | xmlnode *jabber_presence_create(JabberBuddyState state, const char *msg, int priority); /* DEPRECATED */ 96 | xmlnode *jabber_presence_create_js(JabberStream *js, JabberBuddyState state, const char *msg, int priority); 97 | void jabber_presence_parse(JabberStream *js, xmlnode *packet); 98 | void jabber_presence_subscription_set(JabberStream *js, const char *who, 99 | const char *type); 100 | void jabber_presence_fake_to_self(JabberStream *js, PurpleStatus *status); 101 | void purple_status_to_jabber(const PurpleStatus *status, JabberBuddyState *state, char **msg, int *priority); 102 | 103 | #endif /* PURPLE_JABBER_PRESENCE_H_ */ 104 | -------------------------------------------------------------------------------- /headers/jabber/roster.h: -------------------------------------------------------------------------------- 1 | /** 2 | * @file roster.h Roster manipulation 3 | * 4 | * purple 5 | * 6 | * Purple is the legal property of its developers, whose names are too numerous 7 | * to list here. Please refer to the COPYRIGHT file distributed with this 8 | * source distribution. 9 | * 10 | * This program is free software; you can redistribute it and/or modify 11 | * it under the terms of the GNU General Public License as published by 12 | * the Free Software Foundation; either version 2 of the License, or 13 | * (at your option) any later version. 14 | * 15 | * This program is distributed in the hope that it will be useful, 16 | * but WITHOUT ANY WARRANTY; without even the implied warranty of 17 | * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the 18 | * GNU General Public License for more details. 19 | * 20 | * You should have received a copy of the GNU General Public License 21 | * along with this program; if not, write to the Free Software 22 | * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02111-1301 USA 23 | */ 24 | #ifndef PURPLE_JABBER_ROSTER_H_ 25 | #define PURPLE_JABBER_ROSTER_H_ 26 | 27 | /* it must *not* be localized */ 28 | #define JABBER_ROSTER_DEFAULT_GROUP "Buddies" 29 | 30 | #include "jabber.h" 31 | 32 | void jabber_roster_request(JabberStream *js); 33 | 34 | void jabber_roster_parse(JabberStream *js, const char *from, 35 | JabberIqType type, const char *id, xmlnode *query); 36 | 37 | void jabber_roster_add_buddy(PurpleConnection *gc, PurpleBuddy *buddy, 38 | PurpleGroup *group); 39 | void jabber_roster_alias_change(PurpleConnection *gc, const char *name, 40 | const char *alias); 41 | void jabber_roster_group_change(PurpleConnection *gc, const char *name, 42 | const char *old_group, const char *new_group); 43 | void jabber_roster_group_rename(PurpleConnection *gc, const char *old_name, 44 | PurpleGroup *group, GList *moved_buddies); 45 | void jabber_roster_remove_buddy(PurpleConnection *gc, PurpleBuddy *buddy, 46 | PurpleGroup *group); 47 | 48 | const gchar * 49 | jabber_roster_group_get_global_name(PurpleGroup *group); 50 | 51 | #endif /* PURPLE_JABBER_ROSTER_H_ */ 52 | -------------------------------------------------------------------------------- /headers/jabber/si.h: -------------------------------------------------------------------------------- 1 | /** 2 | * @file si.h SI transfer functions 3 | * 4 | * purple 5 | * 6 | * Purple is the legal property of its developers, whose names are too numerous 7 | * to list here. Please refer to the COPYRIGHT file distributed with this 8 | * source distribution. 9 | * 10 | * This program is free software; you can redistribute it and/or modify 11 | * it under the terms of the GNU General Public License as published by 12 | * the Free Software Foundation; either version 2 of the License, or 13 | * (at your option) any later version. 14 | * 15 | * This program is distributed in the hope that it will be useful, 16 | * but WITHOUT ANY WARRANTY; without even the implied warranty of 17 | * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the 18 | * GNU General Public License for more details. 19 | * 20 | * You should have received a copy of the GNU General Public License 21 | * along with this program; if not, write to the Free Software 22 | * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02111-1301 USA 23 | */ 24 | #ifndef PURPLE_JABBER_SI_H_ 25 | #define PURPLE_JABBER_SI_H_ 26 | 27 | #include "ft.h" 28 | 29 | #include "jabber.h" 30 | 31 | void jabber_bytestreams_parse(JabberStream *js, const char *from, 32 | JabberIqType type, const char *id, xmlnode *query); 33 | void jabber_si_parse(JabberStream *js, const char *from, JabberIqType type, 34 | const char *id, xmlnode *si); 35 | PurpleXfer *jabber_si_new_xfer(PurpleConnection *gc, const char *who); 36 | void jabber_si_xfer_send(PurpleConnection *gc, const char *who, const char *file); 37 | void jabber_si_init(void); 38 | void jabber_si_uninit(void); 39 | 40 | #endif /* PURPLE_JABBER_SI_H_ */ 41 | -------------------------------------------------------------------------------- /headers/jabber/useravatar.h: -------------------------------------------------------------------------------- 1 | /* 2 | * purple - Jabber Protocol Plugin 3 | * 4 | * Purple is the legal property of its developers, whose names are too numerous 5 | * to list here. Please refer to the COPYRIGHT file distributed with this 6 | * source distribution. 7 | * 8 | * This program is free software; you can redistribute it and/or modify 9 | * it under the terms of the GNU General Public License as published by 10 | * the Free Software Foundation; either version 2 of the License, or 11 | * (at your option) any later version. 12 | * 13 | * This program is distributed in the hope that it will be useful, 14 | * but WITHOUT ANY WARRANTY; without even the implied warranty of 15 | * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the 16 | * GNU General Public License for more details. 17 | * 18 | * You should have received a copy of the GNU General Public License 19 | * along with this program; if not, write to the Free Software 20 | * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02111-1301 USA 21 | * 22 | */ 23 | 24 | #ifndef _PURPLE_JABBER_USERAVATAR_H_ 25 | #define _PURPLE_JABBER_USERAVATAR_H_ 26 | 27 | #include "jabber.h" 28 | #include "imgstore.h" 29 | 30 | /* Implementation of XEP-0084 */ 31 | 32 | void jabber_avatar_init(void); 33 | void jabber_avatar_set(JabberStream *js, PurpleStoredImage *img); 34 | 35 | void jabber_avatar_fetch_mine(JabberStream *js); 36 | 37 | #endif /* _PURPLE_JABBER_USERAVATAR_H_ */ 38 | -------------------------------------------------------------------------------- /headers/jabber/usermood.h: -------------------------------------------------------------------------------- 1 | /* 2 | * purple - Jabber Protocol Plugin 3 | * 4 | * Purple is the legal property of its developers, whose names are too numerous 5 | * to list here. Please refer to the COPYRIGHT file distributed with this 6 | * source distribution. 7 | * 8 | * This program is free software; you can redistribute it and/or modify 9 | * it under the terms of the GNU General Public License as published by 10 | * the Free Software Foundation; either version 2 of the License, or 11 | * (at your option) any later version. 12 | * 13 | * This program is distributed in the hope that it will be useful, 14 | * but WITHOUT ANY WARRANTY; without even the implied warranty of 15 | * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the 16 | * GNU General Public License for more details. 17 | * 18 | * You should have received a copy of the GNU General Public License 19 | * along with this program; if not, write to the Free Software 20 | * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02111-1301 USA 21 | * 22 | */ 23 | 24 | #ifndef PURPLE_JABBER_USERMOOD_H_ 25 | #define PURPLE_JABBER_USERMOOD_H_ 26 | 27 | #include "jabber.h" 28 | 29 | /* Implementation of XEP-0107 */ 30 | 31 | void jabber_mood_init(void); 32 | 33 | void jabber_mood_set(JabberStream *js, 34 | const char *mood, /* must be one of the valid strings defined in the XEP */ 35 | const char *text /* might be NULL */); 36 | 37 | PurpleMood *jabber_get_moods(PurpleAccount *account); 38 | 39 | #endif /* PURPLE_JABBER_USERMOOD_H_ */ 40 | -------------------------------------------------------------------------------- /headers/jabber/usernick.h: -------------------------------------------------------------------------------- 1 | /* 2 | * purple - Jabber Protocol Plugin 3 | * 4 | * Purple is the legal property of its developers, whose names are too numerous 5 | * to list here. Please refer to the COPYRIGHT file distributed with this 6 | * source distribution. 7 | * 8 | * This program is free software; you can redistribute it and/or modify 9 | * it under the terms of the GNU General Public License as published by 10 | * the Free Software Foundation; either version 2 of the License, or 11 | * (at your option) any later version. 12 | * 13 | * This program is distributed in the hope that it will be useful, 14 | * but WITHOUT ANY WARRANTY; without even the implied warranty of 15 | * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the 16 | * GNU General Public License for more details. 17 | * 18 | * You should have received a copy of the GNU General Public License 19 | * along with this program; if not, write to the Free Software 20 | * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02111-1301 USA 21 | * 22 | */ 23 | 24 | #ifndef PURPLE_JABBER_USERNICK_H_ 25 | #define PURPLE_JABBER_USERNICK_H_ 26 | 27 | #include "jabber.h" 28 | 29 | /* Implementation of XEP-0172 */ 30 | 31 | void jabber_nick_init(void); 32 | void jabber_nick_init_action(GList **m); 33 | 34 | #endif /* PURPLE_JABBER_USERNICK_H_ */ 35 | -------------------------------------------------------------------------------- /headers/jabber/usertune.h: -------------------------------------------------------------------------------- 1 | /* 2 | * purple - Jabber Protocol Plugin 3 | * 4 | * Purple is the legal property of its developers, whose names are too numerous 5 | * to list here. Please refer to the COPYRIGHT file distributed with this 6 | * source distribution. 7 | * 8 | * This program is free software; you can redistribute it and/or modify 9 | * it under the terms of the GNU General Public License as published by 10 | * the Free Software Foundation; either version 2 of the License, or 11 | * (at your option) any later version. 12 | * 13 | * This program is distributed in the hope that it will be useful, 14 | * but WITHOUT ANY WARRANTY; without even the implied warranty of 15 | * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the 16 | * GNU General Public License for more details. 17 | * 18 | * You should have received a copy of the GNU General Public License 19 | * along with this program; if not, write to the Free Software 20 | * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02111-1301 USA 21 | * 22 | */ 23 | 24 | #ifndef PURPLE_JABBER_USERTUNE_H_ 25 | #define PURPLE_JABBER_USERTUNE_H_ 26 | 27 | #include "jabber.h" 28 | 29 | /* Implementation of XEP-0118 */ 30 | 31 | typedef struct _PurpleJabberTuneInfo PurpleJabberTuneInfo; 32 | struct _PurpleJabberTuneInfo { 33 | char *artist; 34 | char *title; 35 | char *album; 36 | char *track; /* either the index of the track in the album or the URL for a stream */ 37 | int time; /* in seconds, -1 for unknown */ 38 | char *url; 39 | }; 40 | 41 | void jabber_tune_init(void); 42 | 43 | void jabber_tune_set(PurpleConnection *gc, const PurpleJabberTuneInfo *tuneinfo); 44 | 45 | #endif /* PURPLE_JABBER_USERTUNE_H_ */ 46 | -------------------------------------------------------------------------------- /headers/jabber/xdata.h: -------------------------------------------------------------------------------- 1 | /** 2 | * @file xdata.h utility functions 3 | * 4 | * purple 5 | * 6 | * Purple is the legal property of its developers, whose names are too numerous 7 | * to list here. Please refer to the COPYRIGHT file distributed with this 8 | * source distribution. 9 | * 10 | * This program is free software; you can redistribute it and/or modify 11 | * it under the terms of the GNU General Public License as published by 12 | * the Free Software Foundation; either version 2 of the License, or 13 | * (at your option) any later version. 14 | * 15 | * This program is distributed in the hope that it will be useful, 16 | * but WITHOUT ANY WARRANTY; without even the implied warranty of 17 | * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the 18 | * GNU General Public License for more details. 19 | * 20 | * You should have received a copy of the GNU General Public License 21 | * along with this program; if not, write to the Free Software 22 | * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02111-1301 USA 23 | */ 24 | #ifndef PURPLE_JABBER_XDATA_H_ 25 | #define PURPLE_JABBER_XDATA_H_ 26 | 27 | #include "jabber.h" 28 | #include "xmlnode.h" 29 | 30 | typedef struct _JabberXDataAction { 31 | char *name; 32 | char *handle; 33 | } JabberXDataAction; 34 | 35 | typedef void (*jabber_x_data_cb)(JabberStream *js, xmlnode *result, gpointer user_data); 36 | typedef void (*jabber_x_data_action_cb)(JabberStream *js, xmlnode *result, const char *actionhandle, gpointer user_data); 37 | void *jabber_x_data_request(JabberStream *js, xmlnode *packet, jabber_x_data_cb cb, gpointer user_data); 38 | void *jabber_x_data_request_with_actions(JabberStream *js, xmlnode *packet, GList *actions, int defaultaction, jabber_x_data_action_cb cb, gpointer user_data); 39 | 40 | /* 41 | * Return the form type (the CDATA of the value child of the FORM_TYPE 42 | * field entry. 43 | * E.g., for the following, "http://jabber.org/protocol/muc#roominfo". 44 | * 45 | * 46 | * http://jabber.org/protocol/muc#roominfo 47 | * 48 | * 49 | * 50 | * @param form The xmlnode for the form (the 'x' element) 51 | * @returns The FORM_TYPE. Must be freed by caller. 52 | */ 53 | gchar *jabber_x_data_get_formtype(const xmlnode *form); 54 | 55 | #endif /* PURPLE_JABBER_XDATA_H_ */ 56 | -------------------------------------------------------------------------------- /src/lurch.h: -------------------------------------------------------------------------------- 1 | #ifndef __LURCH_H 2 | # define __LURCH_H 3 | 4 | # define LURCH_VERSION "0.7.0" 5 | # define LURCH_AUTHOR "Richard Bayerle " 6 | 7 | #endif /* __LURCH_H */ 8 | -------------------------------------------------------------------------------- /src/lurch_addr.c: -------------------------------------------------------------------------------- 1 | #include 2 | #include 3 | 4 | #include "axc.h" 5 | #include "libomemo.h" 6 | 7 | #include "lurch_addr.h" 8 | #include "lurch_api.h" 9 | #include "lurch_util.h" 10 | 11 | GList * lurch_addr_list_add(GList * addrs_p, const omemo_devicelist * dl_p, const uint32_t * exclude_id_p) { 12 | int ret_val = 0; 13 | 14 | GList * new_l_p = addrs_p; 15 | GList * dl_l_p = (void *) 0; 16 | GList * curr_p = (void *) 0; 17 | lurch_addr curr_addr = {0}; 18 | uint32_t curr_id = 0; 19 | lurch_addr * temp_addr_p = (void *) 0; 20 | 21 | curr_addr.jid = g_strdup(omemo_devicelist_get_owner(dl_p)); 22 | 23 | dl_l_p = omemo_devicelist_get_id_list(dl_p); 24 | 25 | for (curr_p = dl_l_p; curr_p; curr_p = curr_p->next) { 26 | curr_id = omemo_devicelist_list_data(curr_p); 27 | if (exclude_id_p && *exclude_id_p == curr_id) { 28 | continue; 29 | } 30 | 31 | curr_addr.device_id = curr_id; 32 | 33 | temp_addr_p = malloc(sizeof(lurch_addr)); 34 | if (!temp_addr_p) { 35 | ret_val = LURCH_ERR_NOMEM; 36 | goto cleanup; 37 | } 38 | 39 | memcpy(temp_addr_p, &curr_addr, sizeof(lurch_addr)); 40 | 41 | new_l_p = g_list_prepend(new_l_p, temp_addr_p); 42 | } 43 | 44 | cleanup: 45 | g_list_free_full(dl_l_p, free); 46 | 47 | if (ret_val) { 48 | lurch_addr_list_destroy(new_l_p); 49 | return (void *) 0; 50 | } else { 51 | return new_l_p; 52 | } 53 | } 54 | 55 | static void lurch_addr_list_destroy_func(gpointer data) { 56 | lurch_addr * addr_p = (lurch_addr *) data; 57 | free(addr_p->jid); 58 | free(addr_p); 59 | } 60 | 61 | void lurch_addr_list_destroy(GList * addr_l_p) { 62 | g_list_free_full(addr_l_p, lurch_addr_list_destroy_func); 63 | } 64 | 65 | int lurch_addr_sessions_exist(GList * addr_l_p, axc_context * axc_ctx_p, GList ** no_sess_l_pp){ 66 | int ret_val = 0; 67 | 68 | GList * no_sess_l_p = (void *) 0; 69 | 70 | GList * curr_p; 71 | lurch_addr * curr_addr_p; 72 | axc_address curr_axc_addr = {0}; 73 | for (curr_p = addr_l_p; curr_p; curr_p = curr_p->next) { 74 | curr_addr_p = (lurch_addr *) curr_p->data; 75 | 76 | curr_axc_addr.name = curr_addr_p->jid; 77 | curr_axc_addr.name_len = strnlen(curr_axc_addr.name, JABBER_MAX_LEN_BARE); 78 | curr_axc_addr.device_id = curr_addr_p->device_id; 79 | 80 | ret_val = axc_session_exists_initiated(&curr_axc_addr, axc_ctx_p); 81 | if (ret_val < 0) { 82 | purple_debug_error("lurch", "%s: %s (%i)\n", __func__, "failed to see if session exists", ret_val); 83 | goto cleanup; 84 | } else if (ret_val > 0) { 85 | ret_val = 0; 86 | continue; 87 | } else { 88 | no_sess_l_p = g_list_prepend(no_sess_l_p, curr_addr_p); 89 | ret_val = 0; 90 | } 91 | } 92 | 93 | *no_sess_l_pp = no_sess_l_p; 94 | 95 | cleanup: 96 | return ret_val; 97 | } 98 | -------------------------------------------------------------------------------- /src/lurch_addr.h: -------------------------------------------------------------------------------- 1 | #pragma once 2 | 3 | #include 4 | #include 5 | 6 | #include "axc.h" 7 | #include "libomemo.h" 8 | 9 | /** 10 | * Contains the information necessary to adress a device via OMEMO: the jabber ID and the OMEMO device ID. 11 | */ 12 | typedef struct lurch_addr { 13 | char * jid; 14 | uint32_t device_id; 15 | } lurch_addr; 16 | 17 | /** 18 | * Adds an each entry of an omemo devicelist to a GList of lurch_addrs. 19 | * 20 | * @param addrs_p Pointer to the list to add to. Remember NULL is a valid GList *. 21 | * @param dl_p Pointer to the omemo devicelist to add. 22 | * @param exclude_id_p Pointer to an ID that is not to be added. Useful when adding the own devicelist. Can be NULL. 23 | * @return Pointer to the updated GList on success, NULL on error. 24 | */ 25 | GList * lurch_addr_list_add(GList * addrs_p, const omemo_devicelist * dl_p, const uint32_t * exclude_id_p); 26 | 27 | /** 28 | * Frees all memory allocated by the list itself and each of its items. 29 | */ 30 | void lurch_addr_list_destroy(GList * addr_l_p); 31 | 32 | /** 33 | * For a list of lurch_addrs, checks which ones do not have an active session. 34 | * Note that the structs are not copied, the returned list is just a subset 35 | * of the pointers of the input list. 36 | * 37 | * @param addr_l_p A list of pointers to lurch_addr structs. 38 | * @param axc_ctx_p The axc_context to use. 39 | * @param no_sess_l_pp Will point to a list that contains pointers to those 40 | * addresses that do not have a session. 41 | * @return 0 on success, negative on error. 42 | */ 43 | int lurch_addr_sessions_exist(GList * addr_l_p, axc_context * axc_ctx_p, GList ** no_sess_l_pp); 44 | -------------------------------------------------------------------------------- /src/lurch_api.c: -------------------------------------------------------------------------------- 1 | #include 2 | #include 3 | #include 4 | 5 | #include "chat.h" 6 | #include "pep.h" 7 | 8 | #include "axc.h" 9 | #include "libomemo.h" 10 | #include "libomemo_storage.h" 11 | 12 | #include "lurch_api.h" 13 | #include "lurch_api_internal.h" 14 | #include "lurch_util.h" 15 | 16 | #define MODULE_NAME "lurch-api" 17 | 18 | #define DISCO_XMLNS "http://jabber.org/protocol/disco#info" // see XEP-0030: Service Discovery (https://xmpp.org/extensions/xep-0030.html) 19 | 20 | /** 21 | * Returns a GList of int32_t * containing the devices of the calling account. 22 | * If the current device is contained in it (which it should be!), it will be first in the list. 23 | */ 24 | static int32_t lurch_api_id_list_get_own(PurpleAccount * acc_p, GList ** list_pp) { 25 | int32_t ret_val = 0; 26 | char * uname = (void *) 0; 27 | char * db_fn_omemo = (void *) 0; 28 | omemo_devicelist * dl_p = (void *) 0; 29 | axc_context * axc_ctx_p = (void *) 0; 30 | uint32_t own_id = 0; 31 | GList * id_list = (void *) 0; 32 | uint32_t * id_p = (void *) 0; 33 | 34 | uname = lurch_util_uname_strip(purple_account_get_username(acc_p)); 35 | db_fn_omemo = lurch_util_uname_get_db_fn(uname, LURCH_DB_NAME_OMEMO); 36 | 37 | ret_val = omemo_storage_user_devicelist_retrieve(uname, db_fn_omemo, &dl_p); 38 | if (ret_val) { 39 | purple_debug_error(MODULE_NAME, "Failed to access OMEMO DB %s.", db_fn_omemo); 40 | goto cleanup; 41 | } 42 | 43 | ret_val = lurch_util_axc_get_init_ctx(uname, &axc_ctx_p); 44 | if (ret_val) { 45 | purple_debug_error(MODULE_NAME, "Failed to create axc ctx."); 46 | goto cleanup; 47 | } 48 | 49 | ret_val = axc_get_device_id(axc_ctx_p, &own_id); 50 | if (ret_val) { 51 | purple_debug_error(MODULE_NAME, "Failed to access axc db %s. Does the path seem correct?", axc_context_get_db_fn(axc_ctx_p)); 52 | goto cleanup; 53 | } 54 | 55 | if (!omemo_devicelist_contains_id(dl_p, own_id)) { 56 | purple_debug_warning(MODULE_NAME, "This device's ID is not contained in your devicelist?"); 57 | goto cleanup; 58 | } 59 | 60 | ret_val = omemo_devicelist_remove(dl_p, own_id); 61 | if (ret_val) { 62 | purple_debug_error(MODULE_NAME, "Failed to remove the ID from the devicelist."); 63 | goto cleanup; 64 | } 65 | 66 | id_list = omemo_devicelist_get_id_list(dl_p); 67 | 68 | id_p = g_malloc(sizeof(uint32_t)); 69 | if (!id_p) { 70 | ret_val = LURCH_ERR_NOMEM; 71 | goto cleanup; 72 | } 73 | *id_p = own_id; 74 | 75 | id_list = g_list_prepend(id_list, id_p); 76 | 77 | cleanup: 78 | if (!ret_val) { 79 | *list_pp = id_list; 80 | } 81 | 82 | g_free(uname); 83 | g_free(db_fn_omemo); 84 | omemo_devicelist_destroy(dl_p); 85 | axc_context_destroy_all(axc_ctx_p); 86 | 87 | return ret_val; 88 | } 89 | 90 | void lurch_api_id_list_handler(PurpleAccount * acc_p, void (*cb)(int32_t err, GList * id_list, void * user_data_p), void * user_data_p) { 91 | int32_t ret_val = 0; 92 | GList * id_list = (void *) 0; 93 | 94 | ret_val = lurch_api_id_list_get_own(acc_p, &id_list); 95 | if (ret_val) { 96 | purple_debug_error(MODULE_NAME, "Failed to get the own, sorted ID list."); 97 | goto cleanup; 98 | } 99 | 100 | cleanup: 101 | cb(ret_val, id_list, user_data_p); 102 | 103 | g_list_free_full(id_list, free); 104 | } 105 | 106 | void lurch_api_id_remove_handler(PurpleAccount * acc_p, uint32_t device_id, void (*cb)(int32_t err, void * user_data_p), void * user_data_p) { 107 | int32_t ret_val = 0; 108 | char * uname = (void *) 0; 109 | char * db_fn_omemo = (void *) 0; 110 | omemo_devicelist * dl_p = (void *) 0; 111 | char * exported_devicelist = (void *) 0; 112 | xmlnode * publish_node_p = (void *) 0; 113 | 114 | uname = lurch_util_uname_strip(purple_account_get_username(acc_p)); 115 | db_fn_omemo = lurch_util_uname_get_db_fn(uname, LURCH_DB_NAME_OMEMO); 116 | 117 | ret_val = omemo_storage_user_devicelist_retrieve(uname, db_fn_omemo, &dl_p); 118 | if (ret_val) { 119 | purple_debug_error(MODULE_NAME, "Failed to access the OMEMO DB %s to retrieve the devicelist.", db_fn_omemo); 120 | goto cleanup; 121 | } 122 | 123 | if (!omemo_devicelist_contains_id(dl_p, device_id)) { 124 | ret_val = LURCH_ERR_DEVICE_NOT_IN_LIST; 125 | purple_debug_error(MODULE_NAME, "Your devicelist does not contain the device ID %i.", device_id); 126 | goto cleanup; 127 | } 128 | 129 | ret_val = omemo_devicelist_remove(dl_p, device_id); 130 | if (ret_val) { 131 | purple_debug_error(MODULE_NAME, "Failed to remove the device ID %i from %s's devicelist.", device_id, uname); 132 | goto cleanup; 133 | } 134 | 135 | ret_val = omemo_devicelist_export(dl_p, &exported_devicelist); 136 | if (ret_val) { 137 | purple_debug_error(MODULE_NAME, "Failed to export new devicelist without device ID %i.", device_id); 138 | goto cleanup; 139 | } 140 | 141 | publish_node_p = xmlnode_from_str(exported_devicelist, -1); 142 | jabber_pep_publish(purple_connection_get_protocol_data(purple_account_get_connection(acc_p)), publish_node_p); 143 | // publish_node_p will be freed by the jabber prpl 144 | 145 | cleanup: 146 | cb(ret_val, user_data_p); 147 | 148 | g_free(uname); 149 | g_free(db_fn_omemo); 150 | omemo_devicelist_destroy(dl_p); 151 | g_free(exported_devicelist); 152 | } 153 | 154 | void lurch_api_enable_im_handler(PurpleAccount * acc_p, const char * contact_bare_jid, void (*cb)(int32_t err, void * user_data_p), void * user_data_p) { 155 | int32_t ret_val = 0; 156 | char * uname = (void *) 0; 157 | char * db_fn_omemo = (void *) 0; 158 | 159 | uname = lurch_util_uname_strip(purple_account_get_username(acc_p)); 160 | db_fn_omemo = lurch_util_uname_get_db_fn(uname, LURCH_DB_NAME_OMEMO); 161 | 162 | ret_val = omemo_storage_chatlist_delete(contact_bare_jid, db_fn_omemo); 163 | if (ret_val) { 164 | purple_debug_error(MODULE_NAME, "Failed to delete %s from the blacklist in OMEMO DB %s.", contact_bare_jid, db_fn_omemo); 165 | } 166 | 167 | cb(ret_val, user_data_p); 168 | 169 | g_free(uname); 170 | g_free(db_fn_omemo); 171 | } 172 | 173 | void lurch_api_disable_im_handler(PurpleAccount * acc_p, const char * contact_bare_jid, void (*cb)(int32_t err, void * user_data_p), void * user_data_p) { 174 | int32_t ret_val = 0; 175 | char * uname = (void *) 0; 176 | char * db_fn_omemo = (void *) 0; 177 | 178 | uname = lurch_util_uname_strip(purple_account_get_username(acc_p)); 179 | db_fn_omemo = lurch_util_uname_get_db_fn(uname, LURCH_DB_NAME_OMEMO); 180 | 181 | ret_val = omemo_storage_chatlist_save(contact_bare_jid, db_fn_omemo); 182 | if (ret_val) { 183 | purple_debug_error(MODULE_NAME, "Failed to add %s to the blacklist in OMEMO DB %s.", contact_bare_jid, db_fn_omemo); 184 | } 185 | 186 | cb(ret_val, user_data_p); 187 | 188 | g_free(uname); 189 | g_free(db_fn_omemo); 190 | } 191 | 192 | void lurch_api_enable_chat_handler(PurpleAccount * acc_p, const char * full_conversation_name, void (*cb)(int32_t err, void * user_data_p), void * user_data_p) { 193 | int32_t ret_val = 0; 194 | char * uname = (void *) 0; 195 | char * db_fn_omemo = (void *) 0; 196 | 197 | uname = lurch_util_uname_strip(purple_account_get_username(acc_p)); 198 | db_fn_omemo = lurch_util_uname_get_db_fn(uname, LURCH_DB_NAME_OMEMO); 199 | 200 | ret_val = omemo_storage_chatlist_save(full_conversation_name, db_fn_omemo); 201 | if (ret_val) { 202 | purple_debug_error(MODULE_NAME, "Failed to enable OMEMO for chat %s using DB %s.\n", full_conversation_name, db_fn_omemo); 203 | } 204 | 205 | cb(ret_val, user_data_p); 206 | 207 | g_free(uname); 208 | g_free(db_fn_omemo); 209 | } 210 | 211 | void lurch_api_disable_chat_handler(PurpleAccount * acc_p, const char * full_conversation_name, void (*cb)(int32_t err, void * user_data_p), void * user_data_p) { 212 | int32_t ret_val = 0; 213 | char * uname = (void *) 0; 214 | char * db_fn_omemo = (void *) 0; 215 | 216 | uname = lurch_util_uname_strip(purple_account_get_username(acc_p)); 217 | db_fn_omemo = lurch_util_uname_get_db_fn(uname, LURCH_DB_NAME_OMEMO); 218 | 219 | ret_val = omemo_storage_chatlist_delete(full_conversation_name, db_fn_omemo); 220 | if (ret_val) { 221 | purple_debug_error(MODULE_NAME, "Failed to disable OMEMO for chat %s using DB %s.\n", full_conversation_name, db_fn_omemo); 222 | } 223 | 224 | cb(ret_val, user_data_p); 225 | 226 | g_free(uname); 227 | g_free(db_fn_omemo); 228 | } 229 | 230 | void lurch_api_fp_get_handler(PurpleAccount * acc_p, void (*cb)(int32_t err, const char * fp_printable, void * user_data_p), void * user_data_p) { 231 | int32_t ret_val = 0; 232 | char * uname = (void *) 0; 233 | axc_context * axc_ctx_p = (void *) 0; 234 | axc_buf * key_buf_p = (void *) 0; 235 | char * fp_printable = (void *) 0; 236 | 237 | uname = lurch_util_uname_strip(purple_account_get_username(acc_p)); 238 | 239 | ret_val = lurch_util_axc_get_init_ctx(uname, &axc_ctx_p); 240 | if (ret_val) { 241 | purple_debug_error(MODULE_NAME, "Failed to create axc ctx.\n"); 242 | goto cleanup; 243 | } 244 | 245 | ret_val = axc_key_load_public_own(axc_ctx_p, &key_buf_p); 246 | if (ret_val) { 247 | purple_debug_error(MODULE_NAME, "Failed to load public key from axc db %s.", axc_context_get_db_fn(axc_ctx_p)); 248 | goto cleanup; 249 | } 250 | 251 | fp_printable = lurch_util_fp_get_printable(key_buf_p); 252 | 253 | cleanup: 254 | cb(ret_val, fp_printable, user_data_p); 255 | 256 | g_free(fp_printable); 257 | axc_buf_free(key_buf_p); 258 | axc_context_destroy_all(axc_ctx_p); 259 | } 260 | 261 | /** 262 | * Given a list of IDs, retrieves the public keys from the libsignal sessions and creates a hash table with ID to fingerprint pairs. 263 | * If there is an entry in the devicelist, but no session yet, the fingerprint cannot be retrieved this way and the value will be NULL. 264 | * g_hash_table_destroy() the table when done with it. 265 | */ 266 | static int32_t lurch_api_fp_create_table(const char * jid, axc_context * axc_ctx_p, const GList * id_list, GHashTable ** id_fp_table_pp) { 267 | int32_t ret_val = 0; 268 | GHashTable * id_fp_table = (void *) 0; 269 | const GList * curr_p = (void *) 0; 270 | uint32_t curr_device_id = 0; 271 | axc_buf * key_buf_p = (void *) 0; 272 | 273 | id_fp_table = g_hash_table_new_full(g_int_hash, g_int_equal, NULL, g_free); 274 | 275 | for (curr_p = id_list; curr_p; curr_p = curr_p->next) { 276 | curr_device_id = omemo_devicelist_list_data(curr_p); 277 | 278 | ret_val = axc_key_load_public_addr(jid, curr_device_id, axc_ctx_p, &key_buf_p); 279 | if (ret_val < 0) { 280 | purple_debug_error(MODULE_NAME, "Failed to load key for %s:%i", jid, curr_device_id); 281 | goto cleanup; 282 | } else if (ret_val == 0) { 283 | purple_debug_warning(MODULE_NAME, "Tried to load public key for %s:%i, but no session exists", jid, curr_device_id); 284 | (void) g_hash_table_insert(id_fp_table, curr_p->data, NULL); 285 | continue; 286 | } 287 | 288 | (void) g_hash_table_insert(id_fp_table, curr_p->data, lurch_util_fp_get_printable(key_buf_p)); 289 | 290 | axc_buf_free(key_buf_p); 291 | key_buf_p = (void *) 0; 292 | 293 | ret_val = 0; 294 | } 295 | 296 | cleanup: 297 | if (ret_val) { 298 | g_hash_table_destroy(id_fp_table); 299 | } else { 300 | *id_fp_table_pp = id_fp_table; 301 | } 302 | 303 | return ret_val; 304 | } 305 | 306 | // returns NULL as hash table if devicelist is empty 307 | void lurch_api_fp_list_handler(PurpleAccount * acc_p, void (*cb)(int32_t err, GHashTable * id_fp_table, void * user_data_p), void * user_data_p) { 308 | int32_t ret_val = 0; 309 | GList * own_id_list = (void *) 0; 310 | char * uname = (void *) 0; 311 | axc_context * axc_ctx_p = (void *) 0; 312 | GHashTable * id_fp_table = (void *) 0; 313 | axc_buf * key_buf_p = (void *) 0; 314 | 315 | ret_val = lurch_api_id_list_get_own(acc_p, &own_id_list); 316 | if (ret_val) { 317 | purple_debug_error(MODULE_NAME, "Failed to get the own, sorted ID list."); 318 | goto cleanup; 319 | } 320 | 321 | if (g_list_length(own_id_list) == 0) { 322 | goto cleanup; 323 | } 324 | 325 | uname = lurch_util_uname_strip(purple_account_get_username(acc_p)); 326 | ret_val = lurch_util_axc_get_init_ctx(uname, &axc_ctx_p); 327 | if (ret_val) { 328 | purple_debug_error(MODULE_NAME, "Failed to create axc ctx for %s.", uname); 329 | goto cleanup; 330 | } 331 | 332 | ret_val = lurch_api_fp_create_table(uname, axc_ctx_p, own_id_list->next, &id_fp_table); 333 | if (ret_val) { 334 | goto cleanup; 335 | } 336 | 337 | ret_val = axc_key_load_public_own(axc_ctx_p, &key_buf_p); 338 | if (ret_val) { 339 | purple_debug_error(MODULE_NAME, "Failed to load public key from axc db %s.", axc_context_get_db_fn(axc_ctx_p)); 340 | goto cleanup; 341 | } 342 | 343 | (void) g_hash_table_insert(id_fp_table, own_id_list->data, lurch_util_fp_get_printable(key_buf_p)); 344 | 345 | cleanup: 346 | cb(ret_val, id_fp_table, user_data_p); 347 | 348 | g_list_free_full(own_id_list, g_free); 349 | g_free(uname); 350 | axc_context_destroy_all(axc_ctx_p); 351 | g_hash_table_destroy(id_fp_table); 352 | axc_buf_free(key_buf_p); 353 | } 354 | 355 | // returns NULL as hash table if devicelist is empty 356 | void lurch_api_fp_other_handler(PurpleAccount * acc_p, const char * contact_bare_jid, void (*cb)(int32_t err, GHashTable * id_fp_table, void * user_data_p), void * user_data_p) { 357 | int32_t ret_val = 0; 358 | char * uname = (void *) 0; 359 | char * db_fn_omemo = (void *) 0; 360 | omemo_devicelist * dl_p = (void *) 0; 361 | axc_context * axc_ctx_p = (void *) 0; 362 | GHashTable * id_fp_table = (void *) 0; 363 | GList * id_list = (void *) 0; 364 | axc_buf * key_buf_p = (void *) 0; 365 | 366 | uname = lurch_util_uname_strip(purple_account_get_username(acc_p)); 367 | db_fn_omemo = lurch_util_uname_get_db_fn(uname, LURCH_DB_NAME_OMEMO); 368 | 369 | ret_val = omemo_storage_user_devicelist_retrieve(contact_bare_jid, db_fn_omemo, &dl_p); 370 | if (ret_val) { 371 | purple_debug_error(MODULE_NAME, "Failed to access OMEMO DB %s.", db_fn_omemo); 372 | goto cleanup; 373 | } 374 | 375 | if (omemo_devicelist_is_empty(dl_p)) { 376 | goto cleanup; 377 | } 378 | 379 | ret_val = lurch_util_axc_get_init_ctx(uname, &axc_ctx_p); 380 | if (ret_val) { 381 | purple_debug_error(MODULE_NAME, "Failed to create axc ctx for %s.", uname); 382 | goto cleanup; 383 | } 384 | 385 | id_list = omemo_devicelist_get_id_list(dl_p); 386 | 387 | ret_val = lurch_api_fp_create_table(contact_bare_jid, axc_ctx_p, id_list, &id_fp_table); 388 | 389 | cleanup: 390 | cb(ret_val, id_fp_table, user_data_p); 391 | 392 | g_free(uname); 393 | g_free(db_fn_omemo); 394 | omemo_devicelist_destroy(dl_p); 395 | axc_context_destroy_all(axc_ctx_p); 396 | g_list_free_full(id_list, free); 397 | axc_buf_free(key_buf_p); 398 | 399 | if (id_fp_table) { 400 | g_hash_table_destroy(id_fp_table); 401 | } 402 | } 403 | 404 | void lurch_api_status_im_handler(PurpleAccount * acc_p, const char * contact_bare_jid, void (*cb)(int32_t err, lurch_status_t status, void * user_data_p), void * user_data_p) { 405 | int32_t ret_val = 0; 406 | lurch_status_t status = LURCH_STATUS_DISABLED; 407 | 408 | char * uname = (void *) 0; 409 | char * db_fn_omemo = (void *) 0; 410 | omemo_devicelist * dl_p = (void *) 0; 411 | axc_context * axc_ctx_p = (void *) 0; 412 | 413 | uname = lurch_util_uname_strip(purple_account_get_username(acc_p)); 414 | db_fn_omemo = lurch_util_uname_get_db_fn(uname, LURCH_DB_NAME_OMEMO); 415 | 416 | ret_val = omemo_storage_chatlist_exists(contact_bare_jid, db_fn_omemo); 417 | if (ret_val < 0 || ret_val > 1) { 418 | purple_debug_error(MODULE_NAME, "Failed to look up %s in file %s.", contact_bare_jid, db_fn_omemo); 419 | goto cleanup; 420 | } else if (ret_val == 0) { 421 | // conversation is not on blacklist, continue 422 | } else if (ret_val == 1) { 423 | ret_val = 0; 424 | status = LURCH_STATUS_DISABLED; 425 | goto cleanup; 426 | } 427 | 428 | ret_val = omemo_storage_user_devicelist_retrieve(contact_bare_jid, db_fn_omemo, &dl_p); 429 | if (ret_val) { 430 | purple_debug_error(MODULE_NAME, "Failed to get the devicelist for %s from %s.", contact_bare_jid, db_fn_omemo); 431 | goto cleanup; 432 | } 433 | 434 | if (omemo_devicelist_is_empty(dl_p)) { 435 | ret_val = 0; 436 | status = LURCH_STATUS_NOT_SUPPORTED; 437 | goto cleanup; 438 | } 439 | 440 | ret_val = lurch_util_axc_get_init_ctx(uname, &axc_ctx_p); 441 | if (ret_val) { 442 | purple_debug_error(MODULE_NAME, "Failed to create axc ctx for %s.", uname); 443 | goto cleanup; 444 | } 445 | 446 | ret_val = axc_session_exists_any(contact_bare_jid, axc_ctx_p); 447 | if (ret_val < 0) { 448 | purple_debug_error(MODULE_NAME, "Failed to look up session with %s.", contact_bare_jid); 449 | goto cleanup; 450 | } else if (ret_val == 0) { 451 | ret_val = 0; 452 | status = LURCH_STATUS_NO_SESSION; 453 | } else { 454 | ret_val = 0; 455 | status = LURCH_STATUS_OK; 456 | } 457 | 458 | cleanup: 459 | cb(ret_val, status, user_data_p); 460 | 461 | g_free(uname); 462 | g_free(db_fn_omemo); 463 | omemo_devicelist_destroy(dl_p); 464 | axc_context_destroy_all(axc_ctx_p); 465 | } 466 | 467 | /** 468 | * Callback for the discovery request. 469 | * data_p needs to be a pointer to lurch_api_status_chat_cb_data, which will be freed. 470 | */ 471 | void lurch_api_status_chat_discover_cb(JabberStream * js_p, const char * from, JabberIqType type, const char * id, xmlnode * packet_p, gpointer data_p) { 472 | int32_t ret_val = 0; 473 | lurch_status_chat_t status = LURCH_STATUS_CHAT_DISABLED; 474 | 475 | xmlnode * query_node_p = (void *) 0; 476 | xmlnode * child_node_p = (void *) 0; 477 | PurpleConversation * conv_p = (void *) 0; 478 | JabberChat * muc_p = (void *) 0; 479 | const char * feature_name = (void *) 0; 480 | GList * curr_item_p = (void *) 0; 481 | JabberChatMember * curr_muc_member_p = (void *) 0; 482 | char * curr_muc_member_bare_jid = (void *) 0; 483 | omemo_devicelist * curr_dl_p = (void *) 0; 484 | 485 | lurch_api_status_chat_cb_data * cb_data_p = (lurch_api_status_chat_cb_data *) data_p; 486 | gboolean is_anonymous = TRUE; 487 | 488 | if (type == JABBER_IQ_ERROR) { 489 | purple_debug_error(MODULE_NAME, "MUC feature discovery request for %s returned an error.\n", from ); 490 | ret_val = EXIT_FAILURE; 491 | goto cleanup; 492 | } 493 | 494 | query_node_p = xmlnode_get_child_with_namespace(packet_p, "query", DISCO_XMLNS); 495 | if (!query_node_p) { 496 | purple_debug_error(MODULE_NAME, "no 'query' node in feature discovery reply for %s\n", from); 497 | ret_val = EXIT_FAILURE; 498 | goto cleanup; 499 | } 500 | 501 | for (child_node_p = query_node_p->child; child_node_p; child_node_p = child_node_p->next) { 502 | if (g_strcmp0(child_node_p->name, "feature")) { 503 | continue; 504 | } 505 | 506 | feature_name = xmlnode_get_attrib(child_node_p, "var"); 507 | if (!g_strcmp0("muc_nonanonymous", feature_name)) { 508 | is_anonymous = FALSE; 509 | } else if (!g_strcmp0("muc_open", feature_name)) { 510 | purple_debug_warning(MODULE_NAME, "muc %s is open, this is likely to cause problems for OMEMO\n", from); 511 | } 512 | } 513 | 514 | if (is_anonymous) { 515 | status = LURCH_STATUS_CHAT_ANONYMOUS; 516 | goto cleanup; 517 | } 518 | 519 | conv_p = purple_find_conversation_with_account(PURPLE_CONV_TYPE_CHAT, from, js_p->gc->account); 520 | if (!conv_p) { 521 | purple_debug_error(MODULE_NAME, "could not find groupchat %s\n", from); 522 | ret_val = EXIT_FAILURE; 523 | goto cleanup; 524 | } 525 | 526 | muc_p = jabber_chat_find_by_conv(conv_p); 527 | if (!muc_p) { 528 | purple_debug_error(MODULE_NAME, "Could not find the data for groupchat %s.\n", from); 529 | ret_val = EXIT_FAILURE; 530 | goto cleanup; 531 | } 532 | 533 | for (curr_item_p = g_hash_table_get_values(muc_p->members); curr_item_p; curr_item_p = curr_item_p->next) { 534 | curr_muc_member_p = (JabberChatMember *) curr_item_p->data; 535 | curr_muc_member_bare_jid = jabber_get_bare_jid(curr_muc_member_p->jid); 536 | 537 | if (!curr_muc_member_bare_jid) { 538 | // getting here means that the MUC is not anonymous, but the members' JIDs are not available 539 | // this can happen when the room is reconfigured to be anonymous while the client is in it 540 | purple_debug_warning(MODULE_NAME, "Could not access %s's JID even though the room is public. Rejoining will probably fix this.\n", curr_muc_member_p->handle); 541 | status = LURCH_STATUS_CHAT_NO_JIDS; 542 | goto cleanup; 543 | } 544 | 545 | ret_val = omemo_storage_user_devicelist_retrieve(curr_muc_member_bare_jid, cb_data_p->db_fn_omemo, &curr_dl_p); 546 | if (ret_val) { 547 | purple_debug_error(MODULE_NAME, "Could not retrieve the devicelist for %s (JID: %s) from %s.\n", curr_muc_member_p->handle, curr_muc_member_bare_jid, cb_data_p->db_fn_omemo); 548 | goto cleanup; 549 | } 550 | 551 | if (omemo_devicelist_is_empty(curr_dl_p)) { 552 | purple_debug_warning( 553 | MODULE_NAME, 554 | "Could not find chat %s member %s's devicelist in OMEMO DB %s. This probably means the user is not in this account's contact list.", 555 | from, curr_muc_member_bare_jid, cb_data_p->db_fn_omemo 556 | ); 557 | 558 | status = LURCH_STATUS_CHAT_NO_DEVICELIST; 559 | goto cleanup; 560 | } 561 | 562 | g_free(curr_muc_member_bare_jid); 563 | curr_muc_member_bare_jid = (void *) 0; 564 | 565 | omemo_devicelist_destroy(curr_dl_p); 566 | curr_dl_p = (void *) 0; 567 | } 568 | 569 | status = LURCH_STATUS_CHAT_OK; 570 | 571 | cleanup: 572 | cb_data_p->cb(ret_val, status, cb_data_p->user_data_p); 573 | 574 | g_free(cb_data_p->db_fn_omemo); 575 | g_free(cb_data_p); 576 | 577 | // if loop was exited early, this needs to be cleaned up here 578 | g_free(curr_muc_member_bare_jid); 579 | omemo_devicelist_destroy(curr_dl_p); 580 | } 581 | 582 | // Send a discovery request to a MUC to learn its properties, e.g. for determining whether it is set to anonymous. 583 | // See https://xmpp.org/extensions/xep-0045.html#disco-roominfo 584 | static void lurch_api_status_chat_discover(PurpleAccount * acc_p, const char * full_conversation_name, lurch_api_status_chat_cb_data * data_p) { 585 | JabberIq * jiq_p = (void *) 0; 586 | xmlnode * query_node_p = (void *) 0; 587 | 588 | JabberStream * js_p = purple_connection_get_protocol_data(purple_account_get_connection(acc_p)); 589 | 590 | jiq_p = jabber_iq_new(js_p, JABBER_IQ_GET); 591 | xmlnode_set_attrib(jiq_p->node, "to", full_conversation_name); 592 | query_node_p = xmlnode_new_child(jiq_p->node, "query"); 593 | xmlnode_set_namespace(query_node_p, DISCO_XMLNS); 594 | 595 | jabber_iq_set_callback(jiq_p, lurch_api_status_chat_discover_cb, data_p); 596 | jabber_iq_send(jiq_p); // this also frees the memory 597 | 598 | purple_debug_info(MODULE_NAME, "sent feature discovery request to MUC %s\n", full_conversation_name); 599 | } 600 | 601 | void lurch_api_status_chat_handler(PurpleAccount * acc_p, const char * full_conversation_name, void (*cb)(int32_t err, lurch_status_chat_t status, void * user_data_p), void * user_data_p) { 602 | int32_t ret_val = 0; 603 | lurch_status_chat_t status = LURCH_STATUS_CHAT_DISABLED; 604 | gboolean early_exit = FALSE; // call the provided callback directly in this function instead of in the iq query callback 605 | 606 | char * uname = (void *) 0; 607 | char * db_fn_omemo = (void *) 0; 608 | 609 | uname = lurch_util_uname_strip(purple_account_get_username(acc_p)); 610 | db_fn_omemo = lurch_util_uname_get_db_fn(uname, LURCH_DB_NAME_OMEMO); 611 | 612 | ret_val = omemo_storage_chatlist_exists(full_conversation_name, db_fn_omemo); 613 | if (ret_val < 0 || ret_val > 1) { 614 | purple_debug_error(MODULE_NAME, "Failed to look up %s in file %s.", full_conversation_name, db_fn_omemo); 615 | early_exit = TRUE; 616 | goto cleanup; 617 | } else if (ret_val == 0) { 618 | ret_val = 0; 619 | status = LURCH_STATUS_CHAT_DISABLED; 620 | early_exit = TRUE; 621 | goto cleanup; 622 | } else if (ret_val == 1) { 623 | // omemo enabled for chat, continue 624 | ret_val = 0; 625 | } 626 | 627 | lurch_api_status_chat_cb_data * cb_data_p = (void *) 0; 628 | cb_data_p = g_malloc0(sizeof(lurch_api_status_chat_cb_data)); 629 | if (!cb_data_p) { 630 | ret_val = LURCH_ERR_NOMEM; 631 | early_exit = TRUE; 632 | goto cleanup; 633 | } 634 | cb_data_p->db_fn_omemo = db_fn_omemo; 635 | cb_data_p->cb = cb; 636 | cb_data_p->user_data_p = user_data_p; 637 | 638 | lurch_api_status_chat_discover(acc_p, full_conversation_name, cb_data_p); 639 | 640 | cleanup: 641 | g_free(uname); 642 | 643 | if (early_exit) { 644 | // in other cases, db_fn_omemo will be freed when the cb data is destroyed 645 | g_free(db_fn_omemo); 646 | 647 | // in the regular case, the callback is passed on and called later 648 | cb(ret_val, status, user_data_p); 649 | } 650 | } 651 | 652 | static void lurch_api_marshal_VOID__POINTER_INT_POINTER_POINTER(PurpleCallback cb, va_list args, void * data, void ** return_val) { 653 | void * arg1 = va_arg(args, void *); 654 | gint32 arg2 = va_arg(args, guint); 655 | void * arg3 = va_arg(args, void *); 656 | void * arg4 = va_arg(args, void *); 657 | 658 | ((void (*)(void *, guint, void *, void *, void *))cb)(arg1, arg2, arg3, arg4, data); 659 | } 660 | 661 | typedef enum { 662 | LURCH_API_HANDLER_ACC_CB_DATA = 0, 663 | LURCH_API_HANDLER_ACC_JID_CB_DATA, 664 | LURCH_API_HANDLER_ACC_DID_CB_DATA 665 | } lurch_api_handler_t; 666 | 667 | typedef struct { 668 | const char * name; 669 | void * handler; 670 | lurch_api_handler_t handler_type; 671 | } lurch_signal_info; 672 | 673 | const lurch_signal_info signal_infos[] = { 674 | { 675 | .name = "lurch-id-list", 676 | .handler = lurch_api_id_list_handler, 677 | .handler_type = LURCH_API_HANDLER_ACC_CB_DATA, 678 | }, 679 | { 680 | .name = "lurch-id-remove", 681 | .handler = lurch_api_id_remove_handler, 682 | .handler_type = LURCH_API_HANDLER_ACC_DID_CB_DATA, 683 | }, 684 | { 685 | .name = "lurch-enable-im", 686 | .handler = lurch_api_enable_im_handler, 687 | .handler_type = LURCH_API_HANDLER_ACC_JID_CB_DATA, 688 | }, 689 | { 690 | .name = "lurch-disable-im", 691 | .handler = lurch_api_disable_im_handler, 692 | .handler_type = LURCH_API_HANDLER_ACC_JID_CB_DATA, 693 | }, 694 | { 695 | .name = "lurch-enable-chat", 696 | .handler = lurch_api_enable_chat_handler, 697 | .handler_type = LURCH_API_HANDLER_ACC_JID_CB_DATA, 698 | }, 699 | { 700 | .name = "lurch-disable-chat", 701 | .handler = lurch_api_disable_chat_handler, 702 | .handler_type = LURCH_API_HANDLER_ACC_JID_CB_DATA, 703 | }, 704 | { 705 | .name = "lurch-fp-get", 706 | .handler = lurch_api_fp_get_handler, 707 | .handler_type = LURCH_API_HANDLER_ACC_CB_DATA, 708 | }, 709 | { 710 | .name = "lurch-fp-list", 711 | .handler = lurch_api_fp_list_handler, 712 | .handler_type = LURCH_API_HANDLER_ACC_CB_DATA, 713 | }, 714 | { 715 | .name = "lurch-fp-other", 716 | .handler = lurch_api_fp_other_handler, 717 | .handler_type = LURCH_API_HANDLER_ACC_JID_CB_DATA, 718 | }, 719 | { 720 | .name = "lurch-status-im", 721 | .handler = lurch_api_status_im_handler, 722 | .handler_type = LURCH_API_HANDLER_ACC_JID_CB_DATA, 723 | }, 724 | { 725 | .name = "lurch-status-chat", 726 | .handler = lurch_api_status_chat_handler, 727 | .handler_type = LURCH_API_HANDLER_ACC_JID_CB_DATA, 728 | }, 729 | }; 730 | 731 | static int get_num_of_signals() { 732 | return (sizeof signal_infos) / (sizeof signal_infos[0]); 733 | } 734 | 735 | void lurch_api_init() { 736 | void * plugins_handle_p = purple_plugins_get_handle(); 737 | 738 | for (int i = 0; i < get_num_of_signals(); i++) { 739 | lurch_signal_info signal_info = signal_infos[i]; 740 | const char * signal_name = signal_info.name; 741 | 742 | switch (signal_info.handler_type) { 743 | case LURCH_API_HANDLER_ACC_CB_DATA: 744 | purple_signal_register( 745 | plugins_handle_p, 746 | signal_name, 747 | purple_marshal_VOID__POINTER_POINTER_POINTER, 748 | NULL, 749 | 3, 750 | purple_value_new(PURPLE_TYPE_SUBTYPE, PURPLE_SUBTYPE_ACCOUNT), 751 | purple_value_new(PURPLE_TYPE_POINTER), 752 | purple_value_new(PURPLE_TYPE_POINTER) 753 | ); 754 | break; 755 | case LURCH_API_HANDLER_ACC_JID_CB_DATA: 756 | purple_signal_register( 757 | plugins_handle_p, 758 | signal_name, 759 | purple_marshal_VOID__POINTER_POINTER_POINTER_POINTER, 760 | NULL, 761 | 4, 762 | purple_value_new(PURPLE_TYPE_SUBTYPE, PURPLE_SUBTYPE_ACCOUNT), 763 | purple_value_new(PURPLE_TYPE_STRING), 764 | purple_value_new(PURPLE_TYPE_POINTER), 765 | purple_value_new(PURPLE_TYPE_POINTER) 766 | ); 767 | break; 768 | case LURCH_API_HANDLER_ACC_DID_CB_DATA: 769 | purple_signal_register( 770 | plugins_handle_p, 771 | signal_name, 772 | lurch_api_marshal_VOID__POINTER_INT_POINTER_POINTER, 773 | NULL, 774 | 4, 775 | purple_value_new(PURPLE_TYPE_SUBTYPE, PURPLE_SUBTYPE_ACCOUNT), 776 | purple_value_new(PURPLE_TYPE_INT), 777 | purple_value_new(PURPLE_TYPE_POINTER), 778 | purple_value_new(PURPLE_TYPE_POINTER) 779 | ); 780 | break; 781 | default: 782 | purple_debug_fatal(MODULE_NAME, "Unknown handler function type, aborting initialization."); 783 | } 784 | 785 | purple_signal_connect( 786 | plugins_handle_p, 787 | signal_name, 788 | MODULE_NAME, 789 | PURPLE_CALLBACK(signal_info.handler), 790 | NULL 791 | ); 792 | } 793 | } 794 | 795 | void lurch_api_unload() { 796 | void * plugins_handle_p = purple_plugins_get_handle(); 797 | 798 | for (int i = 0; i < get_num_of_signals(); i++) { 799 | lurch_signal_info signal_info = signal_infos[i]; 800 | const char * signal_name = signal_info.name; 801 | 802 | purple_signal_disconnect( 803 | plugins_handle_p, 804 | signal_name, 805 | MODULE_NAME, 806 | PURPLE_CALLBACK(signal_info.handler) 807 | ); 808 | 809 | purple_signal_unregister(plugins_handle_p, signal_name); 810 | } 811 | } 812 | -------------------------------------------------------------------------------- /src/lurch_api.h: -------------------------------------------------------------------------------- 1 | #pragma once 2 | 3 | #define LURCH_ERR -1000000 4 | #define LURCH_ERR_NOMEM -1000001 5 | #define LURCH_ERR_NO_BUNDLE -1000010 6 | #define LURCH_ERR_DEVICE_NOT_IN_LIST -1000100 7 | 8 | typedef enum { 9 | LURCH_STATUS_DISABLED = 0, // manually disabled 10 | LURCH_STATUS_NOT_SUPPORTED, // no OMEMO support, i.e. there is no devicelist node 11 | LURCH_STATUS_NO_SESSION, // OMEMO is supported, but there is no libsignal session yet 12 | LURCH_STATUS_OK // OMEMO is supported and session exists 13 | } lurch_status_t; 14 | 15 | typedef enum { 16 | LURCH_STATUS_CHAT_DISABLED = 0, // OMEMO was not manually enabled 17 | LURCH_STATUS_CHAT_ANONYMOUS, // chat is anonymous, i.e. a member's JID could not be accessed 18 | LURCH_STATUS_CHAT_NO_DEVICELIST, // a member's devicelist could not be accessed, probably because s/he is not a contact 19 | LURCH_STATUS_CHAT_OK, // in theory, OMEMO should work 20 | LURCH_STATUS_CHAT_NO_JIDS // the chat is not anonymous, but the JIDs are still not visible 21 | } lurch_status_chat_t; 22 | 23 | /** 24 | * Initializes the API by registering the signals and signal handlers. 25 | */ 26 | void lurch_api_init(); 27 | 28 | /** 29 | * Unregisters the signals and disconnects the signal handlers. 30 | */ 31 | void lurch_api_unload(); 32 | 33 | /** 34 | * USAGE 35 | * 36 | * Some functions users might be interested in can be called via libpurple signals. 37 | * Thus, the libpurple commands interface uses these as well and lurch_cmd_ui.c is therefore full of examples. 38 | * Generally, the workflow is as follows: 39 | * 40 | * - Find the signal you need and check the handler function's parameters. 41 | * Generally, you will need to pass the user's PurpleAccount, a callback, and the data to be passed to the callback. 42 | * For some functions, the conversation partner's or chat's JID is also required. 43 | * 44 | * - Write the callback function needed by the handler. 45 | * The first parameter is an error value. It is generally the return value from the called functions. 46 | * If it is non-zero, an error occured somewhere and there should be more information in the debug log. 47 | * Otherwise, the call succeeded and the following parameters will be set. 48 | * The last parameter is the data given when emitting the signal. 49 | * 50 | * - Emit the signal using the plugin system handle as the instance and pass the necessary data. 51 | * If you do it wrong, there will be no compiler errors and the pointers are gibberish, so take care. 52 | * You can easily get the plugin system handle anywhere by calling purple_plugins_get_handle(). 53 | */ 54 | 55 | /** 56 | * SIGNAL: lurch-id-list 57 | * 58 | * Gets the specified account's OMEMO devicelist and passes it to the callback as a GList containing uint32_t *. 59 | * To access the actual ID, cast the data member to a uint32_t * and dereference it. 60 | * This device's ID will be the first item in the list. 61 | */ 62 | void lurch_api_id_list_handler(PurpleAccount * acc_p, void (*cb)(int32_t err, GList * id_list, void * user_data_p), void * user_data_p); 63 | 64 | /** 65 | * SIGNAL: lurch-id-remove 66 | * 67 | * Removes the specified OMEMO device ID from the specified account's devicelist. 68 | */ 69 | void lurch_api_id_remove_handler(PurpleAccount * acc_p, uint32_t device_id, void (*cb)(int32_t err, void * user_data_p), void * user_data_p); 70 | 71 | /** 72 | * SIGNAL: lurch-enable-im 73 | * 74 | * Enables OMEMO for the specified contact. 75 | */ 76 | void lurch_api_enable_im_handler(PurpleAccount * acc_p, const char * contact_bare_jid, void (*cb)(int32_t err, void * user_data_p), void * user_data_p); 77 | 78 | /** 79 | * SIGNAL: lurch-disable-im 80 | * 81 | * Disables OMEMO for the specified contact. 82 | */ 83 | void lurch_api_disable_im_handler(PurpleAccount * acc_p, const char * contact_bare_jid, void (*cb)(int32_t err, void * user_data_p), void * user_data_p); 84 | 85 | /** 86 | * SIGNAL: lurch-enable-chat 87 | * 88 | * Enables OMEMO for the specified chat. The conversation name can be obtained by simply calling purple_conversation_get_name(). 89 | */ 90 | void lurch_api_enable_chat_handler(PurpleAccount * acc_p, const char * full_conversation_name, void (*cb)(int32_t err, void * user_data_p), void * user_data_p); 91 | 92 | /** 93 | * SIGNAL: lurch-disable-chat 94 | * 95 | * Disables OMEMO for the specified chat. 96 | */ 97 | void lurch_api_disable_chat_handler(PurpleAccount * acc_p, const char * full_conversation_name, void (*cb)(int32_t err, void * user_data_p), void * user_data_p); 98 | 99 | /** 100 | * SIGNAL: lurch-fp-get 101 | * 102 | * Gets the this device's fingerprint in a printable format. 103 | */ 104 | void lurch_api_fp_get_handler(PurpleAccount * acc_p, void (*cb)(int32_t err, const char * fp_printable, void * user_data_p), void * user_data_p); 105 | 106 | /** 107 | * SIGNAL: lurch-fp-list 108 | * 109 | * Gets the fingerprints of all devices belonging to the specified account and creates a device-ID-to-fingerprint table. 110 | * This is based on sessions, so if there is an entry in the OMEMO devicelist, but no libsignal session yet, the value will be NULL. 111 | * If the whole devicelist is empty, i.e. the account is not an OMEMO user, the whole table will be NULL. 112 | * Watch out as this is not a valid GHashTable. 113 | */ 114 | void lurch_api_fp_list_handler(PurpleAccount * acc_p, void (*cb)(int32_t err, GHashTable * id_fp_table, void * user_data_p), void * user_data_p); 115 | 116 | /** 117 | * SIGNAL: lurch-fp-other 118 | * 119 | * Same as above, but for the specified contact. 120 | */ 121 | void lurch_api_fp_other_handler(PurpleAccount * acc_p, const char * contact_bare_jid, void (*cb)(int32_t err, GHashTable * id_fp_table, void * user_data_p), void * user_data_p); 122 | 123 | /** 124 | * SIGNAL: lurch-status-im 125 | * 126 | * Checks the OMEMO status for the given contact. Result is one of lurch_status_t. 127 | */ 128 | void lurch_api_status_im_handler(PurpleAccount * acc_p, const char * contact_bare_jid, void (*cb)(int32_t err, lurch_status_t status, void * user_data_p), void * user_data_p); 129 | 130 | /** 131 | * SIGNAL: lurch-status-chat 132 | * 133 | * Checks the OMEMO status for a MUC. 134 | * It not only looks up whether OMEMO was enabled, but also checks the preconditions for this feature, i.e. 135 | * whether all chat members' JIDs are visible (non-anonymous) and their devicelists accessible (in contact list). 136 | */ 137 | void lurch_api_status_chat_handler(PurpleAccount * acc_p, const char * full_conversation_name, void (*cb)(int32_t err, lurch_status_chat_t status, void * user_data_p), void * user_data_p); 138 | -------------------------------------------------------------------------------- /src/lurch_api_internal.h: -------------------------------------------------------------------------------- 1 | #pragma once 2 | 3 | #include 4 | 5 | #include "lurch_api.h" 6 | 7 | // things in here are exposed for testing. 8 | 9 | // Bundles the data the MUC feature discovery callback needs. 10 | typedef struct { 11 | char * db_fn_omemo; // Path to the account's OMEMO DB. 12 | void (*cb)(int32_t err, lurch_status_chat_t status, void * user_data_p); // The callback for the API call. 13 | void * user_data_p; // The data to be passed to cb(). 14 | } lurch_api_status_chat_cb_data; 15 | 16 | 17 | void lurch_api_status_chat_discover_cb(JabberStream * js_p, const char * from, JabberIqType type, const char * id, xmlnode * packet_p, gpointer data_p); 18 | -------------------------------------------------------------------------------- /src/lurch_cmd_ui.c: -------------------------------------------------------------------------------- 1 | #include 2 | #include 3 | #include 4 | #include 5 | 6 | #include "jutil.h" 7 | 8 | #include "libomemo.h" 9 | #include "lurch_api.h" 10 | 11 | static void lurch_cmd_print(PurpleConversation * conv_p, const char * msg) { 12 | purple_conversation_write(conv_p, "lurch", msg, PURPLE_MESSAGE_SYSTEM | PURPLE_MESSAGE_NO_LOG, time((void *) 0)); 13 | } 14 | 15 | static void lurch_cmd_print_err(PurpleConversation * conv_p, const char * msg) { 16 | purple_conversation_write(conv_p, "lurch", msg, PURPLE_MESSAGE_SYSTEM | PURPLE_MESSAGE_NO_LOG | PURPLE_MESSAGE_ERROR, time((void *) 0)); 17 | } 18 | 19 | static void lurch_cmd_help(PurpleConversation * conv_p) { 20 | const char * help_message = 21 | "The following commands exist to interact with the lurch plugin:\n\n" 22 | " - '/lurch enable': Enables OMEMO encryption for this conversation. On by default for regular conversations, off for group chats.\n" 23 | " - '/lurch disable': Disables OMEMO encryption for this conversation.\n" 24 | "\n" 25 | " - '/lurch id list': Displays this account's device list.\n" 26 | " - '/lurch id remove ': Removes the device ID from this account's device list.\n" 27 | "\n" 28 | " - '/lurch fp show': Displays this device's key fingerprint.\n" 29 | " - '/lurch fp list': Displays the fingerprints of all your devices.\n" 30 | " - '/lurch fp contact': Displays the fingerprints of all of your conversation partner's devices.\n" 31 | "\n" 32 | " - '/lurch status': Shows the OMEMO status of this conversation from your point of view.\n" 33 | " - '/lurch help': Displays this message."; 34 | 35 | lurch_cmd_print(conv_p, help_message); 36 | } 37 | 38 | void lurch_id_list_print(int32_t err, GList * id_list, void * user_data_p) { 39 | PurpleConversation * conv_p = (PurpleConversation *) user_data_p; 40 | 41 | char * temp_msg_1 = g_strdup_printf("\nYour devicelist is:\n%i (this device)\n", omemo_devicelist_list_data(id_list)); 42 | char * temp_msg_2 = (void *) 0; 43 | char * temp_msg_3 = (void *) 0; 44 | 45 | GList * curr_p = (void *) 0; 46 | 47 | if (err) { 48 | lurch_cmd_print_err(conv_p, "An error occured when trying to retrieve your ID list. Check the debug log for details."); 49 | return; 50 | } 51 | 52 | for (curr_p = id_list->next; curr_p; curr_p = curr_p->next) { 53 | temp_msg_2 = g_strdup_printf("%i\n", omemo_devicelist_list_data(curr_p)); 54 | 55 | temp_msg_3 = g_strconcat(temp_msg_1, temp_msg_2, NULL); 56 | 57 | g_free(temp_msg_1); 58 | temp_msg_1 = temp_msg_3; 59 | g_free(temp_msg_2); 60 | 61 | temp_msg_2 = (void *) 0; 62 | temp_msg_3 = (void *) 0; 63 | } 64 | 65 | lurch_cmd_print(conv_p, temp_msg_1); 66 | 67 | g_free(temp_msg_1); 68 | } 69 | 70 | void lurch_id_remove_print(int32_t err, void * user_data_p) { 71 | PurpleConversation * conv_p = (PurpleConversation *) user_data_p; 72 | 73 | if (err) { 74 | lurch_cmd_print_err(conv_p, "Failed to remove the ID from your devicelist. Check the debug log for details."); 75 | return; 76 | } 77 | 78 | lurch_cmd_print(conv_p, "Successfully removed the ID from your devicelist."); 79 | } 80 | 81 | void lurch_enable_print(int32_t err, void * user_data_p) { 82 | PurpleConversation * conv_p = (PurpleConversation *) user_data_p; 83 | 84 | if (err) { 85 | lurch_cmd_print_err(conv_p, "Failed to enable OMEMO for this conversation."); 86 | return; 87 | } 88 | 89 | purple_conversation_autoset_title(conv_p); 90 | lurch_cmd_print(conv_p, "Successfully enabled OMEMO."); 91 | } 92 | 93 | void lurch_disable_print(int32_t err, void * user_data_p) { 94 | PurpleConversation * conv_p = (PurpleConversation *) user_data_p; 95 | 96 | if (err) { 97 | lurch_cmd_print_err(conv_p, "Failed to disable OMEMO for this conversation."); 98 | return; 99 | } 100 | 101 | purple_conversation_autoset_title(conv_p); 102 | lurch_cmd_print(conv_p, "Successfully disabled OMEMO."); 103 | } 104 | 105 | void lurch_fp_show_print(int32_t err, const char * fp_printable, void * user_data_p) { 106 | PurpleConversation * conv_p = (PurpleConversation *) user_data_p; 107 | char * msg = (void *) 0; 108 | 109 | if (err) { 110 | lurch_cmd_print_err(conv_p, "Failed to get this device's fingerprint. Check the debug log for details."); 111 | return; 112 | } 113 | 114 | msg = g_strdup_printf("This device's fingerprint is %s.", fp_printable); 115 | lurch_cmd_print(conv_p, msg); 116 | 117 | g_free(msg); 118 | } 119 | 120 | void lurch_fp_print(int32_t err, GHashTable * id_fp_table, void * user_data_p) { 121 | PurpleConversation * conv_p = (PurpleConversation *) user_data_p; 122 | 123 | GString * msg = (void *) 0; 124 | GList * key_list = (void *) 0; 125 | const GList * curr_p = (void *) 0; 126 | const char * fp = (void *) 0; 127 | 128 | if (err) { 129 | lurch_cmd_print_err(conv_p, "Failed to get the fingerprints. Check the debug log for details."); 130 | return; 131 | } 132 | 133 | if (!id_fp_table) { 134 | lurch_cmd_print(conv_p, "The devicelist is empty, so there is nothing to show!"); 135 | return; 136 | } 137 | 138 | msg = g_string_new("\n"); 139 | key_list = g_hash_table_get_keys(id_fp_table); 140 | for (curr_p = key_list; curr_p; curr_p = curr_p->next) { 141 | fp = (char *) g_hash_table_lookup(id_fp_table, curr_p->data); 142 | g_string_append_printf(msg, "%i's fingerprint:\n%s\n", *((uint32_t *) curr_p->data), fp ? fp : "(no session)"); 143 | } 144 | 145 | lurch_cmd_print(conv_p, msg->str); 146 | 147 | g_string_free(msg, TRUE); 148 | g_list_free(key_list); 149 | } 150 | 151 | void lurch_status_im_print(int32_t err, lurch_status_t status, void * user_data_p) { 152 | PurpleConversation * conv_p = (PurpleConversation *) user_data_p; 153 | const char * msg = (void *) 0; 154 | 155 | if (err) { 156 | lurch_cmd_print_err(conv_p, "Failed to get the conversation status. Check the debug log for details."); 157 | return; 158 | } 159 | 160 | switch (status) { 161 | case LURCH_STATUS_DISABLED: 162 | msg = "You disabled OMEMO for this conversation. Type '/lurch enable' to switch it back on."; 163 | break; 164 | case LURCH_STATUS_NOT_SUPPORTED: 165 | msg = "Your contact does not support OMEMO. No devicelist could be found."; 166 | break; 167 | case LURCH_STATUS_NO_SESSION: 168 | msg = "Your contact supports OMEMO, but you have not established a session yet. Just start messaging!"; 169 | break; 170 | case LURCH_STATUS_OK: 171 | msg = "OMEMO is enabled for this conversation. You can turn it off by typing '/lurch disable'."; 172 | break; 173 | default: 174 | msg = "Received unknown status code."; 175 | } 176 | 177 | lurch_cmd_print(conv_p, msg); 178 | } 179 | 180 | void lurch_status_chat_print(int32_t err, lurch_status_chat_t status, void * user_data_p) { 181 | PurpleConversation * conv_p = (PurpleConversation *) user_data_p; 182 | const char * msg = (void *) 0; 183 | 184 | if (err) { 185 | lurch_cmd_print_err(conv_p, "Failed to get the conversation status. Check the debug log for details."); 186 | return; 187 | } 188 | 189 | switch (status) { 190 | case LURCH_STATUS_CHAT_DISABLED: 191 | msg = "OMEMO was not enabled for this conversation. Type '/lurch enable' to switch it on."; 192 | break; 193 | case LURCH_STATUS_CHAT_ANONYMOUS: 194 | msg = "The MUC is set to anonymous, which means that the members' JIDs are inaccessible and OMEMO will not work. Ask a moderator to change this."; 195 | break; 196 | case LURCH_STATUS_CHAT_NO_DEVICELIST: 197 | msg = "Could not access the OMEMO devicelist of at least one of the chat members. Make sure every member is in every other member's contact list."; 198 | break; 199 | case LURCH_STATUS_CHAT_OK: 200 | msg = "OMEMO is enabled for this conversation and everything should work. You can turn it off by typing '/lurch disable'."; 201 | break; 202 | case LURCH_STATUS_CHAT_NO_JIDS: 203 | msg = "The MUC is not anonymous, but the members' JIDs are inaccessible. This can happen if the MUC was reconfigured to be non-anonymous while this client was joined. Rejoining the chat will probably fix the issue."; 204 | break; 205 | default: 206 | msg = "Received unknown status code."; 207 | } 208 | 209 | lurch_cmd_print(conv_p, msg); 210 | } 211 | 212 | static void lurch_cmd_id(PurpleConversation * conv_p, const char * arg, const char * param) { 213 | PurpleAccount * acc_p = purple_conversation_get_account(conv_p); 214 | void * plugins_handle = purple_plugins_get_handle(); 215 | 216 | if (!g_strcmp0(arg, "list")) { 217 | purple_signal_emit(plugins_handle, "lurch-id-list", acc_p, lurch_id_list_print, conv_p); 218 | } else if (!g_strcmp0(arg, "remove")) { 219 | if (!param) { 220 | lurch_cmd_print_err(conv_p, "You have to specify the device ID to remove."); 221 | } else { 222 | purple_signal_emit(plugins_handle, "lurch-id-remove", acc_p, strtol(param, (void *) 0, 10), lurch_id_remove_print, conv_p); 223 | } 224 | } else { 225 | lurch_cmd_print(conv_p, "Valid arguments for 'id' are list' and 'remove '."); 226 | } 227 | } 228 | 229 | static void lurch_cmd_enable(PurpleConversation * conv_p) { 230 | char * bare_jid = (void *) 0; 231 | void * plugins_handle = purple_plugins_get_handle(); 232 | PurpleConversationType conv_type = purple_conversation_get_type(conv_p); 233 | PurpleAccount * acc_p = purple_conversation_get_account(conv_p); 234 | const char * conv_name = purple_conversation_get_name(conv_p); 235 | 236 | switch (conv_type) { 237 | case PURPLE_CONV_TYPE_IM: 238 | bare_jid = jabber_get_bare_jid(conv_name); 239 | purple_signal_emit(plugins_handle, "lurch-enable-im", acc_p, bare_jid, lurch_enable_print, conv_p); 240 | break; 241 | case PURPLE_CONV_TYPE_CHAT: 242 | purple_signal_emit(plugins_handle, "lurch-enable-chat", acc_p, conv_name, lurch_enable_print, conv_p); 243 | break; 244 | default: 245 | lurch_cmd_print_err(conv_p, "Conversation type not supported."); 246 | break; 247 | } 248 | 249 | g_free(bare_jid); 250 | } 251 | 252 | static void lurch_cmd_disable(PurpleConversation * conv_p) { 253 | char * bare_jid = (void *) 0; 254 | void * plugins_handle = purple_plugins_get_handle(); 255 | PurpleConversationType conv_type = purple_conversation_get_type(conv_p); 256 | PurpleAccount * acc_p = purple_conversation_get_account(conv_p); 257 | const char * conv_name = purple_conversation_get_name(conv_p); 258 | 259 | switch (conv_type) { 260 | case PURPLE_CONV_TYPE_IM: 261 | bare_jid = jabber_get_bare_jid(conv_name); 262 | purple_signal_emit(plugins_handle, "lurch-disable-im", acc_p, bare_jid, lurch_disable_print, conv_p); 263 | break; 264 | case PURPLE_CONV_TYPE_CHAT: 265 | purple_signal_emit(plugins_handle, "lurch-disable-chat", acc_p, conv_name, lurch_disable_print, conv_p); 266 | break; 267 | default: 268 | lurch_cmd_print_err(conv_p, "Conversation type not supported."); 269 | break; 270 | } 271 | 272 | g_free(bare_jid); 273 | } 274 | 275 | static void lurch_cmd_fp(PurpleConversation * conv_p, const char * arg) { 276 | PurpleAccount * acc_p = purple_conversation_get_account(conv_p); 277 | void * plugins_handle = purple_plugins_get_handle(); 278 | char * conv_bare_jid = (void *) 0; 279 | 280 | if (!g_strcmp0(arg, "show")) { 281 | purple_signal_emit(plugins_handle, "lurch-fp-get", acc_p, lurch_fp_show_print, conv_p); 282 | } else if (!g_strcmp0(arg, "list")) { 283 | lurch_cmd_print(conv_p, "Your devices' fingerprints are:"); 284 | purple_signal_emit(plugins_handle, "lurch-fp-list", acc_p, lurch_fp_print, conv_p); 285 | } else if (!g_strcmp0(arg, "contact")) { 286 | lurch_cmd_print(conv_p, "Your contact's devices' fingerprints are:"); 287 | conv_bare_jid = jabber_get_bare_jid(purple_conversation_get_name(conv_p)); 288 | purple_signal_emit(plugins_handle, "lurch-fp-other", acc_p, conv_bare_jid, lurch_fp_print, conv_p); 289 | } else { 290 | lurch_cmd_print(conv_p, "Valid arguments for 'fp' are 'show', 'list', and 'contact'."); 291 | } 292 | 293 | g_free(conv_bare_jid); 294 | } 295 | 296 | static void lurch_cmd_status(PurpleConversation * conv_p) { 297 | char * bare_jid = (void *) 0; 298 | const char * conv_name = purple_conversation_get_name(conv_p); 299 | PurpleConversationType conv_type = purple_conversation_get_type(conv_p); 300 | void * plugins_handle = purple_plugins_get_handle(); 301 | PurpleAccount * acc_p = purple_conversation_get_account(conv_p); 302 | 303 | switch (conv_type) { 304 | case PURPLE_CONV_TYPE_IM: 305 | bare_jid = jabber_get_bare_jid(conv_name); 306 | purple_signal_emit(plugins_handle, "lurch-status-im", acc_p, bare_jid, lurch_status_im_print, conv_p); 307 | break; 308 | case PURPLE_CONV_TYPE_CHAT: 309 | purple_signal_emit(plugins_handle, "lurch-status-chat", acc_p, conv_name, lurch_status_chat_print, conv_p); 310 | break; 311 | default: 312 | lurch_cmd_print_err(conv_p, "Conversation type not supported."); 313 | } 314 | 315 | g_free(bare_jid); 316 | } 317 | 318 | PurpleCmdRet lurch_cmd_func(PurpleConversation * conv_p, 319 | const gchar * cmd, 320 | gchar ** args, 321 | gchar ** error, 322 | void * data_p) { 323 | const char * command = args[0]; 324 | 325 | if (!g_strcmp0(command, "help")) { 326 | lurch_cmd_help(conv_p); 327 | } else if (!g_strcmp0(command, "enable")) { 328 | lurch_cmd_enable(conv_p); 329 | } else if (!g_strcmp0(command, "disable")) { 330 | lurch_cmd_disable(conv_p); 331 | } else if (!g_strcmp0(command, "id")) { 332 | lurch_cmd_id(conv_p, args[1], args[2]); 333 | } else if (!g_strcmp0(command, "fp")) { 334 | lurch_cmd_fp(conv_p, args[1]); 335 | } else if (!g_strcmp0(command, "status")) { 336 | lurch_cmd_status(conv_p); 337 | } else { 338 | lurch_cmd_print(conv_p, "No such command. Type '/lurch help' for a list of available commands."); 339 | } 340 | 341 | return PURPLE_CMD_RET_OK; 342 | } 343 | -------------------------------------------------------------------------------- /src/lurch_cmd_ui.h: -------------------------------------------------------------------------------- 1 | #pragma once 2 | 3 | #include 4 | #include 5 | 6 | PurpleCmdRet lurch_cmd_func(PurpleConversation * conv_p, 7 | const gchar * cmd, 8 | gchar ** args, 9 | gchar ** error, 10 | void * data_p); 11 | -------------------------------------------------------------------------------- /src/lurch_crypto.c: -------------------------------------------------------------------------------- 1 | 2 | #include 3 | #include 4 | 5 | #include "axc.h" 6 | #include "libomemo.h" 7 | 8 | #include "lurch_addr.h" 9 | #include "lurch_crypto.h" 10 | #include "lurch_util.h" 11 | 12 | int lurch_crypto_encrypt_key(const lurch_addr * recipient_addr_p, 13 | const uint8_t * key_p, 14 | size_t key_len, 15 | axc_context * axc_ctx_p, 16 | axc_buf ** key_ct_buf_pp) { 17 | int ret_val = 0; 18 | char * err_msg_dbg = (void *) 0; 19 | 20 | axc_buf * key_buf_p = (void *) 0; 21 | axc_buf * key_ct_buf_p = (void *) 0; 22 | axc_address axc_addr = {0}; 23 | 24 | purple_debug_info("lurch", "%s: encrypting key for %s:%i\n", __func__, recipient_addr_p->jid, recipient_addr_p->device_id); 25 | 26 | key_buf_p = axc_buf_create(key_p, key_len); 27 | if (!key_buf_p) { 28 | err_msg_dbg = g_strdup_printf("failed to create buffer for the key"); 29 | goto cleanup; 30 | } 31 | 32 | axc_addr.name = recipient_addr_p->jid; 33 | axc_addr.name_len = strnlen(axc_addr.name, JABBER_MAX_LEN_BARE); 34 | axc_addr.device_id = recipient_addr_p->device_id; 35 | 36 | ret_val = axc_message_encrypt_and_serialize(key_buf_p, &axc_addr, axc_ctx_p, &key_ct_buf_p); 37 | if (ret_val) { 38 | err_msg_dbg = g_strdup_printf("failed to encrypt the key"); 39 | goto cleanup; 40 | } 41 | 42 | *key_ct_buf_pp = key_ct_buf_p; 43 | 44 | cleanup: 45 | if (ret_val) { 46 | axc_buf_free(key_ct_buf_p); 47 | } 48 | if (err_msg_dbg) { 49 | purple_debug_error("lurch", "%s: %s (%i)\n", __func__, err_msg_dbg, ret_val); 50 | g_free(err_msg_dbg); 51 | } 52 | axc_buf_free(key_buf_p); 53 | 54 | return ret_val; 55 | } 56 | 57 | int lurch_crypto_encrypt_msg_for_addrs(omemo_message * om_msg_p, GList * addr_l_p, GList * no_sess_l_p, axc_context * axc_ctx_p) { 58 | int ret_val = 0; 59 | char * err_msg_dbg = (void *) 0; 60 | 61 | GList * curr_l_p = (void *) 0; 62 | lurch_addr * curr_addr_p = (void *) 0; 63 | axc_address addr = {0}; 64 | axc_buf * curr_key_ct_buf_p = (void *) 0; 65 | 66 | purple_debug_info("lurch", "%s: trying to encrypt key for %i devices\n", __func__, g_list_length(addr_l_p)); 67 | 68 | for (curr_l_p = addr_l_p; curr_l_p; curr_l_p = curr_l_p->next) { 69 | curr_addr_p = (lurch_addr *) curr_l_p->data; 70 | addr.name = curr_addr_p->jid; 71 | addr.name_len = strnlen(addr.name, JABBER_MAX_LEN_BARE); 72 | addr.device_id = curr_addr_p->device_id; 73 | 74 | ret_val = axc_session_exists_initiated(&addr, axc_ctx_p); 75 | if (ret_val < 0) { 76 | err_msg_dbg = g_strdup_printf("failed to check if session exists, aborting"); 77 | goto cleanup; 78 | } else if (!ret_val) { 79 | purple_debug_warning("lurch", "%s: no initiated session for recipient %s:%d, skipping\n", __func__, addr.name, addr.device_id); 80 | continue; 81 | } else { 82 | ret_val = lurch_crypto_encrypt_key(curr_addr_p, 83 | omemo_message_get_key(om_msg_p), 84 | omemo_message_get_key_len(om_msg_p), 85 | axc_ctx_p, 86 | &curr_key_ct_buf_p); 87 | if (ret_val) { 88 | err_msg_dbg = g_strdup_printf("failed to encrypt key for %s:%i", curr_addr_p->jid, curr_addr_p->device_id); 89 | goto cleanup; 90 | } 91 | 92 | if (g_list_find(no_sess_l_p, curr_addr_p)) { 93 | ret_val = omemo_message_add_recipient_w_prekey(om_msg_p, 94 | curr_addr_p->device_id, 95 | axc_buf_get_data(curr_key_ct_buf_p), 96 | axc_buf_get_len(curr_key_ct_buf_p)); 97 | } else { 98 | ret_val = omemo_message_add_recipient(om_msg_p, 99 | curr_addr_p->device_id, 100 | axc_buf_get_data(curr_key_ct_buf_p), 101 | axc_buf_get_len(curr_key_ct_buf_p)); 102 | } 103 | if (ret_val) { 104 | err_msg_dbg = g_strdup_printf("failed to add recipient to omemo msg"); 105 | goto cleanup; 106 | } 107 | 108 | axc_buf_free(curr_key_ct_buf_p); 109 | curr_key_ct_buf_p = (void *) 0; 110 | } 111 | } 112 | 113 | cleanup: 114 | if (err_msg_dbg) { 115 | purple_debug_error("lurch", "%s: %s (%i)\n", __func__, err_msg_dbg, ret_val); 116 | g_free(err_msg_dbg); 117 | } 118 | axc_buf_free(curr_key_ct_buf_p); 119 | 120 | return ret_val; 121 | } 122 | -------------------------------------------------------------------------------- /src/lurch_crypto.h: -------------------------------------------------------------------------------- 1 | #pragma once 2 | 3 | #include 4 | 5 | #include "axc.h" 6 | #include "libomemo.h" 7 | #include "libomemo_crypto.h" 8 | 9 | /** 10 | * The OMEMO crypto provider settings. 11 | */ 12 | static const omemo_crypto_provider crypto = { 13 | .random_bytes_func = omemo_default_crypto_random_bytes, 14 | .aes_gcm_encrypt_func = omemo_default_crypto_aes_gcm_encrypt, 15 | .aes_gcm_decrypt_func = omemo_default_crypto_aes_gcm_decrypt 16 | }; 17 | 18 | /** 19 | * Encrypts a data buffer, usually the omemo symmetric key, using axolotl. 20 | * Assumes a valid session already exists. 21 | * 22 | * @param recipient_addr_p Pointer to the lurch_addr of the recipient. 23 | * @param key_p Pointer to the key data. 24 | * @param key_len Length of the key data. 25 | * @param axc_ctx_p Pointer to the axc_context to use. 26 | * @param key_ct_pp Will point to a pointer to an axc_buf containing the key ciphertext on success. 27 | * @return 0 on success, negative on error 28 | */ 29 | int lurch_crypto_encrypt_key(const lurch_addr * recipient_addr_p, 30 | const uint8_t * key_p, 31 | size_t key_len, 32 | axc_context * axc_ctx_p, 33 | axc_buf ** key_ct_buf_pp); 34 | 35 | /** 36 | * For each of the recipients, encrypts the symmetric key using the existing axc session, 37 | * then adds it to the omemo message. 38 | * If the session does not exist, the recipient is skipped. 39 | * 40 | * @param om_msg_p Pointer to the omemo message. 41 | * @param addr_l_p Pointer to the head of a list of the intended recipients' lurch_addrs. 42 | * @param no_sess_l_p Pointer to the head of a list of the subset of the inteded recipients for which a session was not yet 43 | * established when the message was written. 44 | * @param axc_ctx_p Pointer to the axc_context to use. 45 | * @return 0 on success, negative on error. 46 | */ 47 | int lurch_crypto_encrypt_msg_for_addrs(omemo_message * om_msg_p, GList * addr_l_p, GList * no_sess_l_p, axc_context * axc_ctx_p); 48 | -------------------------------------------------------------------------------- /src/lurch_util.c: -------------------------------------------------------------------------------- 1 | #include 2 | #include 3 | 4 | #include "axc.h" 5 | 6 | #include "lurch_util.h" 7 | 8 | void lurch_util_axc_log_func(int level, const char * msg, size_t len, void * user_data) { 9 | (void) len; 10 | axc_context * ctx_p = (axc_context *) user_data; 11 | int log_level = axc_context_get_log_level(ctx_p); 12 | 13 | switch(level) { 14 | case AXC_LOG_ERROR: 15 | if (log_level >= AXC_LOG_ERROR) { 16 | purple_debug_error("lurch", "[AXC ERROR] %s\n", msg); 17 | } 18 | break; 19 | case AXC_LOG_WARNING: 20 | if (log_level >= AXC_LOG_WARNING) { 21 | purple_debug_warning("lurch", "[AXC WARNING] %s\n", msg); 22 | } 23 | break; 24 | case AXC_LOG_NOTICE: 25 | if (log_level >= AXC_LOG_NOTICE) { 26 | purple_debug_info("lurch", "[AXC NOTICE] %s\n", msg); 27 | } 28 | break; 29 | case AXC_LOG_INFO: 30 | if (log_level >= AXC_LOG_INFO) { 31 | purple_debug_info("lurch", "[AXC INFO] %s\n", msg); 32 | } 33 | break; 34 | case AXC_LOG_DEBUG: 35 | if (log_level >= AXC_LOG_DEBUG) { 36 | purple_debug_misc("lurch", "[AXC DEBUG] %s\n", msg); 37 | } 38 | break; 39 | default: 40 | purple_debug_misc("lurch", "[AXC %d] %s\n", level, msg); 41 | break; 42 | } 43 | } 44 | 45 | /** 46 | * Creates and initializes the axc context. 47 | * 48 | * @param uname The username. 49 | * @param ctx_pp Will point to an initialized axc context on success. 50 | * @return 0 on success, negative on error. 51 | */ 52 | int lurch_util_axc_get_init_ctx(char * uname, axc_context ** ctx_pp) { 53 | int ret_val = 0; 54 | char * err_msg_dbg = (void *) 0; 55 | 56 | axc_context * ctx_p = (void *) 0; 57 | char * db_fn = (void *) 0; 58 | 59 | ret_val = axc_context_create(&ctx_p); 60 | if (ret_val) { 61 | err_msg_dbg = g_strdup_printf("failed to create axc context"); 62 | goto cleanup; 63 | } 64 | 65 | db_fn = lurch_util_uname_get_db_fn(uname, LURCH_DB_NAME_AXC); 66 | ret_val = axc_context_set_db_fn(ctx_p, db_fn, strlen(db_fn)); 67 | if (ret_val) { 68 | err_msg_dbg = g_strdup_printf("failed to set axc db filename to %s", db_fn); 69 | goto cleanup; 70 | } 71 | 72 | if (purple_prefs_get_bool(LURCH_PREF_AXC_LOGGING)) { 73 | axc_context_set_log_func(ctx_p, lurch_util_axc_log_func); 74 | axc_context_set_log_level(ctx_p, purple_prefs_get_int(LURCH_PREF_AXC_LOGGING_LEVEL)); 75 | } 76 | 77 | ret_val = axc_init(ctx_p); 78 | if (ret_val) { 79 | err_msg_dbg = g_strdup_printf("failed to init axc context"); 80 | goto cleanup; 81 | } 82 | 83 | if (purple_prefs_get_bool(LURCH_PREF_AXC_LOGGING)) { 84 | signal_context_set_log_function(axc_context_get_axolotl_ctx(ctx_p), lurch_util_axc_log_func); 85 | } 86 | 87 | *ctx_pp = ctx_p; 88 | 89 | cleanup: 90 | if (ret_val) { 91 | axc_context_destroy_all(ctx_p); 92 | } 93 | if (err_msg_dbg) { 94 | purple_debug_error("lurch", "%s: %s (%i)\n", __func__, err_msg_dbg, ret_val); 95 | g_free(err_msg_dbg); 96 | } 97 | 98 | g_free (db_fn); 99 | return ret_val; 100 | } 101 | 102 | char * lurch_util_uname_strip(const char * uname) { 103 | char ** split; 104 | char * stripped; 105 | 106 | if (!uname || strlen(uname) == 0) { 107 | return (void *) 0; 108 | } 109 | 110 | split = g_strsplit(uname, "/", 2); 111 | stripped = g_strdup(split[0]); 112 | 113 | g_strfreev(split); 114 | 115 | return stripped; 116 | } 117 | 118 | char * lurch_util_uname_get_db_fn(const char * uname, const char * which) { 119 | return g_strconcat(purple_user_dir(), "/", uname, "_", which, LURCH_DB_SUFFIX, NULL); 120 | } 121 | 122 | char * lurch_util_fp_get_printable(axc_buf * key_buf_p) { 123 | gchar * fp = (void *) 0; 124 | char ** split = (void *) 0; 125 | const size_t fp_parts_len = 32; 126 | char * printable = (void *) 0; 127 | const size_t printable_len = 72; 128 | 129 | if (!key_buf_p) { 130 | purple_debug_warning("lurch", "%s: Key buffer is null, aborting\n", __func__); 131 | goto cleanup; 132 | } 133 | 134 | fp = purple_base16_encode_chunked(axc_buf_get_data(key_buf_p), axc_buf_get_len(key_buf_p)); 135 | if (!fp || strlen(fp) != 98) { 136 | purple_debug_warning("lurch", "%s: Unexpected fingerprint length, aborting\n", __func__); 137 | goto cleanup; 138 | } 139 | 140 | // first part is dismissed for display 141 | split = g_strsplit(fp, ":", fp_parts_len + 1); 142 | printable = g_malloc0(printable_len); 143 | 144 | for (int i = 1; i <= fp_parts_len; i++) { 145 | g_strlcat(printable, split[i], printable_len); 146 | 147 | if (i % 4 == 0 && i != fp_parts_len) { 148 | g_strlcat(printable, " ", printable_len); 149 | } 150 | } 151 | 152 | cleanup: 153 | g_free(fp); 154 | g_strfreev(split); 155 | 156 | return printable; 157 | } 158 | 159 | -------------------------------------------------------------------------------- /src/lurch_util.h: -------------------------------------------------------------------------------- 1 | #pragma once 2 | 3 | #include "axc.h" 4 | 5 | #define JABBER_PROTOCOL_ID "prpl-jabber" 6 | 7 | // see https://www.ietf.org/rfc/rfc3920.txt 8 | #define JABBER_MAX_LEN_NODE 1023 9 | #define JABBER_MAX_LEN_DOMAIN 1023 10 | #define JABBER_MAX_LEN_BARE JABBER_MAX_LEN_NODE + JABBER_MAX_LEN_DOMAIN + 1 11 | 12 | #define LURCH_PREF_ROOT "/plugins/core/lurch" 13 | #define LURCH_PREF_AXC_LOGGING LURCH_PREF_ROOT "/axc_logging" 14 | #define LURCH_PREF_AXC_LOGGING_LEVEL LURCH_PREF_AXC_LOGGING "/level" 15 | 16 | #define LURCH_DB_SUFFIX "_db.sqlite" 17 | #define LURCH_DB_NAME_OMEMO "omemo" 18 | #define LURCH_DB_NAME_AXC "axc" 19 | 20 | 21 | /** 22 | * Creates and initializes the axc context. 23 | * 24 | * @param uname The username. 25 | * @param ctx_pp Will point to an initialized axc context on success. 26 | * @return 0 on success, negative on error. 27 | */ 28 | int lurch_util_axc_get_init_ctx(char * uname, axc_context ** ctx_pp); 29 | 30 | /** 31 | * For some reason pidgin returns account names with a trailing "/". 32 | * This function removes it. 33 | * All other functions asking for the username assume the "/" is already stripped. 34 | * jabber_get_bare_jid returns null for some reason or other. 35 | * 36 | * @param uname The username. 37 | * @return A duplicated string with the trailing "/" removed. free() when done with it. 38 | */ 39 | char * lurch_util_uname_strip(const char * uname); 40 | 41 | /** 42 | * Returns the db name, has to be g_free()d. 43 | * 44 | * @param uname The username. 45 | * @param which Either LURCH_DB_NAME_OMEMO or LURCH_DB_NAME_AXC 46 | * @return The path string. free() when done with it. 47 | */ 48 | char * lurch_util_uname_get_db_fn(const char * uname, const char * which); 49 | 50 | /** 51 | * Creates a fingerprint which resembles the one displayed by Conversations etc. 52 | * Also useful for avoiding the smileys produced by ':d'... 53 | * 54 | * @param key_buf_p The buffer containing the public key data. 55 | * @return A newly allocated string which contains the fingerprint in printable format, or NULL. g_free() when done. 56 | */ 57 | char * lurch_util_fp_get_printable(axc_buf * key_buf_p); 58 | 59 | /** 60 | * Log wrapper for AXC. This should only be needed internally, but it's not exactly a secret so it's exposed for unit testing. 61 | * 62 | * @param level an AXC_LOG level 63 | * @param msg the log message 64 | * @param len the length of the message 65 | * @param ctx_p the axc context 66 | */ 67 | void lurch_util_axc_log_func(int level, const char * msg, size_t len, void * user_data); 68 | -------------------------------------------------------------------------------- /test/test_lurch_crypto.c: -------------------------------------------------------------------------------- 1 | #include 2 | #include 3 | #include 4 | #include 5 | #include 6 | 7 | #include 8 | #include "jabber.h" 9 | 10 | #include "libomemo.h" 11 | 12 | #include "../src/lurch_addr.h" 13 | #include "../src/lurch_crypto.h" 14 | 15 | /** 16 | * The mock asserts the function was called, checks the addresses name member via 'name', and returns a mocked int. 17 | */ 18 | int __wrap_axc_message_encrypt_and_serialize(axc_buf * msg_p, const axc_address * recipient_addr_p, axc_context * ctx_p, axc_buf ** ciphertext_pp) { 19 | int ret_val; 20 | 21 | function_called(); 22 | const char * name = recipient_addr_p->name; 23 | check_expected(name); 24 | 25 | // the buffer is freed by the caller, so alloc here to prevent segfault 26 | axc_buf * mock_buf = axc_buf_create(NULL, 0); 27 | *ciphertext_pp = mock_buf; 28 | 29 | ret_val = mock_type(int); 30 | return ret_val; 31 | } 32 | 33 | /** 34 | * The mock asserts the function was called, checks addr_p, and returns a mocked int. 35 | */ 36 | int __wrap_axc_session_exists_initiated(const axc_address * addr_p, axc_context * ctx_p) { 37 | int ret_val; 38 | 39 | function_called(); 40 | check_expected_ptr(addr_p); 41 | 42 | ret_val = mock_type(int); 43 | return ret_val; 44 | } 45 | 46 | /** 47 | * When the list of recipients is empty, nothing happens. 48 | */ 49 | static void test_lurch_crypto_encrypt_msg_for_addrs_empty_list(void ** state) { 50 | (void) state; 51 | 52 | assert_return_code(lurch_crypto_encrypt_msg_for_addrs(NULL, NULL, NULL, NULL), 0); 53 | } 54 | 55 | /** 56 | * When no session exists, nothing happens. 57 | */ 58 | static void test_lurch_crypto_encrypt_msg_for_addrs_no_sessions(void ** state) { 59 | (void) state; 60 | 61 | lurch_addr test_addr = { 62 | .jid = "wanted@jabber.id", 63 | .device_id = 1234 64 | }; 65 | 66 | expect_function_call(__wrap_axc_session_exists_initiated); 67 | expect_string(__wrap_axc_session_exists_initiated, addr_p, &test_addr); 68 | will_return(__wrap_axc_session_exists_initiated, 0); 69 | 70 | assert_return_code(lurch_crypto_encrypt_msg_for_addrs(NULL, g_list_append(NULL, &test_addr), NULL, NULL), 0); 71 | } 72 | 73 | /** 74 | * On error, passes on the status code. 75 | */ 76 | static void test_lurch_crypto_encrypt_msg_for_addrs_err(void ** state) { 77 | (void) state; 78 | 79 | lurch_addr test_addr = { 80 | .jid = "wanted@jabber.id", 81 | .device_id = 1234 82 | }; 83 | 84 | const int wanted_errcode = -1337; 85 | expect_function_call(__wrap_axc_session_exists_initiated); 86 | expect_string(__wrap_axc_session_exists_initiated, addr_p, &test_addr); 87 | will_return(__wrap_axc_session_exists_initiated, wanted_errcode); 88 | 89 | assert_int_equal(lurch_crypto_encrypt_msg_for_addrs(NULL, g_list_append(NULL, &test_addr), NULL, NULL), wanted_errcode); 90 | } 91 | 92 | /** 93 | * Encrypts the OMEMO key with the recipient's Signal session and adds a recipient element to the omemo message. 94 | * Since the session already existed, it is not marked as a "prekey". 95 | */ 96 | static void test_lurch_crypto_encrypt_msg_for_addrs_happy(void ** state) { 97 | (void) state; 98 | 99 | lurch_addr test_addr = { 100 | .jid = "wanted@jabber.id", 101 | .device_id = 1234 102 | }; 103 | 104 | expect_function_call(__wrap_axc_session_exists_initiated); 105 | expect_string(__wrap_axc_session_exists_initiated, addr_p, &test_addr); 106 | will_return(__wrap_axc_session_exists_initiated, 1); 107 | 108 | expect_function_call(__wrap_axc_message_encrypt_and_serialize); 109 | expect_string(__wrap_axc_message_encrypt_and_serialize, name, test_addr.jid); 110 | will_return(__wrap_axc_message_encrypt_and_serialize, 0); 111 | 112 | omemo_message * om_msg_p; 113 | assert_int_equal(omemo_message_create(789, &crypto, &om_msg_p), 0); 114 | 115 | assert_return_code(lurch_crypto_encrypt_msg_for_addrs(om_msg_p, g_list_append(NULL, &test_addr), NULL, NULL), 0); 116 | 117 | uint8_t * key_buf_p = NULL; 118 | size_t key_buf_size = 234; 119 | assert_int_equal(omemo_message_get_encrypted_key(om_msg_p, test_addr.device_id, &key_buf_p, &key_buf_size), 0); 120 | assert_non_null(key_buf_p); 121 | assert_int_equal(key_buf_size, 0); // the mock encryption returns a buffer of length 0 122 | 123 | bool is_prekey = true; 124 | assert_int_equal(omemo_message_is_encrypted_key_prekey(om_msg_p, test_addr.device_id, &is_prekey), 0); 125 | assert_false(is_prekey); 126 | } 127 | 128 | /** 129 | * Encrypts the OMEMO key with the recipient's Signal session and adds a recipient element to the omemo message. 130 | * Since the session did not exist yet, it is marked as a "prekey". 131 | */ 132 | static void test_lurch_crypto_encrypt_msg_for_addrs_prekey(void ** state) { 133 | (void) state; 134 | 135 | lurch_addr test_addr = { 136 | .jid = "wanted@jabber.id", 137 | .device_id = 1234 138 | }; 139 | 140 | expect_function_call(__wrap_axc_session_exists_initiated); 141 | expect_string(__wrap_axc_session_exists_initiated, addr_p, &test_addr); 142 | will_return(__wrap_axc_session_exists_initiated, 1); 143 | 144 | expect_function_call(__wrap_axc_message_encrypt_and_serialize); 145 | expect_string(__wrap_axc_message_encrypt_and_serialize, name, test_addr.jid); 146 | will_return(__wrap_axc_message_encrypt_and_serialize, 0); 147 | 148 | omemo_message * om_msg_p; 149 | assert_int_equal(omemo_message_create(789, &crypto, &om_msg_p), 0); 150 | 151 | assert_return_code(lurch_crypto_encrypt_msg_for_addrs(om_msg_p, g_list_append(NULL, &test_addr), g_list_append(NULL, &test_addr), NULL), 0); 152 | 153 | uint8_t * key_buf_p = NULL; 154 | size_t key_buf_size = 234; 155 | assert_int_equal(omemo_message_get_encrypted_key(om_msg_p, test_addr.device_id, &key_buf_p, &key_buf_size), 0); 156 | assert_non_null(key_buf_p); 157 | assert_int_equal(key_buf_size, 0); // the mock encryption returns a buffer of length 0 158 | 159 | bool is_prekey = false; 160 | assert_int_equal(omemo_message_is_encrypted_key_prekey(om_msg_p, test_addr.device_id, &is_prekey), 0); 161 | assert_true(is_prekey); 162 | } 163 | 164 | int main(void) { 165 | const struct CMUnitTest tests[] = { 166 | cmocka_unit_test(test_lurch_crypto_encrypt_msg_for_addrs_empty_list), 167 | cmocka_unit_test(test_lurch_crypto_encrypt_msg_for_addrs_no_sessions), 168 | cmocka_unit_test(test_lurch_crypto_encrypt_msg_for_addrs_err), 169 | cmocka_unit_test(test_lurch_crypto_encrypt_msg_for_addrs_happy), 170 | cmocka_unit_test(test_lurch_crypto_encrypt_msg_for_addrs_prekey) 171 | }; 172 | 173 | return cmocka_run_group_tests_name("lurch_crypto", tests, NULL, NULL); 174 | } 175 | -------------------------------------------------------------------------------- /test/test_lurch_util.c: -------------------------------------------------------------------------------- 1 | #include 2 | #include 3 | #include 4 | #include 5 | #include 6 | 7 | #include "axc.h" 8 | 9 | #include "../src/lurch.h" 10 | #include "../src/lurch_util.h" 11 | 12 | void __wrap_purple_debug_error(const char * category, const char * format, ...) { 13 | function_called(); 14 | } 15 | 16 | void __wrap_purple_debug_info(const char * category, const char * format, ...) { 17 | function_called(); 18 | } 19 | 20 | void __wrap_purple_debug_misc(const char * category, const char * format, ...) { 21 | } 22 | 23 | int __wrap_axc_context_create(axc_context ** axc_ctx_pp) { 24 | int ret_val; 25 | ret_val = mock_type(int); 26 | return ret_val; 27 | } 28 | 29 | const char * __wrap_purple_user_dir(void) { 30 | char * user_dir; 31 | user_dir = mock_ptr_type(char *); 32 | return user_dir; 33 | } 34 | 35 | gboolean __wrap_purple_prefs_get_bool(const char * pref_name) { 36 | check_expected(pref_name); 37 | 38 | gboolean pref_val; 39 | pref_val = mock_type(gboolean); 40 | return pref_val; 41 | } 42 | 43 | int __wrap_purple_prefs_get_int(const char * pref_name) { 44 | check_expected(pref_name); 45 | 46 | int pref_val; 47 | pref_val = mock_type(int); 48 | return pref_val; 49 | } 50 | 51 | char * __wrap_purple_base16_encode_chunked(const guchar *data, gsize len) { 52 | char * fp; 53 | fp = mock_ptr_type(char *); 54 | return fp; 55 | } 56 | 57 | /** 58 | * Log only errors when log level is set to AXC_LOG_ERROR, using purple_debug_error(). 59 | */ 60 | static void test_lurch_util_axc_log_func_error(void ** state) { 61 | (void) state; 62 | 63 | axc_context * axc_ctx_p = (void *) 0; 64 | 65 | (void) axc_context_create(&axc_ctx_p); 66 | axc_context_set_log_level(axc_ctx_p, AXC_LOG_ERROR); 67 | expect_function_call(__wrap_purple_debug_error); 68 | 69 | lurch_util_axc_log_func(AXC_LOG_ERROR, "test", 4, axc_ctx_p); 70 | lurch_util_axc_log_func(AXC_LOG_WARNING, "test", 4, axc_ctx_p); 71 | lurch_util_axc_log_func(AXC_LOG_NOTICE, "test", 4, axc_ctx_p); 72 | lurch_util_axc_log_func(AXC_LOG_INFO, "test", 4, axc_ctx_p); 73 | lurch_util_axc_log_func(AXC_LOG_DEBUG, "test", 4, axc_ctx_p); 74 | lurch_util_axc_log_func(-1, "test", 4, axc_ctx_p); 75 | 76 | axc_context_destroy_all(axc_ctx_p); 77 | } 78 | 79 | /** 80 | * Log level info and above, but not below. 81 | */ 82 | static void test_lurch_util_axc_log_func_info(void ** state) { 83 | (void) state; 84 | 85 | axc_context * axc_ctx_p = (void *) 0; 86 | 87 | (void) axc_context_create(&axc_ctx_p); 88 | axc_context_set_log_level(axc_ctx_p, AXC_LOG_INFO); 89 | expect_function_call(__wrap_purple_debug_error); 90 | expect_function_calls(__wrap_purple_debug_info, 2); 91 | 92 | lurch_util_axc_log_func(AXC_LOG_ERROR, "test", 4, axc_ctx_p); 93 | lurch_util_axc_log_func(AXC_LOG_WARNING, "test", 4, axc_ctx_p); 94 | lurch_util_axc_log_func(AXC_LOG_NOTICE, "test", 4, axc_ctx_p); 95 | lurch_util_axc_log_func(AXC_LOG_INFO, "test", 4, axc_ctx_p); 96 | lurch_util_axc_log_func(AXC_LOG_DEBUG, "test", 4, axc_ctx_p); 97 | 98 | axc_context_destroy_all(axc_ctx_p); 99 | } 100 | 101 | 102 | static void test_lurch_util_axc_get_init_ctx_w_logging(void ** state) { 103 | (void) state; 104 | 105 | will_return(__wrap_purple_user_dir, "/home/testuser/.purple"); 106 | 107 | expect_string_count(__wrap_purple_prefs_get_bool, pref_name, LURCH_PREF_AXC_LOGGING, 2); 108 | will_return_count(__wrap_purple_prefs_get_bool, TRUE, 2); 109 | 110 | expect_string(__wrap_purple_prefs_get_int, pref_name, LURCH_PREF_AXC_LOGGING_LEVEL); 111 | will_return(__wrap_purple_prefs_get_int, AXC_LOG_NOTICE); 112 | 113 | char * test_jid = "test-user@example.com"; 114 | 115 | axc_context * test_axc_ctx_p = (void *) 0; 116 | lurch_util_axc_get_init_ctx(test_jid, &test_axc_ctx_p); 117 | 118 | assert_string_equal(axc_context_get_db_fn(test_axc_ctx_p), "/home/testuser/.purple/test-user@example.com_axc_db.sqlite"); 119 | assert_int_equal(axc_context_get_log_level(test_axc_ctx_p), AXC_LOG_NOTICE); 120 | } 121 | 122 | static void test_lurch_util_axc_get_init_ctx_wo_logging(void ** state) { 123 | (void) state; 124 | 125 | will_return(__wrap_purple_user_dir, "/home/testuser/.purple"); 126 | 127 | expect_string_count(__wrap_purple_prefs_get_bool, pref_name, LURCH_PREF_AXC_LOGGING, 2); 128 | will_return_count(__wrap_purple_prefs_get_bool, FALSE, 2); 129 | 130 | char * test_jid = "test-user@example.com"; 131 | 132 | axc_context * test_axc_ctx_p = (void *) 0; 133 | lurch_util_axc_get_init_ctx(test_jid, &test_axc_ctx_p); 134 | 135 | assert_string_equal(axc_context_get_db_fn(test_axc_ctx_p), "/home/testuser/.purple/test-user@example.com_axc_db.sqlite"); 136 | assert_int_equal(axc_context_get_log_level(test_axc_ctx_p), -1); 137 | } 138 | 139 | static void test_lurch_util_uname_strip(void ** state) { 140 | (void) state; 141 | 142 | assert_string_equal(lurch_util_uname_strip("node@domain/resource"), "node@domain"); 143 | } 144 | 145 | static void test_lurch_util_uname_strip_no_resource(void ** state) { 146 | (void) state; 147 | 148 | assert_string_equal(lurch_util_uname_strip("node@domain/"), "node@domain"); 149 | assert_string_equal(lurch_util_uname_strip("node@domain"), "node@domain"); 150 | } 151 | 152 | static void test_lurch_util_uname_strip_empty(void ** state) { 153 | (void) state; 154 | 155 | assert_null(lurch_util_uname_strip("")); 156 | assert_null(lurch_util_uname_strip(NULL)); 157 | } 158 | 159 | static void test_lurch_util_uname_get_db_fn(void ** state) { 160 | (void) state; 161 | 162 | will_return(__wrap_purple_user_dir, "/home/testuser/.purple"); 163 | 164 | assert_string_equal(lurch_util_uname_get_db_fn("test-uname@example.com", "TESTTYPE"), 165 | "/home/testuser/.purple/test-uname@example.com_TESTTYPE_db.sqlite"); 166 | } 167 | 168 | static void test_lurch_util_fp_get_printable(void ** state) { 169 | (void) state; 170 | 171 | const char * fp_as_returned_by_pidgin = 172 | "12:34:56:78:12:34:56:78:12:34:56:78:12:34:56:78:12:34:56:78:12:34:56:78:12:34:56:78:12:ab:cd:ef:gh"; 173 | 174 | will_return(__wrap_purple_base16_encode_chunked, g_strdup(fp_as_returned_by_pidgin)); 175 | 176 | char * printable_fp = lurch_util_fp_get_printable((void *) &"fake non-null pointer"); 177 | assert_non_null(printable_fp); 178 | assert_string_equal(printable_fp, "34567812 34567812 34567812 34567812 34567812 34567812 34567812 abcdefgh"); 179 | } 180 | 181 | static void test_lurch_util_fp_get_printable_invalid(void ** state) { 182 | (void) state; 183 | 184 | assert_null(lurch_util_fp_get_printable(NULL)); 185 | 186 | will_return(__wrap_purple_base16_encode_chunked, NULL); 187 | assert_null(lurch_util_fp_get_printable((void *) &"fake non-null pointer")); 188 | 189 | const char * too_short = 190 | "12:34:56:78:12:34:56:78:12:34:56:78:12:34:56:78:12:34:56:78:12:34:56:78:12:34:56:78:12:ab:cd:efgh"; 191 | will_return(__wrap_purple_base16_encode_chunked, g_strdup(too_short)); 192 | assert_null(lurch_util_fp_get_printable((void *) &"fake non-null pointer")); 193 | 194 | const char * too_long = 195 | "12:34:56:78:12:34:56:78:12:34:56:78:12:34:56:78:12:34:56:78:12:34:56:78:12:34:56:78:12:ab:cd:ef:gh:"; 196 | will_return(__wrap_purple_base16_encode_chunked, g_strdup(too_long)); 197 | 198 | assert_null(lurch_util_fp_get_printable((void *) &"fake non-null pointer")); 199 | } 200 | 201 | int main(void) { 202 | const struct CMUnitTest tests[] = { 203 | cmocka_unit_test(test_lurch_util_axc_log_func_error), 204 | cmocka_unit_test(test_lurch_util_axc_log_func_info), 205 | cmocka_unit_test(test_lurch_util_axc_get_init_ctx_w_logging), 206 | cmocka_unit_test(test_lurch_util_axc_get_init_ctx_wo_logging), 207 | cmocka_unit_test(test_lurch_util_uname_strip), 208 | cmocka_unit_test(test_lurch_util_uname_strip_no_resource), 209 | cmocka_unit_test(test_lurch_util_uname_strip_empty), 210 | cmocka_unit_test(test_lurch_util_uname_get_db_fn), 211 | cmocka_unit_test(test_lurch_util_fp_get_printable), 212 | cmocka_unit_test(test_lurch_util_fp_get_printable_invalid) 213 | }; 214 | 215 | return cmocka_run_group_tests_name("lurch_util", tests, NULL, NULL); 216 | } --------------------------------------------------------------------------------