source: otr.c @ 8d7cc55

Last change on this file since 8d7cc55 was 37437f2, checked in by dequis <dx@…>, at 2018-08-26T21:17:06Z

otr: Don't block attempts to connect/smp/smpq to "offline" users

Other otr implementations don't have this check, and in many modern IM
protocols the offline status isn't a reliable indicator of message
deliverability.

Given how this applies to explicit connection attempts, it's better to
just let these go through and assume the user knows what they are doing.
The worst that can happen here is some slight disappointment.

Change suggested by arc, thanks!

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