source: otr.c @ 3fb7139

Last change on this file since 3fb7139 was 2c81d15, checked in by dequis <dx@…>, at 2015-05-15T03:20:42Z

otr: Fix 'otr info' display problems

Patch by 'anonymous' from trac ticket 1150.

  • Property mode set to 100644
File size: 58.2 KB
RevLine 
[5ebff60]1/********************************************************************\
[e2b15bb]2  * BitlBee -- An IRC to other IM-networks gateway                     *
3  *                                                                    *
[0e788f5]4  * Copyright 2002-2012 Wilmer van der Gaast and others                *
[e2b15bb]5  \********************************************************************/
6
7/*
8  OTR support (cf. http://www.cypherpunks.ca/otr/)
[5ebff60]9
[81265e0]10  (c) 2008-2011,2013 Sven Moritz Hallberg <pesco@khjk.org>
11  funded by stonedcoder.org
[5ebff60]12
[e2b15bb]13  files used to store OTR data:
14    <configdir>/<nick>.otr_keys
15    <configdir>/<nick>.otr_fprints
[e4752a6]16    <configdir>/<nick>.otr_instags  <- don't copy this one between hosts
[5ebff60]17
[e2b15bb]18  top-level todos: (search for TODO for more ;-))
19    integrate otr_load/otr_save with existing storage backends
20    per-account policy settings
21    per-user policy settings
[e4752a6]22    add a way to select recipient instance
[e2b15bb]23*/
24
25/*
26  This program is free software; you can redistribute it and/or modify
27  it under the terms of the GNU General Public License as published by
28  the Free Software Foundation; either version 2 of the License, or
29  (at your option) any later version.
30
31  This program is distributed in the hope that it will be useful,
32  but WITHOUT ANY WARRANTY; without even the implied warranty of
33  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
34  GNU General Public License for more details.
35
36  You should have received a copy of the GNU General Public License with
37  the Debian GNU/Linux distribution in /usr/share/common-licenses/GPL;
[6f10697]38  if not, write to the Free Software Foundation, Inc., 51 Franklin St.,
39  Fifth Floor, Boston, MA  02110-1301  USA
[e2b15bb]40*/
41
[764c7d1]42#include "bitlbee.h"
43#include "irc.h"
44#include "otr.h"
45#include <sys/types.h>
[27db433]46#include <sys/wait.h>
[764c7d1]47#include <unistd.h>
[dc9797f]48#include <assert.h>
[6738a67]49#include <signal.h>
[764c7d1]50
51
52/** OTR interface routines for the OtrlMessageAppOps struct: **/
53
54OtrlPolicy op_policy(void *opdata, ConnContext *context);
55
56void op_create_privkey(void *opdata, const char *accountname, const char *protocol);
57
58int op_is_logged_in(void *opdata, const char *accountname, const char *protocol,
[5ebff60]59                    const char *recipient);
[764c7d1]60
61void op_inject_message(void *opdata, const char *accountname, const char *protocol,
[5ebff60]62                       const char *recipient, const char *message);
[764c7d1]63
64void op_new_fingerprint(void *opdata, OtrlUserState us, const char *accountname,
[5ebff60]65                        const char *protocol, const char *username, unsigned char fingerprint[20]);
[764c7d1]66
67void op_write_fingerprints(void *opdata);
68
69void op_gone_secure(void *opdata, ConnContext *context);
70
71void op_gone_insecure(void *opdata, ConnContext *context);
72
73void op_still_secure(void *opdata, ConnContext *context, int is_reply);
74
75void op_log_message(void *opdata, const char *message);
76
[a13855a]77int op_max_message_size(void *opdata, ConnContext *context);
[764c7d1]78
[a13855a]79const char *op_account_name(void *opdata, const char *account, const char *protocol);
[764c7d1]80
[e65039a]81void op_create_instag(void *opdata, const char *account, const char *protocol);
82
[5d2bc9d]83void op_convert_msg(void *opdata, ConnContext *ctx, OtrlConvertType typ,
[5ebff60]84                    char **dst, const char *src);
[5d2bc9d]85void op_convert_free(void *opdata, ConnContext *ctx, char *msg);
86
[352a6b0]87void op_handle_smp_event(void *opdata, OtrlSMPEvent ev, ConnContext *ctx,
[5ebff60]88                         unsigned short percent, char *question);
[764c7d1]89
[090c9b7]90void op_handle_msg_event(void *opdata, OtrlMessageEvent ev, ConnContext *ctx,
[5ebff60]91                         const char *message, gcry_error_t err);
[090c9b7]92
[6d9f0ba]93const char *op_otr_error_message(void *opdata, ConnContext *ctx,
[5ebff60]94                                 OtrlErrorCode err_code);
[6d9f0ba]95
[764c7d1]96/** otr sub-command handlers: **/
97
[8358691]98static void cmd_otr(irc_t *irc, char **args);
[8521b02]99void cmd_otr_connect(irc_t *irc, char **args);
100void cmd_otr_disconnect(irc_t *irc, char **args);
[69ef042]101void cmd_otr_reconnect(irc_t *irc, char **args);
[5a71d9c]102void cmd_otr_smp(irc_t *irc, char **args);
[99a01b9]103void cmd_otr_smpq(irc_t *irc, char **args);
[5a71d9c]104void cmd_otr_trust(irc_t *irc, char **args);
[764c7d1]105void cmd_otr_info(irc_t *irc, char **args);
[94e7eb3]106void cmd_otr_keygen(irc_t *irc, char **args);
[c595308]107void cmd_otr_forget(irc_t *irc, char **args);
[764c7d1]108
109const command_t otr_commands[] = {
[8521b02]110        { "connect",     1, &cmd_otr_connect,    0 },
111        { "disconnect",  1, &cmd_otr_disconnect, 0 },
[69ef042]112        { "reconnect",   1, &cmd_otr_reconnect,  0 },
[8521b02]113        { "smp",         2, &cmd_otr_smp,        0 },
[99a01b9]114        { "smpq",        3, &cmd_otr_smpq,       0 },
[8521b02]115        { "trust",       6, &cmd_otr_trust,      0 },
116        { "info",        0, &cmd_otr_info,       0 },
[94e7eb3]117        { "keygen",      1, &cmd_otr_keygen,     0 },
[c595308]118        { "forget",      2, &cmd_otr_forget,     0 },
[764c7d1]119        { NULL }
120};
121
[6738a67]122typedef struct {
123        void *fst;
124        void *snd;
[5ebff60]125} pair_t;
[6738a67]126
[0c85c08]127static OtrlMessageAppOps otr_ops;   /* collects interface functions required by OTR */
128
[764c7d1]129
130/** misc. helpers/subroutines: **/
131
[dc9797f]132/* check whether we are already generating a key for a given account */
133int keygen_in_progress(irc_t *irc, const char *handle, const char *protocol);
134
[522a00f]135/* start background process to generate a (new) key for a given account */
[764c7d1]136void otr_keygen(irc_t *irc, const char *handle, const char *protocol);
[c595308]137
[27db433]138/* main function for the forked keygen slave */
[12cc58b]139void keygen_child_main(OtrlUserState us, int infd, int outfd);
[27db433]140
[522a00f]141/* mainloop handler for when a keygen finishes */
[764c7d1]142gboolean keygen_finish_handler(gpointer data, gint fd, b_input_condition cond);
[c595308]143
[27db433]144/* copy the contents of file a to file b, overwriting it if it exists */
145void copyfile(const char *a, const char *b);
146
147/* read one line of input from a stream, excluding trailing newline */
148void myfgets(char *s, int size, FILE *stream);
149
[c595308]150/* some yes/no handlers */
[6738a67]151void yes_keygen(void *data);
152void yes_forget_fingerprint(void *data);
153void yes_forget_context(void *data);
154void yes_forget_key(void *data);
[764c7d1]155
[22ec21d]156/* timeout handler that calls otrl_message_poll */
157gboolean ev_message_poll(gpointer data, gint fd, b_input_condition cond);
158
[764c7d1]159/* helper to make sure accountname and protocol match the incoming "opdata" */
160struct im_connection *check_imc(void *opdata, const char *accountname,
[5ebff60]161                                const char *protocol);
[764c7d1]162
[5a71d9c]163/* determine the nick for a given handle/protocol pair
164   returns "handle/protocol" if not found */
[764c7d1]165const char *peernick(irc_t *irc, const char *handle, const char *protocol);
166
[8521b02]167/* turn a hexadecimal digit into its numerical value */
168int hexval(char a);
169
[ad2d8bc]170/* determine the irc_user_t for a given handle/protocol pair
[5a71d9c]171   returns NULL if not found */
[ad2d8bc]172irc_user_t *peeruser(irc_t *irc, const char *handle, const char *protocol);
[5a71d9c]173
[090c9b7]174/* show an otr-related message to the user */
175void display_otr_message(void *opdata, ConnContext *ctx, const char *fmt, ...);
176
177/* write an otr-related message to the system log */
178void log_otr_message(void *opdata, const char *fmt, ...);
179
[99a01b9]180/* combined handler for the 'otr smp' and 'otr smpq' commands */
[7c91392]181void otr_smp_or_smpq(irc_t *irc, const char *nick, const char *question,
[5ebff60]182                     const char *secret);
[99a01b9]183
[a0c6fc5]184/* update flags within the irc_user structure to reflect OTR status of context */
185void otr_update_uflags(ConnContext *context, irc_user_t *u);
186
[5a71d9c]187/* update op/voice flag of given user according to encryption state and settings
188   returns 0 if neither op_buddies nor voice_buddies is set to "encrypted",
189   i.e. msgstate should be announced seperately */
[ad2d8bc]190int otr_update_modeflags(irc_t *irc, irc_user_t *u);
[5a71d9c]191
[8521b02]192/* show general info about the OTR subsystem; called by 'otr info' */
193void show_general_otr_info(irc_t *irc);
194
[2c81d15]195/* show info about a given OTR context and subcontexts/instances. bestctx
196   may be either NULL or preferred destination context (this is hilighted
197   in the output as being the target for a message) */
198void show_otr_context_info(irc_t *irc, ConnContext *ctx, ConnContext *bestctx);
[8521b02]199
[764c7d1]200/* show the list of fingerprints associated with a given context */
201void show_fingerprints(irc_t *irc, ConnContext *ctx);
202
[c595308]203/* find a fingerprint by prefix (given as any number of hex strings) */
204Fingerprint *match_fingerprint(irc_t *irc, ConnContext *ctx, const char **args);
205
[5f4eede]206/* find a private key by fingerprint prefix (given as any number of hex strings) */
207OtrlPrivKey *match_privkey(irc_t *irc, const char **args);
208
[c7b94ef]209/* check whether a string is safe to use in a path component */
210int strsane(const char *s);
211
[c239fff]212/* close the OTR connection with the given buddy */
213gboolean otr_disconnect_user(irc_t *irc, irc_user_t *u);
214
215/* close all active OTR connections */
216void otr_disconnect_all(irc_t *irc);
217
[0c85c08]218/* functions to be called for certain events */
219static const struct irc_plugin otr_plugin;
220
[764c7d1]221/*** routines declared in otr.h: ***/
222
[858ea01]223#ifdef OTR_BI
224#define init_plugin otr_init
225#endif
226
227void init_plugin(void)
[764c7d1]228{
229        OTRL_INIT;
[5ebff60]230
[764c7d1]231        /* fill global OtrlMessageAppOps */
[0c85c08]232        otr_ops.policy = &op_policy;
233        otr_ops.create_privkey = &op_create_privkey;
234        otr_ops.is_logged_in = &op_is_logged_in;
235        otr_ops.inject_message = &op_inject_message;
236        otr_ops.update_context_list = NULL;
237        otr_ops.new_fingerprint = &op_new_fingerprint;
238        otr_ops.write_fingerprints = &op_write_fingerprints;
239        otr_ops.gone_secure = &op_gone_secure;
240        otr_ops.gone_insecure = &op_gone_insecure;
241        otr_ops.still_secure = &op_still_secure;
242        otr_ops.max_message_size = &op_max_message_size;
243        otr_ops.account_name = &op_account_name;
244        otr_ops.account_name_free = NULL;
[81265e0]245
246        /* stuff added with libotr 4.0.0 */
247        otr_ops.received_symkey = NULL;         /* we don't use the extra key */
[6d9f0ba]248        otr_ops.otr_error_message = &op_otr_error_message;
[81265e0]249        otr_ops.otr_error_message_free = NULL;
[6d9f0ba]250        otr_ops.resent_msg_prefix = NULL;       /* default: [resent] */
[81265e0]251        otr_ops.resent_msg_prefix_free = NULL;
[352a6b0]252        otr_ops.handle_smp_event = &op_handle_smp_event;
[090c9b7]253        otr_ops.handle_msg_event = &op_handle_msg_event;
[e65039a]254        otr_ops.create_instag = &op_create_instag;
[5d2bc9d]255        otr_ops.convert_msg = &op_convert_msg;
256        otr_ops.convert_free = &op_convert_free;
[5ebff60]257        otr_ops.timer_control = NULL;           /* we just poll */
258
259        root_command_add("otr", 1, cmd_otr, 0);
260        register_irc_plugin(&otr_plugin);
[764c7d1]261}
262
[0c85c08]263gboolean otr_irc_new(irc_t *irc)
[dc9797f]264{
[0c85c08]265        set_t *s;
266        GSList *l;
[5ebff60]267
[0c85c08]268        irc->otr = g_new0(otr_t, 1);
269        irc->otr->us = otrl_userstate_create();
[5ebff60]270
271        s = set_add(&irc->b->set, "otr_color_encrypted", "true", set_eval_bool, irc);
272
273        s = set_add(&irc->b->set, "otr_policy", "opportunistic", set_eval_list, irc);
274        l = g_slist_prepend(NULL, "never");
275        l = g_slist_prepend(l, "opportunistic");
276        l = g_slist_prepend(l, "manual");
277        l = g_slist_prepend(l, "always");
[0c85c08]278        s->eval_data = l;
[1082395]279
[5ebff60]280        s = set_add(&irc->b->set, "otr_does_html", "true", set_eval_bool, irc);
281
[22ec21d]282        /* regularly call otrl_message_poll */
283        gint definterval = otrl_message_poll_get_default_interval(irc->otr->us);
[c347a12]284        irc->otr->timer = b_timeout_add(definterval, ev_message_poll, irc->otr);
[22ec21d]285
[0c85c08]286        return TRUE;
[dc9797f]287}
288
[0c85c08]289void otr_irc_free(irc_t *irc)
[dc9797f]290{
[098a75b]291        set_t *s;
[0c85c08]292        otr_t *otr = irc->otr;
[5ebff60]293
[c239fff]294        otr_disconnect_all(irc);
[c347a12]295        b_event_remove(otr->timer);
[dc9797f]296        otrl_userstate_free(otr->us);
[098a75b]297
298        s = set_find(&irc->b->set, "otr_policy");
299        g_slist_free(s->eval_data);
300
[5ebff60]301        if (otr->keygen) {
[dc9797f]302                kill(otr->keygen, SIGTERM);
303                waitpid(otr->keygen, NULL, 0);
304                /* TODO: remove stale keygen tempfiles */
305        }
[5ebff60]306        if (otr->to) {
[dc9797f]307                fclose(otr->to);
[5ebff60]308        }
309        if (otr->from) {
[dc9797f]310                fclose(otr->from);
[5ebff60]311        }
312        while (otr->todo) {
[dc9797f]313                kg_t *p = otr->todo;
314                otr->todo = p->next;
315                g_free(p);
316        }
317        g_free(otr);
318}
319
[764c7d1]320void otr_load(irc_t *irc)
321{
322        char s[512];
323        account_t *a;
324        gcry_error_t e;
[522a00f]325        gcry_error_t enoent = gcry_error_from_errno(ENOENT);
[5ebff60]326        int kg = 0;
[764c7d1]327
[5ebff60]328        if (strsane(irc->user->nick)) {
[c7b94ef]329                g_snprintf(s, 511, "%s%s.otr_keys", global.conf->configdir, irc->user->nick);
330                e = otrl_privkey_read(irc->otr->us, s);
[5ebff60]331                if (e && e != enoent) {
[c7b94ef]332                        irc_rootmsg(irc, "otr load: %s: %s", s, gcry_strerror(e));
333                }
334                g_snprintf(s, 511, "%s%s.otr_fprints", global.conf->configdir, irc->user->nick);
335                e = otrl_privkey_read_fingerprints(irc->otr->us, s, NULL, NULL);
[5ebff60]336                if (e && e != enoent) {
[c7b94ef]337                        irc_rootmsg(irc, "otr load: %s: %s", s, gcry_strerror(e));
338                }
[e65039a]339                g_snprintf(s, 511, "%s%s.otr_instags", global.conf->configdir, irc->user->nick);
340                e = otrl_instag_read(irc->otr->us, s);
[5ebff60]341                if (e && e != enoent) {
[e65039a]342                        irc_rootmsg(irc, "otr load: %s: %s", s, gcry_strerror(e));
343                }
[764c7d1]344        }
[5ebff60]345
[764c7d1]346        /* check for otr keys on all accounts */
[5ebff60]347        for (a = irc->b->accounts; a; a = a->next) {
[3064ea4]348                kg = otr_check_for_key(a) || kg;
349        }
[5ebff60]350        if (kg) {
[e67e513]351                irc_rootmsg(irc, "Notice: "
[5ebff60]352                            "The accounts above do not have OTR encryption keys associated with them, yet. "
353                            "These keys are now being generated in the background. "
354                            "You will be notified as they are completed. "
355                            "It is not necessary to wait; "
356                            "BitlBee can be used normally during key generation. "
357                            "You may safely ignore this message if you don't know what OTR is. ;)");
[764c7d1]358        }
359}
360
361void otr_save(irc_t *irc)
362{
363        char s[512];
[3c80a9d]364        gcry_error_t e;
[764c7d1]365
[5ebff60]366        if (strsane(irc->user->nick)) {
[c7b94ef]367                g_snprintf(s, 511, "%s%s.otr_fprints", global.conf->configdir, irc->user->nick);
368                e = otrl_privkey_write_fingerprints(irc->otr->us, s);
[5ebff60]369                if (e) {
[c7b94ef]370                        irc_rootmsg(irc, "otr save: %s: %s", s, gcry_strerror(e));
371                }
372                chmod(s, 0600);
[3c80a9d]373        }
[764c7d1]374}
375
376void otr_remove(const char *nick)
377{
378        char s[512];
[5ebff60]379
380        if (strsane(nick)) {
[c7b94ef]381                g_snprintf(s, 511, "%s%s.otr_keys", global.conf->configdir, nick);
382                unlink(s);
383                g_snprintf(s, 511, "%s%s.otr_fprints", global.conf->configdir, nick);
384                unlink(s);
385        }
[764c7d1]386}
387
388void otr_rename(const char *onick, const char *nnick)
389{
390        char s[512], t[512];
[5ebff60]391
392        if (strsane(nnick) && strsane(onick)) {
[c7b94ef]393                g_snprintf(s, 511, "%s%s.otr_keys", global.conf->configdir, onick);
394                g_snprintf(t, 511, "%s%s.otr_keys", global.conf->configdir, nnick);
[5ebff60]395                rename(s, t);
[c7b94ef]396                g_snprintf(s, 511, "%s%s.otr_fprints", global.conf->configdir, onick);
397                g_snprintf(t, 511, "%s%s.otr_fprints", global.conf->configdir, nnick);
[5ebff60]398                rename(s, t);
[c7b94ef]399        }
[764c7d1]400}
401
[3064ea4]402int otr_check_for_key(account_t *a)
[764c7d1]403{
[ad2d8bc]404        irc_t *irc = a->bee->ui_data;
[a13855a]405        OtrlPrivKey *k;
[5ebff60]406
[1dd3470]407        /* don't do OTR on certain (not classic IM) protocols, e.g. twitter */
[5ebff60]408        if (a->prpl->options & OPT_NOOTR) {
[1dd3470]409                return 0;
410        }
[5ebff60]411
[dc9797f]412        k = otrl_privkey_find(irc->otr->us, a->user, a->prpl->name);
[5ebff60]413        if (k) {
[e67e513]414                irc_rootmsg(irc, "otr: %s/%s ready", a->user, a->prpl->name);
[3064ea4]415                return 0;
[5ebff60]416        }
417        if (keygen_in_progress(irc, a->user, a->prpl->name)) {
[e67e513]418                irc_rootmsg(irc, "otr: keygen for %s/%s already in progress", a->user, a->prpl->name);
[3064ea4]419                return 0;
[764c7d1]420        } else {
[e67e513]421                irc_rootmsg(irc, "otr: starting background keygen for %s/%s", a->user, a->prpl->name);
[764c7d1]422                otr_keygen(irc, a->user, a->prpl->name);
[3064ea4]423                return 1;
[764c7d1]424        }
425}
426
[934db064]427char *otr_filter_msg_in(irc_user_t *iu, char *msg, int flags)
[764c7d1]428{
429        int ignore_msg;
430        char *newmsg = NULL;
431        OtrlTLV *tlvs = NULL;
[934db064]432        irc_t *irc = iu->irc;
433        struct im_connection *ic = iu->bu->ic;
[5ebff60]434
[1dd3470]435        /* don't do OTR on certain (not classic IM) protocols, e.g. twitter */
[5ebff60]436        if (ic->acc->prpl->options & OPT_NOOTR) {
[934db064]437                return msg;
[1dd3470]438        }
[5ebff60]439
[0c85c08]440        ignore_msg = otrl_message_receiving(irc->otr->us, &otr_ops, ic,
[5ebff60]441                                            ic->acc->user, ic->acc->prpl->name, iu->bu->handle, msg, &newmsg,
442                                            &tlvs, NULL, NULL, NULL);
[764c7d1]443
[098a75b]444        if (tlvs) {
445                otrl_tlv_free(tlvs);
446        }
447
[5ebff60]448        if (ignore_msg) {
[764c7d1]449                /* this was an internal OTR protocol message */
450                return NULL;
[5ebff60]451        } else if (!newmsg) {
[764c7d1]452                /* this was a non-OTR message */
[da44b08]453                return msg;
[764c7d1]454        } else {
[da44b08]455                /* we're done with the original msg, which will be caller-freed. */
[5d2bc9d]456                return newmsg;
[764c7d1]457        }
458}
459
[934db064]460char *otr_filter_msg_out(irc_user_t *iu, char *msg, int flags)
[5ebff60]461{
[5a71d9c]462        int st;
463        char *otrmsg = NULL;
464        ConnContext *ctx = NULL;
[934db064]465        irc_t *irc = iu->irc;
466        struct im_connection *ic = iu->bu->ic;
[329f9fe]467        otrl_instag_t instag = OTRL_INSTAG_BEST; // XXX?
[5ebff60]468
[329f9fe]469        /* NB: in libotr 4.0.0 OTRL_INSTAG_RECENT will cause a null-pointer deref
470         * in otrl_message_sending with newly-added OTR contexts.
471         */
[1dd3470]472
473        /* don't do OTR on certain (not classic IM) protocols, e.g. twitter */
[5ebff60]474        if (ic->acc->prpl->options & OPT_NOOTR) {
[934db064]475                return msg;
[1dd3470]476        }
[1082395]477
[0c85c08]478        st = otrl_message_sending(irc->otr->us, &otr_ops, ic,
[5ebff60]479                                  ic->acc->user, ic->acc->prpl->name, iu->bu->handle, instag,
480                                  msg, NULL, &otrmsg, OTRL_FRAGMENT_SEND_ALL_BUT_LAST, &ctx, NULL, NULL);
[367ea3c]481
[5ebff60]482        if (otrmsg && otrmsg != msg) {
[367ea3c]483                /* libotr wants us to replace our message */
484                /* NB: caller will free old msg */
[098a75b]485                msg = st ? NULL : g_strdup(otrmsg);
[367ea3c]486                otrl_message_free(otrmsg);
[764c7d1]487        }
[5ebff60]488
489        if (st) {
[367ea3c]490                irc_usernotice(iu, "otr: error handling outgoing message: %d", st);
[5ebff60]491                msg = NULL;     /* do not send plaintext! */
[cc17b76]492        }
[367ea3c]493
494        return msg;
[764c7d1]495}
496
[0c85c08]497static const struct irc_plugin otr_plugin =
498{
499        otr_irc_new,
500        otr_irc_free,
[934db064]501        otr_filter_msg_out,
502        otr_filter_msg_in,
[2dcaf9a]503        otr_load,
504        otr_save,
505        otr_remove,
[0c85c08]506};
507
[8358691]508static void cmd_otr(irc_t *irc, char **args)
[764c7d1]509{
510        const command_t *cmd;
[5ebff60]511
512        if (!args[0]) {
[764c7d1]513                return;
[5ebff60]514        }
515
516        if (!args[1]) {
[764c7d1]517                return;
[5ebff60]518        }
519
520        for (cmd = otr_commands; cmd->command; cmd++) {
521                if (strcmp(cmd->command, args[1]) == 0) {
[764c7d1]522                        break;
[5ebff60]523                }
[764c7d1]524        }
[5ebff60]525
526        if (!cmd->command) {
[e67e513]527                irc_rootmsg(irc, "%s: unknown subcommand \"%s\", see \x02help otr\x02",
[5ebff60]528                            args[0], args[1]);
[764c7d1]529                return;
530        }
[5ebff60]531
532        if (!args[cmd->required_parameters + 1]) {
[e67e513]533                irc_rootmsg(irc, "%s %s: not enough arguments (%d req.)",
[5ebff60]534                            args[0], args[1], cmd->required_parameters);
[764c7d1]535                return;
536        }
[5ebff60]537
538        cmd->execute(irc, args + 1);
[764c7d1]539}
540
541
542/*** OTR "MessageAppOps" callbacks for global.otr_ui: ***/
543
544OtrlPolicy op_policy(void *opdata, ConnContext *context)
545{
[e2b15bb]546        struct im_connection *ic = check_imc(opdata, context->accountname, context->protocol);
[ad2d8bc]547        irc_t *irc = ic->bee->ui_data;
[e2b15bb]548        const char *p;
[5ebff60]549
[dc9797f]550        /* policy override during keygen: if we're missing the key for context but are currently
551           generating it, then that's as much as we can do. => temporarily return NEVER. */
[5ebff60]552        if (keygen_in_progress(irc, context->accountname, context->protocol) &&
553            !otrl_privkey_find(irc->otr->us, context->accountname, context->protocol)) {
[dc9797f]554                return OTRL_POLICY_NEVER;
[5ebff60]555        }
[e2b15bb]556
[ad2d8bc]557        p = set_getstr(&ic->bee->set, "otr_policy");
[5ebff60]558        if (!strcmp(p, "never")) {
[e2b15bb]559                return OTRL_POLICY_NEVER;
[5ebff60]560        }
561        if (!strcmp(p, "opportunistic")) {
[e2b15bb]562                return OTRL_POLICY_OPPORTUNISTIC;
[5ebff60]563        }
564        if (!strcmp(p, "manual")) {
[e2b15bb]565                return OTRL_POLICY_MANUAL;
[5ebff60]566        }
567        if (!strcmp(p, "always")) {
[e2b15bb]568                return OTRL_POLICY_ALWAYS;
[5ebff60]569        }
570
[764c7d1]571        return OTRL_POLICY_OPPORTUNISTIC;
572}
573
574void op_create_privkey(void *opdata, const char *accountname,
[5ebff60]575                       const char *protocol)
[764c7d1]576{
577        struct im_connection *ic = check_imc(opdata, accountname, protocol);
[ad2d8bc]578        irc_t *irc = ic->bee->ui_data;
[5ebff60]579
[dc9797f]580        /* will fail silently if keygen already in progress */
[ad2d8bc]581        otr_keygen(irc, accountname, protocol);
[764c7d1]582}
583
584int op_is_logged_in(void *opdata, const char *accountname,
[5ebff60]585                    const char *protocol, const char *recipient)
[764c7d1]586{
587        struct im_connection *ic = check_imc(opdata, accountname, protocol);
[ad2d8bc]588        bee_user_t *bu;
[764c7d1]589
[ad2d8bc]590        /* lookup the irc_user_t for the given recipient */
591        bu = bee_user_by_handle(ic->bee, ic, recipient);
[5ebff60]592        if (bu) {
593                if (bu->flags & BEE_USER_ONLINE) {
[764c7d1]594                        return 1;
[5ebff60]595                } else {
[764c7d1]596                        return 0;
[5ebff60]597                }
[764c7d1]598        } else {
599                return -1;
600        }
601}
602
603void op_inject_message(void *opdata, const char *accountname,
[5ebff60]604                       const char *protocol, const char *recipient, const char *message)
[764c7d1]605{
606        struct im_connection *ic = check_imc(opdata, accountname, protocol);
[ad2d8bc]607        irc_t *irc = ic->bee->ui_data;
[764c7d1]608
609        if (strcmp(accountname, recipient) == 0) {
610                /* huh? injecting messages to myself? */
[e67e513]611                irc_rootmsg(irc, "note to self: %s", message);
[764c7d1]612        } else {
613                /* need to drop some consts here :-( */
614                /* TODO: get flags into op_inject_message?! */
[5ebff60]615                ic->acc->prpl->buddy_msg(ic, (char *) recipient, (char *) message, 0);
[764c7d1]616                /* ignoring return value :-/ */
617        }
618}
619
620void op_new_fingerprint(void *opdata, OtrlUserState us,
[5ebff60]621                        const char *accountname, const char *protocol,
622                        const char *username, unsigned char fingerprint[20])
[764c7d1]623{
624        struct im_connection *ic = check_imc(opdata, accountname, protocol);
[ad2d8bc]625        irc_t *irc = ic->bee->ui_data;
[409c2de]626        irc_user_t *u = peeruser(irc, username, protocol);
[5ebff60]627        char hunam[45];         /* anybody looking? ;-) */
628
[764c7d1]629        otrl_privkey_hash_to_human(hunam, fingerprint);
[5ebff60]630        if (u) {
[409c2de]631                irc_usernotice(u, "new fingerprint: %s", hunam);
632        } else {
633                /* this case shouldn't normally happen */
634                irc_rootmsg(irc, "new fingerprint for %s/%s: %s",
[5ebff60]635                            username, protocol, hunam);
[409c2de]636        }
[764c7d1]637}
638
639void op_write_fingerprints(void *opdata)
640{
[5ebff60]641        struct im_connection *ic = (struct im_connection *) opdata;
[ad2d8bc]642        irc_t *irc = ic->bee->ui_data;
[764c7d1]643
[ad2d8bc]644        otr_save(irc);
[764c7d1]645}
646
647void op_gone_secure(void *opdata, ConnContext *context)
648{
649        struct im_connection *ic =
[5ebff60]650                check_imc(opdata, context->accountname, context->protocol);
[ad2d8bc]651        irc_user_t *u;
652        irc_t *irc = ic->bee->ui_data;
[764c7d1]653
[ad2d8bc]654        u = peeruser(irc, context->username, context->protocol);
[5ebff60]655        if (!u) {
[5a71d9c]656                log_message(LOGLVL_ERROR,
[5ebff60]657                            "BUG: otr.c: op_gone_secure: irc_user_t for %s/%s/%s not found!",
658                            context->username, context->protocol, context->accountname);
[5a71d9c]659                return;
660        }
[5ebff60]661
[a0c6fc5]662        otr_update_uflags(context, u);
[5ebff60]663        if (!otr_update_modeflags(irc, u)) {
[f1cf01c]664                char *trust = u->flags & IRC_USER_OTR_TRUSTED ? "trusted" : "untrusted!";
[409c2de]665                irc_usernotice(u, "conversation is now off the record (%s)", trust);
[f1cf01c]666        }
[764c7d1]667}
668
669void op_gone_insecure(void *opdata, ConnContext *context)
670{
671        struct im_connection *ic =
[5ebff60]672                check_imc(opdata, context->accountname, context->protocol);
[ad2d8bc]673        irc_t *irc = ic->bee->ui_data;
674        irc_user_t *u;
[764c7d1]675
[ad2d8bc]676        u = peeruser(irc, context->username, context->protocol);
[5ebff60]677        if (!u) {
[5a71d9c]678                log_message(LOGLVL_ERROR,
[5ebff60]679                            "BUG: otr.c: op_gone_insecure: irc_user_t for %s/%s/%s not found!",
680                            context->username, context->protocol, context->accountname);
[5a71d9c]681                return;
682        }
[a0c6fc5]683        otr_update_uflags(context, u);
[5ebff60]684        if (!otr_update_modeflags(irc, u)) {
[409c2de]685                irc_usernotice(u, "conversation is now in cleartext");
[5ebff60]686        }
[764c7d1]687}
688
689void op_still_secure(void *opdata, ConnContext *context, int is_reply)
690{
691        struct im_connection *ic =
[5ebff60]692                check_imc(opdata, context->accountname, context->protocol);
[ad2d8bc]693        irc_t *irc = ic->bee->ui_data;
694        irc_user_t *u;
[764c7d1]695
[ad2d8bc]696        u = peeruser(irc, context->username, context->protocol);
[5ebff60]697        if (!u) {
[5a71d9c]698                log_message(LOGLVL_ERROR,
[5ebff60]699                            "BUG: otr.c: op_still_secure: irc_user_t for %s/%s/%s not found!",
700                            context->username, context->protocol, context->accountname);
[5a71d9c]701                return;
702        }
[a0c6fc5]703
704        otr_update_uflags(context, u);
[5ebff60]705        if (!otr_update_modeflags(irc, u)) {
[f1cf01c]706                char *trust = u->flags & IRC_USER_OTR_TRUSTED ? "trusted" : "untrusted!";
[409c2de]707                irc_usernotice(u, "otr connection has been refreshed (%s)", trust);
[f1cf01c]708        }
[764c7d1]709}
710
[a13855a]711int op_max_message_size(void *opdata, ConnContext *context)
712{
[5a71d9c]713        struct im_connection *ic =
[5ebff60]714                check_imc(opdata, context->accountname, context->protocol);
715
[5a71d9c]716        return ic->acc->prpl->mms;
[a13855a]717}
718
719const char *op_account_name(void *opdata, const char *account, const char *protocol)
720{
[5ebff60]721        struct im_connection *ic = (struct im_connection *) opdata;
[ad2d8bc]722        irc_t *irc = ic->bee->ui_data;
[a13855a]723
[ad2d8bc]724        return peernick(irc, account, protocol);
[a13855a]725}
726
[e65039a]727void op_create_instag(void *opdata, const char *account, const char *protocol)
728{
729        struct im_connection *ic =
[5ebff60]730                check_imc(opdata, account, protocol);
[e65039a]731        irc_t *irc = ic->bee->ui_data;
732        gcry_error_t e;
733        char s[512];
[5ebff60]734
[e65039a]735        g_snprintf(s, 511, "%s%s.otr_instags", global.conf->configdir,
[5ebff60]736                   irc->user->nick);
[e65039a]737        e = otrl_instag_generate(irc->otr->us, s, account, protocol);
[5ebff60]738        if (e) {
[e65039a]739                irc_rootmsg(irc, "otr: %s/%s: otrl_instag_generate failed: %s",
[5ebff60]740                            account, protocol, gcry_strerror(e));
[e65039a]741        }
742}
743
[5d2bc9d]744void op_convert_msg(void *opdata, ConnContext *ctx, OtrlConvertType typ,
[5ebff60]745                    char **dst, const char *src)
[5d2bc9d]746{
747        struct im_connection *ic =
[5ebff60]748                check_imc(opdata, ctx->accountname, ctx->protocol);
[5d2bc9d]749        irc_t *irc = ic->bee->ui_data;
750        irc_user_t *iu = peeruser(irc, ctx->username, ctx->protocol);
751
[5ebff60]752        if (typ == OTRL_CONVERT_RECEIVING) {
[5d2bc9d]753                char *msg = g_strdup(src);
[74c9e7f]754                char *buf = msg;
[5d2bc9d]755
756                /* HTML decoding */
[5ebff60]757                if (set_getbool(&ic->bee->set, "otr_does_html") &&
758                    !(ic->flags & OPT_DOES_HTML) &&
759                    set_getbool(&ic->bee->set, "strip_html")) {
[5d2bc9d]760                        strip_html(msg);
761                        *dst = msg;
762                }
763
764                /* coloring */
[5ebff60]765                if (set_getbool(&ic->bee->set, "otr_color_encrypted")) {
[5d2bc9d]766                        int color;                /* color according to f'print trust */
[5ebff60]767                        char *pre = "", *sep = "";    /* optional parts */
[5d2bc9d]768                        const char *trust = ctx->active_fingerprint->trust;
769
[5ebff60]770                        if (trust && trust[0] != '\0') {
771                                color = 3;   /* green */
772                        } else {
773                                color = 5;   /* red */
[5d2bc9d]774
[5ebff60]775                        }
[5d2bc9d]776                        /* in a query window, keep "/me " uncolored at the beginning */
[5ebff60]777                        if (g_strncasecmp(msg, "/me ", 4) == 0
778                            && irc_user_msgdest(iu) == irc->user->nick) {
[5d2bc9d]779                                msg += 4;  /* skip */
780                                pre = "/me ";
781                        }
782
783                        /* comma in first place could mess with the color code */
[5ebff60]784                        if (msg[0] == ',') {
785                                /* insert a space between color spec and message */
786                                sep = " ";
[5d2bc9d]787                        }
788
789                        *dst = g_strdup_printf("%s\x03%.2d%s%s\x0F", pre,
[5ebff60]790                                               color, sep, msg);
[74c9e7f]791                        g_free(buf);
[5d2bc9d]792                }
793        } else {
794                /* HTML encoding */
795                /* consider OTR plaintext to be HTML if otr_does_html is set */
[5ebff60]796                if (ctx && ctx->msgstate == OTRL_MSGSTATE_ENCRYPTED &&
797                    set_getbool(&ic->bee->set, "otr_does_html") &&
798                    (g_strncasecmp(src, "<html>", 6) != 0)) {
[5d2bc9d]799                        *dst = escape_html(src);
800                }
801        }
802}
803
804void op_convert_free(void *opdata, ConnContext *ctx, char *msg)
805{
806        g_free(msg);
807}
[5ebff60]808
[352a6b0]809/* Socialist Millionaires' Protocol */
810void op_handle_smp_event(void *opdata, OtrlSMPEvent ev, ConnContext *ctx,
[5ebff60]811                         unsigned short percent, char *question)
[352a6b0]812{
813        struct im_connection *ic =
[5ebff60]814                check_imc(opdata, ctx->accountname, ctx->protocol);
[352a6b0]815        irc_t *irc = ic->bee->ui_data;
816        OtrlUserState us = irc->otr->us;
817        irc_user_t *u = peeruser(irc, ctx->username, ctx->protocol);
818
[5ebff60]819        if (!u) {
820                return;
821        }
[352a6b0]822
[5ebff60]823        switch (ev) {
[352a6b0]824        case OTRL_SMPEVENT_ASK_FOR_SECRET:
825                irc_rootmsg(irc, "smp: initiated by %s"
[5ebff60]826                            " - respond with \x02otr smp %s <secret>\x02",
827                            u->nick, u->nick);
[352a6b0]828                break;
829        case OTRL_SMPEVENT_ASK_FOR_ANSWER:
830                irc_rootmsg(irc, "smp: initiated by %s with question: \x02\"%s\"\x02", u->nick,
[5ebff60]831                            question);
[352a6b0]832                irc_rootmsg(irc, "smp: respond with \x02otr smp %s <answer>\x02",
[5ebff60]833                            u->nick);
[352a6b0]834                break;
835        case OTRL_SMPEVENT_CHEATED:
836                irc_rootmsg(irc, "smp %s: opponent violated protocol, aborting",
[5ebff60]837                            u->nick);
[352a6b0]838                otrl_message_abort_smp(us, &otr_ops, u->bu->ic, ctx);
839                otrl_sm_state_free(ctx->smstate);
840                break;
841        case OTRL_SMPEVENT_NONE:
842                break;
843        case OTRL_SMPEVENT_IN_PROGRESS:
844                break;
845        case OTRL_SMPEVENT_SUCCESS:
[5ebff60]846                if (ctx->smstate->received_question) {
[352a6b0]847                        irc_rootmsg(irc, "smp %s: correct answer, you are trusted",
[5ebff60]848                                    u->nick);
[352a6b0]849                } else {
850                        irc_rootmsg(irc, "smp %s: secrets proved equal, fingerprint trusted",
[5ebff60]851                                    u->nick);
[352a6b0]852                }
853                otrl_sm_state_free(ctx->smstate);
854                break;
855        case OTRL_SMPEVENT_FAILURE:
[5ebff60]856                if (ctx->smstate->received_question) {
[352a6b0]857                        irc_rootmsg(irc, "smp %s: wrong answer, you are not trusted",
[5ebff60]858                                    u->nick);
[352a6b0]859                } else {
860                        irc_rootmsg(irc, "smp %s: secrets did not match, fingerprint not trusted",
[5ebff60]861                                    u->nick);
[352a6b0]862                }
863                otrl_sm_state_free(ctx->smstate);
864                break;
865        case OTRL_SMPEVENT_ABORT:
[5ebff60]866                irc_rootmsg(irc, "smp: received abort from %s", u->nick);
[352a6b0]867                otrl_sm_state_free(ctx->smstate);
868                break;
869        case OTRL_SMPEVENT_ERROR:
870                irc_rootmsg(irc, "smp %s: protocol error, aborting",
[5ebff60]871                            u->nick);
[352a6b0]872                otrl_message_abort_smp(us, &otr_ops, u->bu->ic, ctx);
873                otrl_sm_state_free(ctx->smstate);
874                break;
875        }
876}
877
[090c9b7]878void op_handle_msg_event(void *opdata, OtrlMessageEvent ev, ConnContext *ctx,
[5ebff60]879                         const char *message, gcry_error_t err)
[090c9b7]880{
[5ebff60]881        switch (ev) {
[090c9b7]882        case OTRL_MSGEVENT_ENCRYPTION_REQUIRED:
883                display_otr_message(opdata, ctx,
[5ebff60]884                                    "policy requires encryption - message not sent");
[090c9b7]885                break;
886        case OTRL_MSGEVENT_ENCRYPTION_ERROR:
887                display_otr_message(opdata, ctx,
[5ebff60]888                                    "error during encryption - message not sent");
[090c9b7]889                break;
890        case OTRL_MSGEVENT_CONNECTION_ENDED:
891                display_otr_message(opdata, ctx,
[5ebff60]892                                    "other end has disconnected OTR - "
893                                    "close connection or reconnect!");
[090c9b7]894                break;
895        case OTRL_MSGEVENT_SETUP_ERROR:
896                display_otr_message(opdata, ctx,
[5ebff60]897                                    "OTR connection failed: %s", gcry_strerror(err));
[090c9b7]898                break;
899        case OTRL_MSGEVENT_MSG_REFLECTED:
900                display_otr_message(opdata, ctx,
[5ebff60]901                                    "received our own OTR message (!?)");
[090c9b7]902                break;
903        case OTRL_MSGEVENT_MSG_RESENT:
904                display_otr_message(opdata, ctx,
[5ebff60]905                                    "the previous message was resent");
[090c9b7]906                break;
907        case OTRL_MSGEVENT_RCVDMSG_NOT_IN_PRIVATE:
908                display_otr_message(opdata, ctx,
[5ebff60]909                                    "unexpected encrypted message received");
[090c9b7]910                break;
911        case OTRL_MSGEVENT_RCVDMSG_UNREADABLE:
912                display_otr_message(opdata, ctx,
[5ebff60]913                                    "unreadable encrypted message received");
[090c9b7]914                break;
915        case OTRL_MSGEVENT_RCVDMSG_MALFORMED:
916                display_otr_message(opdata, ctx,
[5ebff60]917                                    "malformed OTR message received");
[090c9b7]918                break;
[51f937e]919        case OTRL_MSGEVENT_LOG_HEARTBEAT_RCVD:
[5ebff60]920                if (global.conf->verbose) {
[51f937e]921                        log_otr_message(opdata, "%s/%s: heartbeat received",
[5ebff60]922                                        ctx->accountname, ctx->protocol);
[51f937e]923                }
924                break;
925        case OTRL_MSGEVENT_LOG_HEARTBEAT_SENT:
[5ebff60]926                if (global.conf->verbose) {
[51f937e]927                        log_otr_message(opdata, "%s/%s: heartbeat sent",
[5ebff60]928                                        ctx->accountname, ctx->protocol);
[51f937e]929                }
930                break;
[090c9b7]931        case OTRL_MSGEVENT_RCVDMSG_GENERAL_ERR:
932                display_otr_message(opdata, ctx,
[5ebff60]933                                    "OTR error message received: %s", message);
[090c9b7]934                break;
935        case OTRL_MSGEVENT_RCVDMSG_UNENCRYPTED:
936                display_otr_message(opdata, ctx,
[5ebff60]937                                    "unencrypted message received: %s", message);
[090c9b7]938                break;
939        case OTRL_MSGEVENT_RCVDMSG_UNRECOGNIZED:
940                display_otr_message(opdata, ctx,
[5ebff60]941                                    "unrecognized OTR message received");
[090c9b7]942                break;
[e4752a6]943        case OTRL_MSGEVENT_RCVDMSG_FOR_OTHER_INSTANCE:
944                display_otr_message(opdata, ctx,
[5ebff60]945                                    "OTR message for a different instance received");
[e4752a6]946                break;
[090c9b7]947        default:
[51f937e]948                /* shouldn't happen */
[090c9b7]949                break;
950        }
951}
952
[6d9f0ba]953const char *op_otr_error_message(void *opdata, ConnContext *ctx,
[5ebff60]954                                 OtrlErrorCode err_code)
[6d9f0ba]955{
[5ebff60]956        switch (err_code) {
[6d9f0ba]957        case OTRL_ERRCODE_ENCRYPTION_ERROR:
958                return "i failed to encrypt a message";
959        case OTRL_ERRCODE_MSG_NOT_IN_PRIVATE:
960                return "you sent an encrypted message i didn't expect";
961        case OTRL_ERRCODE_MSG_UNREADABLE:
962                return "could not read encrypted message";
963        case OTRL_ERRCODE_MSG_MALFORMED:
964                return "you sent a malformed OTR message";
965        default:
966                return "i suffered an unexpected OTR error";
967        }
968}
969
[5d2bc9d]970
[764c7d1]971
972/*** OTR sub-command handlers ***/
973
[69ef042]974void cmd_otr_reconnect(irc_t *irc, char **args)
975{
976        cmd_otr_disconnect(irc, args);
977        cmd_otr_connect(irc, args);
978}
979
[8521b02]980void cmd_otr_disconnect(irc_t *irc, char **args)
[764c7d1]981{
[ad2d8bc]982        irc_user_t *u;
[764c7d1]983
[5ebff60]984        if (!strcmp("*", args[1])) {
[c239fff]985                otr_disconnect_all(irc);
986                irc_rootmsg(irc, "all conversations are now in cleartext");
987        } else {
988                u = irc_user_by_name(irc, args[1]);
[5ebff60]989                if (otr_disconnect_user(irc, u)) {
[c239fff]990                        irc_usernotice(u, "conversation is now in cleartext");
[5ebff60]991                } else {
[c239fff]992                        irc_rootmsg(irc, "%s: unknown user", args[1]);
[5ebff60]993                }
[c595308]994        }
[764c7d1]995}
996
[8521b02]997void cmd_otr_connect(irc_t *irc, char **args)
[764c7d1]998{
[ad2d8bc]999        irc_user_t *u;
[820a2a7]1000        char *msg, *query = "?OTR?";
[764c7d1]1001
[ad2d8bc]1002        u = irc_user_by_name(irc, args[1]);
[5ebff60]1003        if (!u || !u->bu || !u->bu->ic) {
[e67e513]1004                irc_rootmsg(irc, "%s: unknown user", args[1]);
[764c7d1]1005                return;
1006        }
[5ebff60]1007        if (!(u->bu->flags & BEE_USER_ONLINE)) {
[e67e513]1008                irc_rootmsg(irc, "%s is offline", args[1]);
[764c7d1]1009                return;
1010        }
[5ebff60]1011
[fbcb481]1012        /* passing this through the filter so it goes through libotr which
1013         * will replace the simple query string with a proper one */
[820a2a7]1014        msg = otr_filter_msg_out(u, query, 0);
1015
1016        /* send the message */
[5ebff60]1017        if (msg) {
[820a2a7]1018                u->bu->ic->acc->prpl->buddy_msg(u->bu->ic, u->bu->handle, msg, 0);  /* XXX flags? */
1019                /* XXX error message? */
1020
[5ebff60]1021                if (msg != query) {
[820a2a7]1022                        g_free(msg);
[5ebff60]1023                }
[820a2a7]1024        }
[764c7d1]1025}
1026
[5a71d9c]1027void cmd_otr_smp(irc_t *irc, char **args)
[764c7d1]1028{
[5ebff60]1029        otr_smp_or_smpq(irc, args[1], NULL, args[2]);   /* no question */
[99a01b9]1030}
[764c7d1]1031
[99a01b9]1032void cmd_otr_smpq(irc_t *irc, char **args)
1033{
[7c91392]1034        otr_smp_or_smpq(irc, args[1], args[2], args[3]);
[764c7d1]1035}
1036
[5a71d9c]1037void cmd_otr_trust(irc_t *irc, char **args)
1038{
[ad2d8bc]1039        irc_user_t *u;
[5a71d9c]1040        ConnContext *ctx;
1041        unsigned char raw[20];
1042        Fingerprint *fp;
[5ebff60]1043        int i, j;
1044
[ad2d8bc]1045        u = irc_user_by_name(irc, args[1]);
[5ebff60]1046        if (!u || !u->bu || !u->bu->ic) {
[e67e513]1047                irc_rootmsg(irc, "%s: unknown user", args[1]);
[5a71d9c]1048                return;
1049        }
[5ebff60]1050
[ad2d8bc]1051        ctx = otrl_context_find(irc->otr->us, u->bu->handle,
[5ebff60]1052                                u->bu->ic->acc->user, u->bu->ic->acc->prpl->name, OTRL_INSTAG_MASTER, 0, NULL, NULL,
1053                                NULL);
1054        if (!ctx) {
[e67e513]1055                irc_rootmsg(irc, "%s: no otr context with user", args[1]);
[5a71d9c]1056                return;
1057        }
[5ebff60]1058
[5a71d9c]1059        /* convert given fingerprint to raw representation */
[5ebff60]1060        for (i = 0; i < 5; i++) {
1061                for (j = 0; j < 4; j++) {
1062                        char *p = args[2 + i] + (2 * j);
1063                        char *q = p + 1;
[5a71d9c]1064                        int x, y;
[5ebff60]1065
1066                        if (!*p || !*q) {
1067                                irc_rootmsg(irc, "failed: truncated fingerprint block %d", i + 1);
[5a71d9c]1068                                return;
1069                        }
[5ebff60]1070
[5a71d9c]1071                        x = hexval(*p);
1072                        y = hexval(*q);
[5ebff60]1073                        if (x < 0) {
1074                                irc_rootmsg(irc, "failed: %d. hex digit of block %d out of range", 2 * j + 1, i + 1);
[5a71d9c]1075                                return;
1076                        }
[5ebff60]1077                        if (y < 0) {
1078                                irc_rootmsg(irc, "failed: %d. hex digit of block %d out of range", 2 * j + 2, i + 1);
[5a71d9c]1079                                return;
1080                        }
1081
[5ebff60]1082                        raw[i * 4 + j] = x * 16 + y;
[5a71d9c]1083                }
1084        }
1085        fp = otrl_context_find_fingerprint(ctx, raw, 0, NULL);
[5ebff60]1086        if (!fp) {
[e67e513]1087                irc_rootmsg(irc, "failed: no such fingerprint for %s", args[1]);
[5a71d9c]1088        } else {
1089                char *trust = args[7] ? args[7] : "affirmed";
1090                otrl_context_set_trust(fp, trust);
[e67e513]1091                irc_rootmsg(irc, "fingerprint match, trust set to \"%s\"", trust);
[5ebff60]1092                if (u->flags & IRC_USER_OTR_ENCRYPTED) {
[ad2d8bc]1093                        u->flags |= IRC_USER_OTR_TRUSTED;
[5ebff60]1094                }
[5a71d9c]1095                otr_update_modeflags(irc, u);
1096        }
1097}
1098
[8521b02]1099void cmd_otr_info(irc_t *irc, char **args)
[764c7d1]1100{
[5ebff60]1101        if (!args[1]) {
[8521b02]1102                show_general_otr_info(irc);
[764c7d1]1103        } else {
[8521b02]1104                char *arg = g_strdup(args[1]);
[5ebff60]1105                char *myhandle, *handle = NULL, *protocol;
[2c81d15]1106                ConnContext *bestctx = NULL, *ctx;
[5ebff60]1107
[8521b02]1108                /* interpret arg as 'user/protocol/account' if possible */
1109                protocol = strchr(arg, '/');
[9e64011]1110                myhandle = NULL;
[5ebff60]1111                if (protocol) {
[8521b02]1112                        *(protocol++) = '\0';
1113                        myhandle = strchr(protocol, '/');
[764c7d1]1114                }
[5ebff60]1115                if (protocol && myhandle) {
[8521b02]1116                        *(myhandle++) = '\0';
1117                        handle = arg;
[5ebff60]1118                        ctx = otrl_context_find(irc->otr->us, handle, myhandle, protocol, OTRL_INSTAG_MASTER, 0, NULL,
1119                                                NULL, NULL);
1120                        if (!ctx) {
[e67e513]1121                                irc_rootmsg(irc, "no such context");
[8521b02]1122                                g_free(arg);
1123                                return;
1124                        }
[764c7d1]1125                } else {
[ad2d8bc]1126                        irc_user_t *u = irc_user_by_name(irc, args[1]);
[5ebff60]1127                        if (!u || !u->bu || !u->bu->ic) {
[e67e513]1128                                irc_rootmsg(irc, "%s: unknown user", args[1]);
[8521b02]1129                                g_free(arg);
1130                                return;
[5a71d9c]1131                        }
[ad2d8bc]1132                        ctx = otrl_context_find(irc->otr->us, u->bu->handle, u->bu->ic->acc->user,
[5ebff60]1133                                                u->bu->ic->acc->prpl->name, OTRL_INSTAG_MASTER, 0, NULL, NULL, NULL);
1134                        if (!ctx) {
[e67e513]1135                                irc_rootmsg(irc, "no otr context with %s", args[1]);
[8521b02]1136                                g_free(arg);
1137                                return;
1138                        }
[2c81d15]1139                        /* This does no harm if it returns NULL */
1140                        bestctx = otrl_context_find(irc->otr->us, u->bu->handle, u->bu->ic->acc->user,
1141                                                    u->bu->ic->acc->prpl->name, OTRL_INSTAG_BEST, 0, NULL, NULL, NULL);
[8521b02]1142                }
[5ebff60]1143
[8521b02]1144                /* show how we resolved the (nick) argument, if we did */
[5ebff60]1145                if (handle != arg) {
[2c81d15]1146                        irc_rootmsg(irc, "%s:", args[1]);
1147                        irc_rootmsg(irc, "  they are: %s/%s", ctx->username, ctx->protocol);
1148                        irc_rootmsg(irc, "  we are: %s/%s", ctx->accountname, ctx->protocol);
[764c7d1]1149                }
[2c81d15]1150                show_otr_context_info(irc, ctx, bestctx);
[8521b02]1151                g_free(arg);
[764c7d1]1152        }
1153}
1154
[94e7eb3]1155void cmd_otr_keygen(irc_t *irc, char **args)
1156{
1157        account_t *a;
[5ebff60]1158
[eabe6d4]1159        if ((a = account_get(irc->b, args[1])) == NULL) {
1160                irc_rootmsg(irc, "Could not find account `%s'.", args[1]);
[94e7eb3]1161                return;
1162        }
[5ebff60]1163
1164        if (keygen_in_progress(irc, a->user, a->prpl->name)) {
[eabe6d4]1165                irc_rootmsg(irc, "keygen for account `%s' already in progress", a->tag);
[dc9797f]1166                return;
1167        }
[5ebff60]1168
1169        if (otrl_privkey_find(irc->otr->us, a->user, a->prpl->name)) {
[eabe6d4]1170                char *s = g_strdup_printf("account `%s' already has a key, replace it?", a->tag);
[ad2d8bc]1171                query_add(irc, NULL, s, yes_keygen, NULL, NULL, a);
[82e8fe8]1172                g_free(s);
[94e7eb3]1173        } else {
1174                otr_keygen(irc, a->user, a->prpl->name);
1175        }
1176}
1177
[6738a67]1178void yes_forget_fingerprint(void *data)
[c595308]1179{
[5ebff60]1180        pair_t *p = (pair_t *) data;
1181        irc_t *irc = (irc_t *) p->fst;
1182        Fingerprint *fp = (Fingerprint *) p->snd;
[6738a67]1183
1184        g_free(p);
[5ebff60]1185
1186        if (fp == fp->context->active_fingerprint) {
[e67e513]1187                irc_rootmsg(irc, "that fingerprint is active, terminate otr connection first");
[c595308]1188                return;
1189        }
[5ebff60]1190
[c595308]1191        otrl_context_forget_fingerprint(fp, 0);
1192}
1193
[6738a67]1194void yes_forget_context(void *data)
[c595308]1195{
[5ebff60]1196        pair_t *p = (pair_t *) data;
1197        irc_t *irc = (irc_t *) p->fst;
1198        ConnContext *ctx = (ConnContext *) p->snd;
[6738a67]1199
1200        g_free(p);
[f93e01c]1201
1202        // XXX forget all contexts
[5ebff60]1203
1204        if (ctx->msgstate == OTRL_MSGSTATE_ENCRYPTED) {
[e67e513]1205                irc_rootmsg(irc, "active otr connection with %s, terminate it first",
[5ebff60]1206                            peernick(irc, ctx->username, ctx->protocol));
[c595308]1207                return;
1208        }
[5ebff60]1209
1210        if (ctx->msgstate == OTRL_MSGSTATE_FINISHED) {
[c595308]1211                otrl_context_force_plaintext(ctx);
[5ebff60]1212        }
[c595308]1213        otrl_context_forget(ctx);
1214}
1215
[6738a67]1216void yes_forget_key(void *data)
[d0faf62]1217{
[5ebff60]1218        OtrlPrivKey *key = (OtrlPrivKey *) data;
1219
[d0faf62]1220        otrl_privkey_forget(key);
[37bff51]1221        /* Hm, libotr doesn't seem to offer a function for explicitly /writing/
1222           keyfiles. So the key will be back on the next load... */
1223        /* TODO: Actually erase forgotten keys from storage? */
[d0faf62]1224}
1225
[c595308]1226void cmd_otr_forget(irc_t *irc, char **args)
1227{
[5ebff60]1228        if (!strcmp(args[1], "fingerprint")) {
[ad2d8bc]1229                irc_user_t *u;
[c595308]1230                ConnContext *ctx;
1231                Fingerprint *fp;
1232                char human[54];
1233                char *s;
[6738a67]1234                pair_t *p;
[5ebff60]1235
1236                if (!args[3]) {
[e67e513]1237                        irc_rootmsg(irc, "otr %s %s: not enough arguments (2 req.)", args[0], args[1]);
[c595308]1238                        return;
1239                }
[5ebff60]1240
[e2b15bb]1241                /* TODO: allow context specs ("user/proto/account") in 'otr forget fingerprint'? */
[ad2d8bc]1242                u = irc_user_by_name(irc, args[2]);
[5ebff60]1243                if (!u || !u->bu || !u->bu->ic) {
[e67e513]1244                        irc_rootmsg(irc, "%s: unknown user", args[2]);
[c595308]1245                        return;
1246                }
[5ebff60]1247
[ad2d8bc]1248                ctx = otrl_context_find(irc->otr->us, u->bu->handle, u->bu->ic->acc->user,
[5ebff60]1249                                        u->bu->ic->acc->prpl->name, OTRL_INSTAG_MASTER, 0, NULL, NULL, NULL);
1250                if (!ctx) {
[e67e513]1251                        irc_rootmsg(irc, "no otr context with %s", args[2]);
[c595308]1252                        return;
1253                }
[5ebff60]1254
1255                fp = match_fingerprint(irc, ctx, ((const char **) args) + 3);
1256                if (!fp) {
[c595308]1257                        /* match_fingerprint does error messages */
1258                        return;
1259                }
[5ebff60]1260
1261                if (fp == ctx->active_fingerprint) {
[e67e513]1262                        irc_rootmsg(irc, "that fingerprint is active, terminate otr connection first");
[c595308]1263                        return;
1264                }
[5ebff60]1265
[c595308]1266                otrl_privkey_hash_to_human(human, fp->fingerprint);
1267                s = g_strdup_printf("about to forget fingerprint %s, are you sure?", human);
[6738a67]1268                p = g_malloc(sizeof(pair_t));
[5ebff60]1269                if (!p) {
[6738a67]1270                        return;
[5ebff60]1271                }
[6738a67]1272                p->fst = irc;
1273                p->snd = fp;
[ad2d8bc]1274                query_add(irc, NULL, s, yes_forget_fingerprint, NULL, NULL, p);
[82e8fe8]1275                g_free(s);
[5ebff60]1276        } else if (!strcmp(args[1], "context")) {
[ad2d8bc]1277                irc_user_t *u;
[c595308]1278                ConnContext *ctx;
1279                char *s;
[6738a67]1280                pair_t *p;
[5ebff60]1281
[e2b15bb]1282                /* TODO: allow context specs ("user/proto/account") in 'otr forget contex'? */
[ad2d8bc]1283                u = irc_user_by_name(irc, args[2]);
[5ebff60]1284                if (!u || !u->bu || !u->bu->ic) {
[e67e513]1285                        irc_rootmsg(irc, "%s: unknown user", args[2]);
[c595308]1286                        return;
1287                }
[5ebff60]1288
[ad2d8bc]1289                ctx = otrl_context_find(irc->otr->us, u->bu->handle, u->bu->ic->acc->user,
[5ebff60]1290                                        u->bu->ic->acc->prpl->name, OTRL_INSTAG_MASTER, 0, NULL, NULL, NULL);
1291                if (!ctx) {
[e67e513]1292                        irc_rootmsg(irc, "no otr context with %s", args[2]);
[c595308]1293                        return;
1294                }
[5ebff60]1295
1296                if (ctx->msgstate == OTRL_MSGSTATE_ENCRYPTED) {
[e67e513]1297                        irc_rootmsg(irc, "active otr connection with %s, terminate it first", args[2]);
[c595308]1298                        return;
1299                }
[5ebff60]1300
[c595308]1301                s = g_strdup_printf("about to forget otr data about %s, are you sure?", args[2]);
[6738a67]1302                p = g_malloc(sizeof(pair_t));
[5ebff60]1303                if (!p) {
[6738a67]1304                        return;
[5ebff60]1305                }
[6738a67]1306                p->fst = irc;
1307                p->snd = ctx;
[ad2d8bc]1308                query_add(irc, NULL, s, yes_forget_context, NULL, NULL, p);
[82e8fe8]1309                g_free(s);
[5ebff60]1310        } else if (!strcmp(args[1], "key")) {
[d0faf62]1311                OtrlPrivKey *key;
1312                char *s;
[5ebff60]1313
1314                key = match_privkey(irc, ((const char **) args) + 2);
1315                if (!key) {
[d0faf62]1316                        /* match_privkey does error messages */
1317                        return;
1318                }
[5ebff60]1319
[d0faf62]1320                s = g_strdup_printf("about to forget the private key for %s/%s, are you sure?",
[5ebff60]1321                                    key->accountname, key->protocol);
[ad2d8bc]1322                query_add(irc, NULL, s, yes_forget_key, NULL, NULL, key);
[d0faf62]1323                g_free(s);
[5ebff60]1324        } else {
[e67e513]1325                irc_rootmsg(irc, "otr %s: unknown subcommand \"%s\", see \x02help otr forget\x02",
[5ebff60]1326                            args[0], args[1]);
[c595308]1327        }
1328}
1329
[764c7d1]1330
1331/*** local helpers / subroutines: ***/
1332
[090c9b7]1333void log_otr_message(void *opdata, const char *fmt, ...)
1334{
1335        va_list va;
1336
1337        va_start(va, fmt);
1338        char *msg = g_strdup_vprintf(fmt, va);
1339        va_end(va);
[5ebff60]1340
[090c9b7]1341        log_message(LOGLVL_INFO, "otr: %s", msg);
[098a75b]1342
1343        g_free(msg);
[090c9b7]1344}
1345
1346void display_otr_message(void *opdata, ConnContext *ctx, const char *fmt, ...)
1347{
1348        struct im_connection *ic =
[5ebff60]1349                check_imc(opdata, ctx->accountname, ctx->protocol);
[090c9b7]1350        irc_t *irc = ic->bee->ui_data;
1351        irc_user_t *u = peeruser(irc, ctx->username, ctx->protocol);
1352        va_list va;
1353
1354        va_start(va, fmt);
1355        char *msg = g_strdup_vprintf(fmt, va);
1356        va_end(va);
1357
[5ebff60]1358        if (u) {
[090c9b7]1359                /* display as a notice from this particular user */
1360                irc_usernotice(u, "%s", msg);
1361        } else {
1362                irc_rootmsg(irc, "[otr] %s", msg);
1363        }
1364
1365        g_free(msg);
1366}
1367
[99a01b9]1368/* combined handler for the 'otr smp' and 'otr smpq' commands */
[7c91392]1369void otr_smp_or_smpq(irc_t *irc, const char *nick, const char *question,
[5ebff60]1370                     const char *secret)
[99a01b9]1371{
1372        irc_user_t *u;
1373        ConnContext *ctx;
[329f9fe]1374        otrl_instag_t instag = OTRL_INSTAG_BEST;  // XXX
[99a01b9]1375
1376        u = irc_user_by_name(irc, nick);
[5ebff60]1377        if (!u || !u->bu || !u->bu->ic) {
[e67e513]1378                irc_rootmsg(irc, "%s: unknown user", nick);
[99a01b9]1379                return;
1380        }
[5ebff60]1381        if (!(u->bu->flags & BEE_USER_ONLINE)) {
[e67e513]1382                irc_rootmsg(irc, "%s is offline", nick);
[99a01b9]1383                return;
1384        }
[5ebff60]1385
[99a01b9]1386        ctx = otrl_context_find(irc->otr->us, u->bu->handle,
[5ebff60]1387                                u->bu->ic->acc->user, u->bu->ic->acc->prpl->name, instag, 0, NULL, NULL, NULL);
1388        if (!ctx || ctx->msgstate != OTRL_MSGSTATE_ENCRYPTED) {
[e67e513]1389                irc_rootmsg(irc, "smp: otr inactive with %s, try \x02otr connect"
[5ebff60]1390                            " %s\x02", nick, nick);
[99a01b9]1391                return;
1392        }
1393
[5ebff60]1394        if (ctx->smstate->nextExpected != OTRL_SMP_EXPECT1) {
[99a01b9]1395                log_message(LOGLVL_INFO,
[5ebff60]1396                            "SMP already in phase %d, sending abort before reinitiating",
1397                            ctx->smstate->nextExpected + 1);
[99a01b9]1398                otrl_message_abort_smp(irc->otr->us, &otr_ops, u->bu->ic, ctx);
1399                otrl_sm_state_free(ctx->smstate);
1400        }
[944d7a5]1401
[5ebff60]1402        if (question) {
[9cc653c]1403                /* this was 'otr smpq', just initiate */
[e67e513]1404                irc_rootmsg(irc, "smp: initiating with %s...", u->nick);
[9cc653c]1405                otrl_message_initiate_smp_q(irc->otr->us, &otr_ops, u->bu->ic, ctx,
[5ebff60]1406                                            question, (unsigned char *) secret, strlen(secret));
[9cc653c]1407                /* smp is now in EXPECT2 */
1408        } else {
1409                /* this was 'otr smp', initiate or reply */
1410                /* warning: the following assumes that smstates are cleared whenever an SMP
[5ebff60]1411                   is completed or aborted! */
1412                if (ctx->smstate->secret == NULL) {
[e67e513]1413                        irc_rootmsg(irc, "smp: initiating with %s...", u->nick);
[99a01b9]1414                        otrl_message_initiate_smp(irc->otr->us, &otr_ops,
[5ebff60]1415                                                  u->bu->ic, ctx, (unsigned char *) secret, strlen(secret));
[9cc653c]1416                        /* smp is now in EXPECT2 */
1417                } else {
1418                        /* if we're still in EXPECT1 but smstate is initialized, we must have
1419                           received the SMP1, so let's issue a response */
[e67e513]1420                        irc_rootmsg(irc, "smp: responding to %s...", u->nick);
[9cc653c]1421                        otrl_message_respond_smp(irc->otr->us, &otr_ops,
[5ebff60]1422                                                 u->bu->ic, ctx, (unsigned char *) secret, strlen(secret));
[9cc653c]1423                        /* smp is now in EXPECT3 */
[99a01b9]1424                }
1425        }
1426}
1427
[22ec21d]1428/* timeout handler that calls otrl_message_poll */
1429gboolean ev_message_poll(gpointer data, gint fd, b_input_condition cond)
1430{
1431        otr_t *otr = data;
1432
[5ebff60]1433        if (otr && otr->us) {
[c347a12]1434                otrl_message_poll(otr->us, &otr_ops, NULL);
[5ebff60]1435        }
[22ec21d]1436
[5ebff60]1437        return TRUE;    /* cycle timer */
[22ec21d]1438}
1439
[764c7d1]1440/* helper to assert that account and protocol names given to ops below always
1441   match the im_connection passed through as opdata */
1442struct im_connection *check_imc(void *opdata, const char *accountname,
[5ebff60]1443                                const char *protocol)
[764c7d1]1444{
[5ebff60]1445        struct im_connection *ic = (struct im_connection *) opdata;
[764c7d1]1446
[fa9478e]1447        /* libotr 4.0.0 has a bug where it doesn't set opdata, so we catch
1448         * that and try to find the desired connection in the global list. */
[5ebff60]1449        if (!ic) {
[fa9478e]1450                GSList *l;
[5ebff60]1451                for (l = get_connections(); l; l = l->next) {
[fa9478e]1452                        ic = l->data;
[5ebff60]1453                        if (strcmp(accountname, ic->acc->user) == 0 &&
1454                            strcmp(protocol, ic->acc->prpl->name) == 0) {
[fa9478e]1455                                break;
[5ebff60]1456                        }
[fa9478e]1457                }
1458                assert(l != NULL);  /* a match should always be found */
[5ebff60]1459                if (!l) {
[37ed402]1460                        return NULL;
[5ebff60]1461                }
[fa9478e]1462        }
1463
[764c7d1]1464        if (strcmp(accountname, ic->acc->user) != 0) {
1465                log_message(LOGLVL_WARNING,
[5ebff60]1466                            "otr: internal account name mismatch: '%s' vs '%s'",
1467                            accountname, ic->acc->user);
[764c7d1]1468        }
1469        if (strcmp(protocol, ic->acc->prpl->name) != 0) {
1470                log_message(LOGLVL_WARNING,
[5ebff60]1471                            "otr: internal protocol name mismatch: '%s' vs '%s'",
1472                            protocol, ic->acc->prpl->name);
[764c7d1]1473        }
[5ebff60]1474
[764c7d1]1475        return ic;
1476}
1477
[ad2d8bc]1478irc_user_t *peeruser(irc_t *irc, const char *handle, const char *protocol)
[764c7d1]1479{
[ad2d8bc]1480        GSList *l;
[5ebff60]1481
1482        for (l = irc->b->users; l; l = l->next) {
[ad2d8bc]1483                bee_user_t *bu = l->data;
[764c7d1]1484                struct prpl *prpl;
[5ebff60]1485                if (!bu->ui_data || !bu->ic || !bu->handle) {
[8521b02]1486                        continue;
[5ebff60]1487                }
[ad2d8bc]1488                prpl = bu->ic->acc->prpl;
[5ebff60]1489                if (strcmp(prpl->name, protocol) == 0
1490                    && prpl->handle_cmp(bu->handle, handle) == 0) {
[ad2d8bc]1491                        return bu->ui_data;
[764c7d1]1492                }
1493        }
[5ebff60]1494
[5a71d9c]1495        return NULL;
1496}
1497
[8521b02]1498int hexval(char a)
1499{
[5ebff60]1500        int x = g_ascii_tolower(a);
1501
1502        if (x >= 'a' && x <= 'f') {
[8521b02]1503                x = x - 'a' + 10;
[5ebff60]1504        } else if (x >= '0' && x <= '9') {
[8521b02]1505                x = x - '0';
[5ebff60]1506        } else {
[8521b02]1507                return -1;
[5ebff60]1508        }
1509
[8521b02]1510        return x;
1511}
1512
[5a71d9c]1513const char *peernick(irc_t *irc, const char *handle, const char *protocol)
1514{
1515        static char fallback[512];
[5ebff60]1516
[ad2d8bc]1517        irc_user_t *u = peeruser(irc, handle, protocol);
[5ebff60]1518
1519        if (u) {
[5a71d9c]1520                return u->nick;
1521        } else {
1522                g_snprintf(fallback, 511, "%s/%s", handle, protocol);
1523                return fallback;
1524        }
1525}
1526
[a0c6fc5]1527void otr_update_uflags(ConnContext *context, irc_user_t *u)
1528{
1529        const char *trust;
1530
[5ebff60]1531        if (context->active_fingerprint) {
[a0c6fc5]1532                u->flags |= IRC_USER_OTR_ENCRYPTED;
1533
1534                trust = context->active_fingerprint->trust;
[5ebff60]1535                if (trust && trust[0]) {
[a0c6fc5]1536                        u->flags |= IRC_USER_OTR_TRUSTED;
[5ebff60]1537                } else {
[a0c6fc5]1538                        u->flags &= ~IRC_USER_OTR_TRUSTED;
[5ebff60]1539                }
[a0c6fc5]1540        } else {
1541                u->flags &= ~IRC_USER_OTR_ENCRYPTED;
1542        }
1543}
1544
[ad2d8bc]1545int otr_update_modeflags(irc_t *irc, irc_user_t *u)
[5a71d9c]1546{
[1dc00fe]1547        return 0;
[764c7d1]1548}
1549
1550void show_fingerprints(irc_t *irc, ConnContext *ctx)
1551{
1552        char human[45];
1553        Fingerprint *fp;
1554        const char *trust;
[5ebff60]1555        int count = 0;
1556
[2c81d15]1557        /* Is this a subcontext? If so, only list the active fingerprint */
1558        if (ctx->m_context != ctx) {
1559                fp = ctx->active_fingerprint;
1560        } else {
1561                fp = &ctx->fingerprint_root;
1562        }
1563
1564        while (fp) {
[5ebff60]1565                if (!fp->fingerprint) {
[2c81d15]1566                        fp = fp->next;
[764c7d1]1567                        continue;
[5ebff60]1568                }
[764c7d1]1569                count++;
1570                otrl_privkey_hash_to_human(human, fp->fingerprint);
[5ebff60]1571                if (!fp->trust || fp->trust[0] == '\0') {
1572                        trust = "untrusted";
[764c7d1]1573                } else {
[5ebff60]1574                        trust = fp->trust;
[764c7d1]1575                }
[5ebff60]1576                if (fp == ctx->active_fingerprint) {
[2c81d15]1577                        irc_rootmsg(irc, "      \x02%s (%s)\x02", human, trust);
[764c7d1]1578                } else {
[2c81d15]1579                        irc_rootmsg(irc, "      %s (%s)", human, trust);
[764c7d1]1580                }
[2c81d15]1581
1582                /* Break if this is a subcontext - we only print active fp */
1583                if (ctx->m_context != ctx) {
1584                        break;
1585                }
1586                fp = fp->next;
[764c7d1]1587        }
[5ebff60]1588        if (count == 0) {
[2c81d15]1589                irc_rootmsg(irc, "      (none)");
[5ebff60]1590        }
[8521b02]1591}
1592
[c595308]1593Fingerprint *match_fingerprint(irc_t *irc, ConnContext *ctx, const char **args)
1594{
1595        Fingerprint *fp, *fp2;
1596        char human[45];
1597        char prefix[45], *p;
1598        int n;
[5ebff60]1599        int i, j;
1600
[c595308]1601        /* assemble the args into a prefix in standard "human" form */
[5ebff60]1602        n = 0;
1603        p = prefix;
1604        for (i = 0; args[i]; i++) {
1605                for (j = 0; args[i][j]; j++) {
[6b13103]1606                        char c = g_ascii_toupper(args[i][j]);
[5ebff60]1607
1608                        if (n >= 40) {
[e67e513]1609                                irc_rootmsg(irc, "too many fingerprint digits given, expected at most 40");
[c595308]1610                                return NULL;
1611                        }
[5ebff60]1612
1613                        if ((c >= 'A' && c <= 'F') || (c >= '0' && c <= '9')) {
[c595308]1614                                *(p++) = c;
1615                        } else {
[5ebff60]1616                                irc_rootmsg(irc, "invalid hex digit '%c' in block %d", args[i][j], i + 1);
[c595308]1617                                return NULL;
1618                        }
[5ebff60]1619
[c595308]1620                        n++;
[5ebff60]1621                        if (n % 8 == 0) {
[c595308]1622                                *(p++) = ' ';
[5ebff60]1623                        }
[c595308]1624                }
1625        }
1626        *p = '\0';
[5ebff60]1627
[c595308]1628        /* find first fingerprint with the given prefix */
1629        n = strlen(prefix);
[5ebff60]1630        for (fp = &ctx->fingerprint_root; fp; fp = fp->next) {
1631                if (!fp->fingerprint) {
[c595308]1632                        continue;
[5ebff60]1633                }
[c595308]1634                otrl_privkey_hash_to_human(human, fp->fingerprint);
[5ebff60]1635                if (!strncmp(prefix, human, n)) {
[c595308]1636                        break;
[5ebff60]1637                }
[c595308]1638        }
[5ebff60]1639        if (!fp) {
[e67e513]1640                irc_rootmsg(irc, "%s: no match", prefix);
[c595308]1641                return NULL;
1642        }
[5ebff60]1643
[c595308]1644        /* make sure the match, if any, is unique */
[5ebff60]1645        for (fp2 = fp->next; fp2; fp2 = fp2->next) {
1646                if (!fp2->fingerprint) {
[c595308]1647                        continue;
[5ebff60]1648                }
[c595308]1649                otrl_privkey_hash_to_human(human, fp2->fingerprint);
[5ebff60]1650                if (!strncmp(prefix, human, n)) {
[c595308]1651                        break;
[5ebff60]1652                }
[c595308]1653        }
[5ebff60]1654        if (fp2) {
[e67e513]1655                irc_rootmsg(irc, "%s: multiple matches", prefix);
[c595308]1656                return NULL;
1657        }
[5ebff60]1658
[c595308]1659        return fp;
1660}
1661
[5f4eede]1662OtrlPrivKey *match_privkey(irc_t *irc, const char **args)
1663{
1664        OtrlPrivKey *k, *k2;
1665        char human[45];
1666        char prefix[45], *p;
1667        int n;
[5ebff60]1668        int i, j;
1669
[5f4eede]1670        /* assemble the args into a prefix in standard "human" form */
[5ebff60]1671        n = 0;
1672        p = prefix;
1673        for (i = 0; args[i]; i++) {
1674                for (j = 0; args[i][j]; j++) {
[6b13103]1675                        char c = g_ascii_toupper(args[i][j]);
[5ebff60]1676
1677                        if (n >= 40) {
[e67e513]1678                                irc_rootmsg(irc, "too many fingerprint digits given, expected at most 40");
[5f4eede]1679                                return NULL;
1680                        }
[5ebff60]1681
1682                        if ((c >= 'A' && c <= 'F') || (c >= '0' && c <= '9')) {
[5f4eede]1683                                *(p++) = c;
1684                        } else {
[5ebff60]1685                                irc_rootmsg(irc, "invalid hex digit '%c' in block %d", args[i][j], i + 1);
[5f4eede]1686                                return NULL;
1687                        }
[5ebff60]1688
[5f4eede]1689                        n++;
[5ebff60]1690                        if (n % 8 == 0) {
[5f4eede]1691                                *(p++) = ' ';
[5ebff60]1692                        }
[5f4eede]1693                }
1694        }
1695        *p = '\0';
[5ebff60]1696
[5f4eede]1697        /* find first key which matches the given prefix */
1698        n = strlen(prefix);
[5ebff60]1699        for (k = irc->otr->us->privkey_root; k; k = k->next) {
[dc9797f]1700                p = otrl_privkey_fingerprint(irc->otr->us, human, k->accountname, k->protocol);
[5ebff60]1701                if (!p) { /* gah! :-P */
[5f4eede]1702                        continue;
[5ebff60]1703                }
1704                if (!strncmp(prefix, human, n)) {
[5f4eede]1705                        break;
[5ebff60]1706                }
[5f4eede]1707        }
[5ebff60]1708        if (!k) {
[e67e513]1709                irc_rootmsg(irc, "%s: no match", prefix);
[5f4eede]1710                return NULL;
1711        }
[5ebff60]1712
[5f4eede]1713        /* make sure the match, if any, is unique */
[5ebff60]1714        for (k2 = k->next; k2; k2 = k2->next) {
[dc9797f]1715                p = otrl_privkey_fingerprint(irc->otr->us, human, k2->accountname, k2->protocol);
[5ebff60]1716                if (!p) { /* gah! :-P */
[5f4eede]1717                        continue;
[5ebff60]1718                }
1719                if (!strncmp(prefix, human, n)) {
[5f4eede]1720                        break;
[5ebff60]1721                }
[5f4eede]1722        }
[5ebff60]1723        if (k2) {
[e67e513]1724                irc_rootmsg(irc, "%s: multiple matches", prefix);
[5f4eede]1725                return NULL;
1726        }
[5ebff60]1727
[5f4eede]1728        return k;
1729}
1730
[8521b02]1731void show_general_otr_info(irc_t *irc)
1732{
1733        ConnContext *ctx;
1734        OtrlPrivKey *key;
1735        char human[45];
[3064ea4]1736        kg_t *kg;
[8521b02]1737
[3064ea4]1738        /* list all privkeys (including ones being generated) */
[e67e513]1739        irc_rootmsg(irc, "\x1fprivate keys:\x1f");
[5ebff60]1740        for (key = irc->otr->us->privkey_root; key; key = key->next) {
[8521b02]1741                const char *hash;
[5ebff60]1742
1743                switch (key->pubkey_type) {
[8521b02]1744                case OTRL_PUBKEY_TYPE_DSA:
[e67e513]1745                        irc_rootmsg(irc, "  %s/%s - DSA", key->accountname, key->protocol);
[8521b02]1746                        break;
1747                default:
[e67e513]1748                        irc_rootmsg(irc, "  %s/%s - type %d", key->accountname, key->protocol,
[5ebff60]1749                                    key->pubkey_type);
[8521b02]1750                }
1751
1752                /* No, it doesn't make much sense to search for the privkey again by
1753                   account/protocol, but libotr currently doesn't provide a direct routine
1754                   for hashing a given 'OtrlPrivKey'... */
[dc9797f]1755                hash = otrl_privkey_fingerprint(irc->otr->us, human, key->accountname, key->protocol);
[5ebff60]1756                if (hash) { /* should always succeed */
[e67e513]1757                        irc_rootmsg(irc, "    %s", human);
[5ebff60]1758                }
[8521b02]1759        }
[5ebff60]1760        if (irc->otr->sent_accountname) {
[e67e513]1761                irc_rootmsg(irc, "  %s/%s - DSA", irc->otr->sent_accountname,
[5ebff60]1762                            irc->otr->sent_protocol);
[e67e513]1763                irc_rootmsg(irc, "    (being generated)");
[1221ef0]1764        }
[5ebff60]1765        for (kg = irc->otr->todo; kg; kg = kg->next) {
[e67e513]1766                irc_rootmsg(irc, "  %s/%s - DSA", kg->accountname, kg->protocol);
1767                irc_rootmsg(irc, "    (queued)");
[3064ea4]1768        }
[5ebff60]1769        if (key == irc->otr->us->privkey_root &&
1770            !irc->otr->sent_accountname &&
1771            kg == irc->otr->todo) {
[e67e513]1772                irc_rootmsg(irc, "  (none)");
[5ebff60]1773        }
[8521b02]1774
1775        /* list all contexts */
[e4752a6]1776        /* XXX remove this, or split off as its own command */
[e67e513]1777        irc_rootmsg(irc, "%s", "");
1778        irc_rootmsg(irc, "\x1f" "connection contexts:\x1f (bold=currently encrypted)");
[2c81d15]1779
1780        ctx = irc->otr->us->context_root;
1781        while (ctx) {
1782                ConnContext *subctx;
[ad2d8bc]1783                irc_user_t *u;
[8521b02]1784                char *userstring;
[2c81d15]1785                char encrypted = 0;
[5ebff60]1786
[8521b02]1787                u = peeruser(irc, ctx->username, ctx->protocol);
[5ebff60]1788                if (u) {
[8521b02]1789                        userstring = g_strdup_printf("%s/%s/%s (%s)",
[5ebff60]1790                                                     ctx->username, ctx->protocol, ctx->accountname, u->nick);
1791                } else {
[8521b02]1792                        userstring = g_strdup_printf("%s/%s/%s",
[5ebff60]1793                                                     ctx->username, ctx->protocol, ctx->accountname);
1794                }
1795
[2c81d15]1796                subctx = ctx;
1797                while (subctx && subctx->m_context == ctx) {
1798                        if (subctx->msgstate == OTRL_MSGSTATE_ENCRYPTED) {
1799                                encrypted = 1;
1800                        }
1801                        subctx = subctx->next;
1802                }
1803
1804                if(encrypted) {
[e67e513]1805                        irc_rootmsg(irc, \x02%s\x02", userstring);
[8521b02]1806                } else {
[e67e513]1807                        irc_rootmsg(irc, "  %s", userstring);
[8521b02]1808                }
[5ebff60]1809
[2c81d15]1810                /* Skip subcontexts/instances from output */
1811                ctx = subctx;
1812
[8521b02]1813                g_free(userstring);
1814        }
[2c81d15]1815
[5ebff60]1816        if (ctx == irc->otr->us->context_root) {
[e67e513]1817                irc_rootmsg(irc, "  (none)");
[5ebff60]1818        }
[8521b02]1819}
1820
[2c81d15]1821void show_otr_context_info(irc_t *irc, ConnContext *ctx, ConnContext *bestctx)
[8521b02]1822{
[2c81d15]1823        ConnContext *subctx;
1824        int instcount = 0;
1825
1826        subctx = ctx;
1827        while (subctx && subctx->m_context == ctx) {
1828                if (subctx->m_context == subctx) {
1829                        if (subctx == bestctx) {
1830                                irc_rootmsg(irc, \x02master context (target):\x02");
1831                        } else {
1832                                irc_rootmsg(irc, "  master context:");
1833                        }
1834                        irc_rootmsg(irc, "    known fingerprints (bold = active for v1 or v2):");
1835                } else {
1836                        if (subctx == bestctx) {
1837                                irc_rootmsg(irc, \x02instance %d (target):\x02", instcount);
1838                        } else {
1839                                irc_rootmsg(irc, "  instance %d:", instcount);
1840                        }
1841                        irc_rootmsg(irc, "    active fingerprint:");
1842                        instcount++;
1843                }
[f93e01c]1844
[2c81d15]1845                show_fingerprints(irc, subctx);
[8521b02]1846
[2c81d15]1847                switch (subctx->msgstate) {
1848                case OTRL_MSGSTATE_PLAINTEXT:
1849                        irc_rootmsg(irc, "    connection state: cleartext");
1850                        break;
1851                case OTRL_MSGSTATE_ENCRYPTED:
1852                        irc_rootmsg(irc, "    connection state: encrypted (v%d)", subctx->protocol_version);
1853                        break;
1854                case OTRL_MSGSTATE_FINISHED:
1855                        irc_rootmsg(irc, "    connection state: shut down");
1856                        break;
1857                default:
1858                        irc_rootmsg(irc, "    connection state: %d", subctx->msgstate);
1859                }
[8521b02]1860
[2c81d15]1861                subctx = subctx->next;
1862        }
[764c7d1]1863}
1864
[dc9797f]1865int keygen_in_progress(irc_t *irc, const char *handle, const char *protocol)
1866{
1867        kg_t *kg;
[5ebff60]1868
1869        if (!irc->otr->sent_accountname || !irc->otr->sent_protocol) {
[dc9797f]1870                return 0;
[5ebff60]1871        }
[dc9797f]1872
1873        /* are we currently working on this key? */
[5ebff60]1874        if (!strcmp(handle, irc->otr->sent_accountname) &&
1875            !strcmp(protocol, irc->otr->sent_protocol)) {
[dc9797f]1876                return 1;
[5ebff60]1877        }
1878
[dc9797f]1879        /* do we have it queued for later? */
[5ebff60]1880        for (kg = irc->otr->todo; kg; kg = kg->next) {
1881                if (!strcmp(handle, kg->accountname) &&
1882                    !strcmp(protocol, kg->protocol)) {
[dc9797f]1883                        return 1;
[5ebff60]1884                }
[dc9797f]1885        }
[5ebff60]1886
[dc9797f]1887        return 0;
1888}
1889
[764c7d1]1890void otr_keygen(irc_t *irc, const char *handle, const char *protocol)
1891{
[dc9797f]1892        /* do nothing if a key for the requested account is already being generated */
[5ebff60]1893        if (keygen_in_progress(irc, handle, protocol)) {
[dc9797f]1894                return;
[5ebff60]1895        }
[764c7d1]1896
[522a00f]1897        /* see if we already have a keygen child running. if not, start one and put a
[27db433]1898           handler on its output. */
[5ebff60]1899        if (!irc->otr->keygen || waitpid(irc->otr->keygen, NULL, WNOHANG)) {
[27db433]1900                pid_t p;
1901                int to[2], from[2];
1902                FILE *tof, *fromf;
[5ebff60]1903
1904                if (pipe(to) < 0 || pipe(from) < 0) {
[e67e513]1905                        irc_rootmsg(irc, "otr keygen: couldn't create pipe: %s", strerror(errno));
[27db433]1906                        return;
1907                }
[5ebff60]1908
[27db433]1909                tof = fdopen(to[1], "w");
1910                fromf = fdopen(from[0], "r");
[5ebff60]1911                if (!tof || !fromf) {
[e67e513]1912                        irc_rootmsg(irc, "otr keygen: couldn't streamify pipe: %s", strerror(errno));
[27db433]1913                        return;
1914                }
[5ebff60]1915
[27db433]1916                p = fork();
[5ebff60]1917                if (p < 0) {
[e67e513]1918                        irc_rootmsg(irc, "otr keygen: couldn't fork: %s", strerror(errno));
[27db433]1919                        return;
1920                }
[5ebff60]1921
1922                if (!p) {
[27db433]1923                        /* child process */
1924                        signal(SIGTERM, exit);
[12cc58b]1925                        keygen_child_main(irc->otr->us, to[0], from[1]);
[27db433]1926                        exit(0);
1927                }
[5ebff60]1928
[dc9797f]1929                irc->otr->keygen = p;
1930                irc->otr->to = tof;
1931                irc->otr->from = fromf;
1932                irc->otr->sent_accountname = NULL;
1933                irc->otr->sent_protocol = NULL;
1934                irc->otr->todo = NULL;
[ad2d8bc]1935                b_input_add(from[0], B_EV_IO_READ, keygen_finish_handler, irc);
[27db433]1936        }
[5ebff60]1937
[dc9797f]1938        /* is the keygen slave currently working? */
[5ebff60]1939        if (irc->otr->sent_accountname) {
[dc9797f]1940                /* enqueue our job for later transmission */
1941                kg_t **kg = &irc->otr->todo;
[5ebff60]1942                while (*kg) {
1943                        kg = &((*kg)->next);
1944                }
[dc9797f]1945                *kg = g_new0(kg_t, 1);
[ba5add7]1946                (*kg)->accountname = g_strdup(handle);
1947                (*kg)->protocol = g_strdup(protocol);
[dc9797f]1948        } else {
1949                /* send our job over and remember it */
1950                fprintf(irc->otr->to, "%s\n%s\n", handle, protocol);
1951                fflush(irc->otr->to);
[ba5add7]1952                irc->otr->sent_accountname = g_strdup(handle);
1953                irc->otr->sent_protocol = g_strdup(protocol);
[dc9797f]1954        }
[27db433]1955}
[522a00f]1956
[12cc58b]1957void keygen_child_main(OtrlUserState us, int infd, int outfd)
[27db433]1958{
1959        FILE *input, *output;
1960        char filename[128], accountname[512], protocol[512];
1961        gcry_error_t e;
1962        int tempfd;
[5ebff60]1963
[27db433]1964        input = fdopen(infd, "r");
1965        output = fdopen(outfd, "w");
[5ebff60]1966
1967        while (!feof(input) && !ferror(input) && !feof(output) && !ferror(output)) {
[27db433]1968                myfgets(accountname, 512, input);
1969                myfgets(protocol, 512, input);
[5ebff60]1970
[27db433]1971                strncpy(filename, "/tmp/bitlbee-XXXXXX", 128);
1972                tempfd = mkstemp(filename);
1973                close(tempfd);
1974
1975                e = otrl_privkey_generate(us, filename, accountname, protocol);
[5ebff60]1976                if (e) {
[27db433]1977                        fprintf(output, "\n");  /* this means failure */
1978                        fprintf(output, "otr keygen: %s\n", gcry_strerror(e));
1979                        unlink(filename);
1980                } else {
1981                        fprintf(output, "%s\n", filename);
1982                        fprintf(output, "otr keygen for %s/%s complete\n", accountname, protocol);
1983                }
1984                fflush(output);
1985        }
[5ebff60]1986
[27db433]1987        fclose(input);
1988        fclose(output);
[764c7d1]1989}
1990
1991gboolean keygen_finish_handler(gpointer data, gint fd, b_input_condition cond)
1992{
[5ebff60]1993        irc_t *irc = (irc_t *) data;
[27db433]1994        char filename[512], msg[512];
1995
[dc9797f]1996        myfgets(filename, 512, irc->otr->from);
1997        myfgets(msg, 512, irc->otr->from);
[5ebff60]1998
[e67e513]1999        irc_rootmsg(irc, "%s", msg);
[5ebff60]2000        if (filename[0]) {
2001                if (strsane(irc->user->nick)) {
[c7b94ef]2002                        char *kf = g_strdup_printf("%s%s.otr_keys", global.conf->configdir, irc->user->nick);
2003                        char *tmp = g_strdup_printf("%s.new", kf);
2004                        copyfile(filename, tmp);
2005                        unlink(filename);
[5ebff60]2006                        rename(tmp, kf);
[c7b94ef]2007                        otrl_privkey_read(irc->otr->us, kf);
2008                        g_free(kf);
2009                        g_free(tmp);
2010                } else {
2011                        otrl_privkey_read(irc->otr->us, filename);
2012                        unlink(filename);
2013                }
[27db433]2014        }
[5ebff60]2015
[dc9797f]2016        /* forget this job */
[ba5add7]2017        g_free(irc->otr->sent_accountname);
2018        g_free(irc->otr->sent_protocol);
[dc9797f]2019        irc->otr->sent_accountname = NULL;
2020        irc->otr->sent_protocol = NULL;
[5ebff60]2021
[dc9797f]2022        /* see if there are any more in the queue */
[5ebff60]2023        if (irc->otr->todo) {
[dc9797f]2024                kg_t *p = irc->otr->todo;
2025                /* send the next one over */
2026                fprintf(irc->otr->to, "%s\n%s\n", p->accountname, p->protocol);
2027                fflush(irc->otr->to);
2028                irc->otr->sent_accountname = p->accountname;
2029                irc->otr->sent_protocol = p->protocol;
2030                irc->otr->todo = p->next;
2031                g_free(p);
2032                return TRUE;   /* keep watching */
2033        } else {
2034                /* okay, the slave is idle now, so kill him */
2035                fclose(irc->otr->from);
2036                fclose(irc->otr->to);
[2dcaf9a]2037                irc->otr->from = irc->otr->to = NULL;
[dc9797f]2038                kill(irc->otr->keygen, SIGTERM);
2039                waitpid(irc->otr->keygen, NULL, 0);
2040                irc->otr->keygen = 0;
[27db433]2041                return FALSE;  /* unregister ourselves */
2042        }
2043}
2044
2045void copyfile(const char *a, const char *b)
2046{
2047        int fda, fdb;
2048        int n;
2049        char buf[1024];
[5ebff60]2050
[27db433]2051        fda = open(a, O_RDONLY);
2052        fdb = open(b, O_WRONLY | O_CREAT | O_TRUNC, 0600);
[5ebff60]2053
2054        while ((n = read(fda, buf, 1024)) > 0) {
[27db433]2055                write(fdb, buf, n);
[5ebff60]2056        }
2057
[27db433]2058        close(fda);
[5ebff60]2059        close(fdb);
[27db433]2060}
2061
2062void myfgets(char *s, int size, FILE *stream)
2063{
[5ebff60]2064        if (!fgets(s, size, stream)) {
[27db433]2065                s[0] = '\0';
2066        } else {
2067                int n = strlen(s);
[5ebff60]2068                if (n > 0 && s[n - 1] == '\n') {
2069                        s[n - 1] = '\0';
2070                }
[27db433]2071        }
[764c7d1]2072}
2073
[6738a67]2074void yes_keygen(void *data)
[764c7d1]2075{
[5ebff60]2076        account_t *acc = (account_t *) data;
[ad2d8bc]2077        irc_t *irc = acc->bee->ui_data;
[5ebff60]2078
2079        if (keygen_in_progress(irc, acc->user, acc->prpl->name)) {
[e67e513]2080                irc_rootmsg(irc, "keygen for %s/%s already in progress",
[5ebff60]2081                            acc->user, acc->prpl->name);
[dc9797f]2082        } else {
[e67e513]2083                irc_rootmsg(irc, "starting background keygen for %s/%s",
[5ebff60]2084                            acc->user, acc->prpl->name);
[e67e513]2085                irc_rootmsg(irc, "you will be notified when it completes");
[ad2d8bc]2086                otr_keygen(irc, acc->user, acc->prpl->name);
[dc9797f]2087        }
[764c7d1]2088}
[3231485]2089
[c7b94ef]2090/* check whether a string is safe to use in a path component */
2091int strsane(const char *s)
2092{
2093        return strpbrk(s, "/\\") == NULL;
2094}
2095
[c239fff]2096/* close the OTR connection with the given buddy */
2097gboolean otr_disconnect_user(irc_t *irc, irc_user_t *u)
2098{
[5ebff60]2099        if (!u || !u->bu || !u->bu->ic) {
[c239fff]2100                return FALSE;
[5ebff60]2101        }
[c239fff]2102
2103        /* XXX we disconnect all instances; is that what we want? */
2104        otrl_message_disconnect_all_instances(irc->otr->us, &otr_ops,
[5ebff60]2105                                              u->bu->ic, u->bu->ic->acc->user, u->bu->ic->acc->prpl->name,
2106                                              u->bu->handle);
2107
[c239fff]2108        u->flags &= ~IRC_USER_OTR_TRUSTED;
2109        u->flags &= ~IRC_USER_OTR_ENCRYPTED;
2110        otr_update_modeflags(irc, u);
2111
2112        return TRUE;
2113}
2114
2115/* close all active OTR connections */
2116void otr_disconnect_all(irc_t *irc)
2117{
2118        irc_user_t *u;
2119        ConnContext *ctx;
2120
[5ebff60]2121        for (ctx = irc->otr->us->context_root; ctx; ctx = ctx->next) {
2122                if (ctx->msgstate == OTRL_MSGSTATE_ENCRYPTED) {
[c239fff]2123                        u = peeruser(irc, ctx->username, ctx->protocol);
2124                        (void) otr_disconnect_user(irc, u);
2125                }
2126        }
2127}
Note: See TracBrowser for help on using the repository browser.