source: otr.c @ 654112d4

Last change on this file since 654112d4 was 654112d4, checked in by dequis <dx@…>, at 2015-08-11T06:48:15Z

otr: color multiline messages

Fixes trac ticket 710.

Incoming messages can have newlines in them, which become several
PRIVMSG on the irc layer. Prepending color codes at the beginning of the
message resulted in showing the rest of those PRIVMSG as white.

This splits the message by newlines and rebuilds it in a GString,
re-adding the color codes right after each newline.

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