source: otr.c @ b201c68

Last change on this file since b201c68 was 0e788f5, checked in by Wilmer van der Gaast <wilmer@…>, at 2013-02-21T19:15:59Z

I'm still bored on a long flight. Wrote a script to automatically update
my copyright mentions since some were getting pretty stale. Left files not
touched since before 2012 alone so that this change doesn't touch almost
EVERY source file.

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