source: otr.c @ 858ea01

Last change on this file since 858ea01 was 858ea01, checked in by Wilmer van der Gaast <wilmer@…>, at 2010-09-30T05:50:43Z

Allow building OTR support as a plugin. Fairly simple, let's hope I can get
away with doing this without libtool (eep).

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