source: otr.c @ 5f4eede

Last change on this file since 5f4eede was 5f4eede, checked in by Sven Moritz Hallberg <sm@…>, at 2008-02-11T23:16:23Z
  • add nonfunctional 'otr forget key' implementation
  • add 'color_encrypted' setting
  • Property mode set to 100644
File size: 41.1 KB
Line 
1#include "bitlbee.h"
2#ifdef WITH_OTR
3#include "irc.h"
4#include "otr.h"
5#include <sys/types.h>
6#include <unistd.h>
7
8/**
9files used to store OTR data:
10  $configdir/$nick.otr_keys
11  $configdir/$nick.otr_fprints
12 **/
13
14
15/** OTR interface routines for the OtrlMessageAppOps struct: **/
16
17OtrlPolicy op_policy(void *opdata, ConnContext *context);
18
19void op_create_privkey(void *opdata, const char *accountname, const char *protocol);
20
21int op_is_logged_in(void *opdata, const char *accountname, const char *protocol,
22        const char *recipient);
23
24void op_inject_message(void *opdata, const char *accountname, const char *protocol,
25        const char *recipient, const char *message);
26
27int op_display_otr_message(void *opdata, const char *accountname, const char *protocol,
28        const char *username, const char *msg);
29
30void op_new_fingerprint(void *opdata, OtrlUserState us, const char *accountname,
31        const char *protocol, const char *username, unsigned char fingerprint[20]);
32
33void op_write_fingerprints(void *opdata);
34
35void op_gone_secure(void *opdata, ConnContext *context);
36
37void op_gone_insecure(void *opdata, ConnContext *context);
38
39void op_still_secure(void *opdata, ConnContext *context, int is_reply);
40
41void op_log_message(void *opdata, const char *message);
42
43int op_max_message_size(void *opdata, ConnContext *context);
44
45const char *op_account_name(void *opdata, const char *account, const char *protocol);
46
47
48/** otr sub-command handlers: **/
49
50void cmd_otr_connect(irc_t *irc, char **args);
51void cmd_otr_disconnect(irc_t *irc, char **args);
52void cmd_otr_smp(irc_t *irc, char **args);
53void cmd_otr_trust(irc_t *irc, char **args);
54void cmd_otr_info(irc_t *irc, char **args);
55void cmd_otr_keygen(irc_t *irc, char **args);
56void cmd_otr_forget(irc_t *irc, char **args);
57
58const command_t otr_commands[] = {
59        { "connect",     1, &cmd_otr_connect,    0 },
60        { "disconnect",  1, &cmd_otr_disconnect, 0 },
61        { "smp",         2, &cmd_otr_smp,        0 },
62        { "trust",       6, &cmd_otr_trust,      0 },
63        { "info",        0, &cmd_otr_info,       0 },
64        { "keygen",      1, &cmd_otr_keygen,     0 },
65        { "forget",      2, &cmd_otr_forget,     0 },
66        { NULL }
67};
68
69
70/** misc. helpers/subroutines: **/
71
72/* start background thread to generate a (new) key for a given account */
73void otr_keygen(irc_t *irc, const char *handle, const char *protocol);
74
75/* keygen thread main func */
76gpointer otr_keygen_thread_func(gpointer data);
77
78/* mainloop handler for when keygen thread finishes */
79gboolean keygen_finish_handler(gpointer data, gint fd, b_input_condition cond);
80
81/* data to be passed to otr_keygen_thread_func */
82struct kgdata {
83        irc_t *irc;            /* access to OTR userstate */
84        char *keyfile;         /* free me! */
85        const char *handle;      /* don't free! */
86        const char *protocol;    /* don't free! */
87        GMutex *mutex;         /* lock for the 'done' flag, free me! */
88        int done;              /* is the thread done? */
89        gcry_error_t result;   /* return value of otrl_privkey_generate */
90};
91
92/* some yes/no handlers */
93void yes_keygen(gpointer w, void *data);
94void yes_forget_fingerprint(gpointer w, void *data);
95void yes_forget_context(gpointer w, void *data);
96void yes_forget_key(gpointer w, void *data);
97
98/* helper to make sure accountname and protocol match the incoming "opdata" */
99struct im_connection *check_imc(void *opdata, const char *accountname,
100        const char *protocol);
101
102/* determine the nick for a given handle/protocol pair
103   returns "handle/protocol" if not found */
104const char *peernick(irc_t *irc, const char *handle, const char *protocol);
105
106/* turn a hexadecimal digit into its numerical value */
107int hexval(char a);
108
109/* determine the user_t for a given handle/protocol pair
110   returns NULL if not found */
111user_t *peeruser(irc_t *irc, const char *handle, const char *protocol);
112
113/* handle SMP TLVs from a received message */
114void otr_handle_smp(struct im_connection *ic, const char *handle, OtrlTLV *tlvs);
115
116/* update op/voice flag of given user according to encryption state and settings
117   returns 0 if neither op_buddies nor voice_buddies is set to "encrypted",
118   i.e. msgstate should be announced seperately */
119int otr_update_modeflags(irc_t *irc, user_t *u);
120
121/* show general info about the OTR subsystem; called by 'otr info' */
122void show_general_otr_info(irc_t *irc);
123
124/* show info about a given OTR context */
125void show_otr_context_info(irc_t *irc, ConnContext *ctx);
126
127/* show the list of fingerprints associated with a given context */
128void show_fingerprints(irc_t *irc, ConnContext *ctx);
129
130/* find a fingerprint by prefix (given as any number of hex strings) */
131Fingerprint *match_fingerprint(irc_t *irc, ConnContext *ctx, const char **args);
132
133/* find a private key by fingerprint prefix (given as any number of hex strings) */
134OtrlPrivKey *match_privkey(irc_t *irc, const char **args);
135
136/* to log out accounts during keygen */
137extern void cmd_account(irc_t *irc, char **cmd);
138
139
140/*** routines declared in otr.h: ***/
141
142void otr_init(void)
143{
144        if(!g_thread_supported()) g_thread_init(NULL);
145        OTRL_INIT;
146       
147        /* fill global OtrlMessageAppOps */
148        global.otr_ops.policy = &op_policy;
149        global.otr_ops.create_privkey = &op_create_privkey;
150        global.otr_ops.is_logged_in = &op_is_logged_in;
151        global.otr_ops.inject_message = &op_inject_message;
152        global.otr_ops.notify = NULL;
153        global.otr_ops.display_otr_message = &op_display_otr_message;
154        global.otr_ops.update_context_list = NULL;
155        global.otr_ops.protocol_name = NULL;
156        global.otr_ops.protocol_name_free = NULL;
157        global.otr_ops.new_fingerprint = &op_new_fingerprint;
158        global.otr_ops.write_fingerprints = &op_write_fingerprints;
159        global.otr_ops.gone_secure = &op_gone_secure;
160        global.otr_ops.gone_insecure = &op_gone_insecure;
161        global.otr_ops.still_secure = &op_still_secure;
162        global.otr_ops.log_message = &op_log_message;
163        global.otr_ops.max_message_size = &op_max_message_size;
164        global.otr_ops.account_name = &op_account_name;
165        global.otr_ops.account_name_free = NULL;
166}
167
168/* Notice on the otr_mutex:
169
170   The incoming/outgoing message handlers try to lock the otr_mutex. If they succeed,
171   this will prevent a concurrent keygen (possibly spawned by that very command)
172   from messing up the userstate. If the lock fails, that means there already is
173   a keygen in progress. Instead of blocking for an unknown time, they
174   will bail out gracefully, informing the user of this temporary "coma".
175   TODO: Hold back incoming/outgoing messages and process them when keygen completes?
176
177   The other routines do not lock the otr_mutex themselves, it is done as a
178   catch-all in the root command handler. Rationale:
179     a) it's easy to code
180     b) it makes it obvious that no command can get its userstate corrupted
181     c) the "irc" struct is readily available there for feedback to the user
182 */
183
184void otr_load(irc_t *irc)
185{
186        char s[512];
187        account_t *a;
188        gcry_error_t e;
189
190        log_message(LOGLVL_DEBUG, "otr_load '%s'", irc->nick);
191
192        g_snprintf(s, 511, "%s%s.otr_keys", global.conf->configdir, irc->nick);
193        e = otrl_privkey_read(irc->otr_us, s);
194        if(e && e!=ENOENT) {
195                log_message(LOGLVL_ERROR, "otr load: %s: %s", s, strerror(e));
196        }
197        g_snprintf(s, 511, "%s%s.otr_fprints", global.conf->configdir, irc->nick);
198        e = otrl_privkey_read_fingerprints(irc->otr_us, s, NULL, NULL);
199        if(e && e!=ENOENT) {
200                log_message(LOGLVL_ERROR, "otr load: %s: %s", s, strerror(e));
201        }
202       
203        /* check for otr keys on all accounts */
204        for(a=irc->accounts; a; a=a->next) {
205                otr_check_for_key(a);
206        }
207}
208
209void otr_save(irc_t *irc)
210{
211        char s[512];
212        gcry_error_t e;
213
214        log_message(LOGLVL_DEBUG, "otr_save '%s'", irc->nick);
215
216        g_snprintf(s, 511, "%s%s.otr_fprints", global.conf->configdir, irc->nick);
217        e = otrl_privkey_write_fingerprints(irc->otr_us, s);
218        if(e) {
219                log_message(LOGLVL_ERROR, "otr save: %s: %s", s, strerror(e));
220        }
221}
222
223void otr_remove(const char *nick)
224{
225        char s[512];
226       
227        log_message(LOGLVL_DEBUG, "otr_remove '%s'", nick);
228
229        g_snprintf(s, 511, "%s%s.otr_keys", global.conf->configdir, nick);
230        unlink(s);
231        g_snprintf(s, 511, "%s%s.otr_fprints", global.conf->configdir, nick);
232        unlink(s);
233}
234
235void otr_rename(const char *onick, const char *nnick)
236{
237        char s[512], t[512];
238       
239        log_message(LOGLVL_DEBUG, "otr_rename '%s' -> '%s'", onick, nnick);
240
241        g_snprintf(s, 511, "%s%s.otr_keys", global.conf->configdir, onick);
242        g_snprintf(t, 511, "%s%s.otr_keys", global.conf->configdir, nnick);
243        rename(s,t);
244        g_snprintf(s, 511, "%s%s.otr_fprints", global.conf->configdir, onick);
245        g_snprintf(t, 511, "%s%s.otr_fprints", global.conf->configdir, nnick);
246        rename(s,t);
247}
248
249void otr_check_for_key(account_t *a)
250{
251        irc_t *irc = a->irc;
252        OtrlPrivKey *k;
253       
254        k = otrl_privkey_find(irc->otr_us, a->user, a->prpl->name);
255        if(k) {
256                irc_usermsg(irc, "otr: %s/%s ready",
257                        a->user, a->prpl->name);
258        } else {
259                otr_keygen(irc, a->user, a->prpl->name);
260        }
261}
262
263char *otr_handle_message(struct im_connection *ic, const char *handle, const char *msg)
264{
265        int ignore_msg;
266        char *newmsg = NULL;
267        OtrlTLV *tlvs = NULL;
268        char *colormsg;
269       
270    if(!g_static_rec_mutex_trylock(&ic->irc->otr_mutex)) {
271                irc_usermsg(ic->irc, "otr keygen in progress - msg from %s dropped",
272                        peernick(ic->irc, handle, ic->acc->prpl->name));
273                return NULL;
274        }
275
276        ignore_msg = otrl_message_receiving(ic->irc->otr_us, &global.otr_ops, ic,
277                ic->acc->user, ic->acc->prpl->name, handle, msg, &newmsg,
278                &tlvs, NULL, NULL);
279
280        otr_handle_smp(ic, handle, tlvs);
281       
282        if(ignore_msg) {
283                /* this was an internal OTR protocol message */
284                g_static_rec_mutex_unlock(&ic->irc->otr_mutex);
285                return NULL;
286        } else if(!newmsg) {
287                /* this was a non-OTR message */
288                g_static_rec_mutex_unlock(&ic->irc->otr_mutex);
289                return g_strdup(msg);
290        } else {
291                /* OTR has processed this message */
292                ConnContext *context = otrl_context_find(ic->irc->otr_us, handle,
293                        ic->acc->user, ic->acc->prpl->name, 0, NULL, NULL, NULL);
294                if(context && context->msgstate == OTRL_MSGSTATE_ENCRYPTED &&
295                   set_getbool(&ic->irc->set, "color_encrypted")) {
296                        /* color according to f'print trust */
297                        char color;
298                        const char *trust = context->active_fingerprint->trust;
299                        if(trust && trust[0] != '\0')
300                                color='3';   /* green */
301                        else
302                                color='5';   /* red */
303                        colormsg = g_strdup_printf("\x03%c%s\x0F", color, newmsg);
304                } else {
305                        colormsg = g_strdup(newmsg);
306                }
307                otrl_message_free(newmsg);
308                g_static_rec_mutex_unlock(&ic->irc->otr_mutex);
309                return colormsg;
310        }
311}
312
313int otr_send_message(struct im_connection *ic, const char *handle, const char *msg, int flags)
314{       
315        int st;
316        char *otrmsg = NULL;
317        ConnContext *ctx = NULL;
318       
319    if(!g_static_rec_mutex_trylock(&ic->irc->otr_mutex)) {
320                irc_usermsg(ic->irc, "otr keygen in progress - msg to %s not sent",
321                        peernick(ic->irc, handle, ic->acc->prpl->name));
322                return 1;
323    }
324   
325        st = otrl_message_sending(ic->irc->otr_us, &global.otr_ops, ic,
326                ic->acc->user, ic->acc->prpl->name, handle,
327                msg, NULL, &otrmsg, NULL, NULL);
328        if(st) {
329                g_static_rec_mutex_unlock(&ic->irc->otr_mutex);
330                return st;
331        }
332
333        ctx = otrl_context_find(ic->irc->otr_us,
334                        handle, ic->acc->user, ic->acc->prpl->name,
335                        1, NULL, NULL, NULL);
336
337        if(otrmsg) {
338                if(!ctx) {
339                        otrl_message_free(otrmsg);
340                        g_static_rec_mutex_unlock(&ic->irc->otr_mutex);
341                        return 1;
342                }
343                st = otrl_message_fragment_and_send(&global.otr_ops, ic, ctx,
344                        otrmsg, OTRL_FRAGMENT_SEND_ALL, NULL);
345                otrl_message_free(otrmsg);
346        } else {
347                /* yeah, well, some const casts as usual... ;-) */
348                st = ic->acc->prpl->buddy_msg( ic, (char *)handle, (char *)msg, flags );
349        }
350       
351        g_static_rec_mutex_unlock(&ic->irc->otr_mutex);
352        return st;
353}
354
355void cmd_otr(irc_t *irc, char **args)
356{
357        const command_t *cmd;
358       
359        if(!args[0])
360                return;
361       
362        if(!args[1])
363                return;
364       
365        for(cmd=otr_commands; cmd->command; cmd++) {
366                if(strcmp(cmd->command, args[1]) == 0)
367                        break;
368        }
369       
370        if(!cmd->command) {
371                irc_usermsg(irc, "%s: unknown subcommand \"%s\", see \x02help otr\x02",
372                        args[0], args[1]);
373                return;
374        }
375       
376        if(!args[cmd->required_parameters+1]) {
377                irc_usermsg(irc, "%s %s: not enough arguments (%d req.)",
378                        args[0], args[1], cmd->required_parameters);
379                return;
380        }
381       
382        cmd->execute(irc, args+1);
383}
384
385
386/*** OTR "MessageAppOps" callbacks for global.otr_ui: ***/
387
388OtrlPolicy op_policy(void *opdata, ConnContext *context)
389{
390        /* TODO: OTR policy configurable */
391        return OTRL_POLICY_OPPORTUNISTIC;
392}
393
394void op_create_privkey(void *opdata, const char *accountname,
395        const char *protocol)
396{
397        struct im_connection *ic = check_imc(opdata, accountname, protocol);
398        char *s;
399       
400        log_message(LOGLVL_DEBUG, "op_create_privkey '%s' '%s'", accountname, protocol);
401
402        s = g_strdup_printf("oops, no otr privkey for %s - generate one now?",
403                accountname);
404        query_add(ic->irc, ic, s, yes_keygen, NULL, ic->acc);
405}
406
407int op_is_logged_in(void *opdata, const char *accountname,
408        const char *protocol, const char *recipient)
409{
410        struct im_connection *ic = check_imc(opdata, accountname, protocol);
411        user_t *u;
412
413        log_message(LOGLVL_DEBUG, "op_is_logged_in '%s' '%s' '%s'", accountname, protocol, recipient);
414       
415        /* lookup the user_t for the given recipient */
416        u = user_findhandle(ic, recipient);
417        if(u) {
418                if(u->online)
419                        return 1;
420                else
421                        return 0;
422        } else {
423                return -1;
424        }
425}
426
427void op_inject_message(void *opdata, const char *accountname,
428        const char *protocol, const char *recipient, const char *message)
429{
430        struct im_connection *ic = check_imc(opdata, accountname, protocol);
431
432        log_message(LOGLVL_DEBUG, "op_inject_message '%s' '%s' '%s' '%s'", accountname, protocol, recipient, message);
433
434        if (strcmp(accountname, recipient) == 0) {
435                /* huh? injecting messages to myself? */
436                irc_usermsg(ic->irc, "note to self: %s", message);
437        } else {
438                /* need to drop some consts here :-( */
439                /* TODO: get flags into op_inject_message?! */
440                ic->acc->prpl->buddy_msg(ic, (char *)recipient, (char *)message, 0);
441                /* ignoring return value :-/ */
442        }
443}
444
445int op_display_otr_message(void *opdata, const char *accountname,
446        const char *protocol, const char *username, const char *message)
447{
448        struct im_connection *ic = check_imc(opdata, accountname, protocol);
449        char *msg = g_strdup(message);
450
451        log_message(LOGLVL_DEBUG, "op_display_otr_message '%s' '%s' '%s' '%s'", accountname, protocol, username, message);
452
453        strip_html(msg);
454        irc_usermsg(ic->irc, "otr: %s", msg);
455
456        g_free(msg);
457        return 0;
458}
459
460void op_new_fingerprint(void *opdata, OtrlUserState us,
461        const char *accountname, const char *protocol,
462        const char *username, unsigned char fingerprint[20])
463{
464        struct im_connection *ic = check_imc(opdata, accountname, protocol);
465        char hunam[45];         /* anybody looking? ;-) */
466       
467        otrl_privkey_hash_to_human(hunam, fingerprint);
468        log_message(LOGLVL_DEBUG, "op_new_fingerprint '%s' '%s' '%s' '%s'", accountname, protocol, username, hunam);
469
470        irc_usermsg(ic->irc, "new fingerprint for %s: %s",
471                peernick(ic->irc, username, protocol), hunam);
472}
473
474void op_write_fingerprints(void *opdata)
475{
476        struct im_connection *ic = (struct im_connection *)opdata;
477
478        log_message(LOGLVL_DEBUG, "op_write_fingerprints");
479
480        otr_save(ic->irc);
481}
482
483void op_gone_secure(void *opdata, ConnContext *context)
484{
485        struct im_connection *ic =
486                check_imc(opdata, context->accountname, context->protocol);
487        user_t *u;
488        const char *trust;
489
490        log_message(LOGLVL_DEBUG, "op_gone_secure '%s' '%s' '%s'", context->accountname, context->protocol, context->username);
491
492        u = peeruser(ic->irc, context->username, context->protocol);
493        if(!u) {
494                log_message(LOGLVL_ERROR,
495                        "BUG: otr.c: op_gone_secure: user_t for %s/%s/%s not found!",
496                        context->username, context->protocol, context->accountname);
497                return;
498        }
499       
500        trust = context->active_fingerprint->trust;
501        if(trust && trust[0])
502                u->encrypted = 2;
503        else
504                u->encrypted = 1;
505        if(!otr_update_modeflags(ic->irc, u))
506                irc_usermsg(ic->irc, "conversation with %s is now off the record", u->nick);
507}
508
509void op_gone_insecure(void *opdata, ConnContext *context)
510{
511        struct im_connection *ic =
512                check_imc(opdata, context->accountname, context->protocol);
513        user_t *u;
514
515        log_message(LOGLVL_DEBUG, "op_gone_insecure '%s' '%s' '%s'", context->accountname, context->protocol, context->username);
516
517        u = peeruser(ic->irc, context->username, context->protocol);
518        if(!u) {
519                log_message(LOGLVL_ERROR,
520                        "BUG: otr.c: op_gone_insecure: user_t for %s/%s/%s not found!",
521                        context->username, context->protocol, context->accountname);
522                return;
523        }
524        u->encrypted = 0;
525        if(!otr_update_modeflags(ic->irc, u))
526                irc_usermsg(ic->irc, "conversation with %s is now in the clear", u->nick);
527}
528
529void op_still_secure(void *opdata, ConnContext *context, int is_reply)
530{
531        struct im_connection *ic =
532                check_imc(opdata, context->accountname, context->protocol);
533        user_t *u;
534
535        log_message(LOGLVL_DEBUG, "op_still_secure '%s' '%s' '%s' is_reply=%d",
536                context->accountname, context->protocol, context->username, is_reply);
537
538        u = peeruser(ic->irc, context->username, context->protocol);
539        if(!u) {
540                log_message(LOGLVL_ERROR,
541                        "BUG: otr.c: op_still_secure: user_t for %s/%s/%s not found!",
542                        context->username, context->protocol, context->accountname);
543                return;
544        }
545        if(context->active_fingerprint->trust[0])
546                u->encrypted = 2;
547        else
548                u->encrypted = 1;
549        if(!otr_update_modeflags(ic->irc, u))
550                irc_usermsg(ic->irc, "otr connection with %s has been refreshed", u->nick);
551}
552
553void op_log_message(void *opdata, const char *message)
554{
555        char *msg = g_strdup(message);
556       
557        strip_html(msg);
558        log_message(LOGLVL_INFO, "otr: %s", msg);
559        g_free(msg);
560}
561
562int op_max_message_size(void *opdata, ConnContext *context)
563{
564        struct im_connection *ic =
565                check_imc(opdata, context->accountname, context->protocol);
566
567        return ic->acc->prpl->mms;
568}
569
570const char *op_account_name(void *opdata, const char *account, const char *protocol)
571{
572        struct im_connection *ic = (struct im_connection *)opdata;
573
574        log_message(LOGLVL_DEBUG, "op_account_name '%s' '%s'", account, protocol);
575       
576        return peernick(ic->irc, account, protocol);
577}
578
579
580/*** OTR sub-command handlers ***/
581
582void cmd_otr_disconnect(irc_t *irc, char **args)
583{
584        user_t *u;
585
586        u = user_find(irc, args[1]);
587        if(!u || !u->ic) {
588                irc_usermsg(irc, "%s: unknown user", args[1]);
589                return;
590        }
591       
592        otrl_message_disconnect(irc->otr_us, &global.otr_ops,
593                u->ic, u->ic->acc->user, u->ic->acc->prpl->name, u->handle);
594       
595        /* for some reason, libotr (3.1.0) doesn't do this itself: */
596        if(u->encrypted) {
597                ConnContext *ctx;
598                ctx = otrl_context_find(irc->otr_us, u->handle, u->ic->acc->user,
599                        u->ic->acc->prpl->name, 0, NULL, NULL, NULL);
600                if(ctx)
601                        op_gone_insecure(u->ic, ctx);
602                else /* huh? */
603                        u->encrypted = 0;
604        }
605}
606
607void cmd_otr_connect(irc_t *irc, char **args)
608{
609        user_t *u;
610
611        u = user_find(irc, args[1]);
612        if(!u || !u->ic) {
613                irc_usermsg(irc, "%s: unknown user", args[1]);
614                return;
615        }
616        if(!u->online) {
617                irc_usermsg(irc, "%s is offline", args[1]);
618                return;
619        }
620       
621        imc_buddy_msg(u->ic, u->handle, "?OTR?", 0);
622}
623
624void cmd_otr_smp(irc_t *irc, char **args)
625{
626        user_t *u;
627        ConnContext *ctx;
628       
629        u = user_find(irc, args[1]);
630        if(!u || !u->ic) {
631                irc_usermsg(irc, "%s: unknown user", args[1]);
632                return;
633        }
634        if(!u->online) {
635                irc_usermsg(irc, "%s is offline", args[1]);
636                return;
637        }
638       
639        ctx = otrl_context_find(irc->otr_us, u->handle,
640                u->ic->acc->user, u->ic->acc->prpl->name, 1, NULL, NULL, NULL);
641        if(!ctx) {
642                /* huh? out of memory or what? */
643                return;
644        }
645
646        if(ctx->smstate->nextExpected != OTRL_SMP_EXPECT1) {
647                log_message(LOGLVL_INFO,
648                        "SMP already in phase %d, sending abort before reinitiating",
649                        ctx->smstate->nextExpected+1);
650                otrl_message_abort_smp(irc->otr_us, &global.otr_ops, u->ic, ctx);
651                otrl_sm_state_free(ctx->smstate);
652        }
653       
654        /* warning: the following assumes that smstates are cleared whenever an SMP
655           is completed or aborted! */ 
656        if(ctx->smstate->secret == NULL) {
657                irc_usermsg(irc, "smp: initiating with %s...", u->nick);
658                otrl_message_initiate_smp(irc->otr_us, &global.otr_ops,
659                        u->ic, ctx, (unsigned char *)args[2], strlen(args[2]));
660                /* smp is now in EXPECT2 */
661        } else {
662                /* if we're still in EXPECT1 but smstate is initialized, we must have
663                   received the SMP1, so let's issue a response */
664                irc_usermsg(irc, "smp: responding to %s...", u->nick);
665                otrl_message_respond_smp(irc->otr_us, &global.otr_ops,
666                        u->ic, ctx, (unsigned char *)args[2], strlen(args[2]));
667                /* smp is now in EXPECT3 */
668        }
669}
670
671void cmd_otr_trust(irc_t *irc, char **args)
672{
673        user_t *u;
674        ConnContext *ctx;
675        unsigned char raw[20];
676        Fingerprint *fp;
677        int i,j;
678       
679        u = user_find(irc, args[1]);
680        if(!u || !u->ic) {
681                irc_usermsg(irc, "%s: unknown user", args[1]);
682                return;
683        }
684       
685        ctx = otrl_context_find(irc->otr_us, u->handle,
686                u->ic->acc->user, u->ic->acc->prpl->name, 0, NULL, NULL, NULL);
687        if(!ctx) {
688                irc_usermsg(irc, "%s: no otr context with user", args[1]);
689                return;
690        }
691       
692        /* convert given fingerprint to raw representation */
693        for(i=0; i<5; i++) {
694                for(j=0; j<4; j++) {
695                        char *p = args[2+i]+(2*j);
696                        char *q = p+1;
697                        int x, y;
698                       
699                        if(!*p || !*q) {
700                                irc_usermsg(irc, "failed: truncated fingerprint block %d", i+1);
701                                return;
702                        }
703                       
704                        x = hexval(*p);
705                        y = hexval(*q);
706                        if(x<0) {
707                                irc_usermsg(irc, "failed: %d. hex digit of block %d out of range", 2*j+1, i+1);
708                                return;
709                        }
710                        if(y<0) {
711                                irc_usermsg(irc, "failed: %d. hex digit of block %d out of range", 2*j+2, i+1);
712                                return;
713                        }
714
715                        raw[i*4+j] = x*16 + y;
716                }
717        }
718        fp = otrl_context_find_fingerprint(ctx, raw, 0, NULL);
719        if(!fp) {
720                irc_usermsg(irc, "failed: no such fingerprint for %s", args[1]);
721        } else {
722                char *trust = args[7] ? args[7] : "affirmed";
723                otrl_context_set_trust(fp, trust);
724                irc_usermsg(irc, "fingerprint match, trust set to \"%s\"", trust);
725                if(u->encrypted)
726                        u->encrypted = 2;
727                otr_update_modeflags(irc, u);
728        }
729}
730
731void cmd_otr_info(irc_t *irc, char **args)
732{
733        if(!args[1]) {
734                show_general_otr_info(irc);
735        } else {
736                char *arg = g_strdup(args[1]);
737                char *myhandle, *handle, *protocol;
738                ConnContext *ctx;
739               
740                /* interpret arg as 'user/protocol/account' if possible */
741                protocol = strchr(arg, '/');
742                if(protocol) {
743                        *(protocol++) = '\0';
744                        myhandle = strchr(protocol, '/');
745                }
746                if(protocol && myhandle) {
747                        *(myhandle++) = '\0';
748                        handle = arg;
749                        ctx = otrl_context_find(irc->otr_us, handle, myhandle, protocol, 0, NULL, NULL, NULL);
750                        if(!ctx) {
751                                irc_usermsg(irc, "no such context");
752                                g_free(arg);
753                                return;
754                        }
755                } else {
756                        user_t *u = user_find(irc, args[1]);
757                        if(!u || !u->ic) {
758                                irc_usermsg(irc, "%s: unknown user", args[1]);
759                                g_free(arg);
760                                return;
761                        }
762                        ctx = otrl_context_find(irc->otr_us, u->handle, u->ic->acc->user,
763                                u->ic->acc->prpl->name, 0, NULL, NULL, NULL);
764                        if(!ctx) {
765                                irc_usermsg(irc, "no otr context with %s", args[1]);
766                                g_free(arg);
767                                return;
768                        }
769                }
770       
771                /* show how we resolved the (nick) argument, if we did */
772                if(handle!=arg) {
773                        irc_usermsg(irc, "%s is %s/%s; we are %s/%s to them", args[1],
774                                ctx->username, ctx->protocol, ctx->accountname, ctx->protocol);
775                }
776                show_otr_context_info(irc, ctx);
777                g_free(arg);
778        }
779}
780
781void cmd_otr_keygen(irc_t *irc, char **args)
782{
783        int i, n;
784        account_t *a;
785       
786        n = atoi(args[1]);
787        if(n<0 || (!n && strcmp(args[1], "0"))) {
788                irc_usermsg(irc, "%s: invalid account number", args[1]);
789                return;
790        }
791       
792        a = irc->accounts;
793        for(i=0; i<n && a; i++, a=a->next);
794        if(!a) {
795                irc_usermsg(irc, "%s: no such account", args[1]);
796                return;
797        }
798       
799        if(otrl_privkey_find(irc->otr_us, a->user, a->prpl->name)) {
800                char *s = g_strdup_printf("account %d already has a key, replace it?", n);
801                query_add(irc, NULL, s, yes_keygen, NULL, a);
802        } else {
803                otr_keygen(irc, a->user, a->prpl->name);
804        }
805}
806
807void yes_forget_fingerprint(gpointer w, void *data)
808{
809        irc_t *irc = (irc_t *)w;
810        Fingerprint *fp = (Fingerprint *)data;
811       
812        if(fp == fp->context->active_fingerprint) {
813                irc_usermsg(irc, "that fingerprint is active, terminate otr connection first");
814                return;
815        }
816               
817        otrl_context_forget_fingerprint(fp, 0);
818}
819
820void yes_forget_context(gpointer w, void *data)
821{
822        irc_t *irc = (irc_t *)w;
823        ConnContext *ctx = (ConnContext *)data;
824       
825        if(ctx->msgstate == OTRL_MSGSTATE_ENCRYPTED) {
826                irc_usermsg(irc, "active otr connection with %s, terminate it first",
827                        peernick(irc, ctx->username, ctx->protocol));
828                return;
829        }
830               
831        if(ctx->msgstate == OTRL_MSGSTATE_FINISHED)
832                otrl_context_force_plaintext(ctx);
833        otrl_context_forget(ctx);
834}
835
836void yes_forget_key(gpointer w, void *data)
837{
838        OtrlPrivKey *key = (OtrlPrivKey *)data;
839       
840        /* FIXME: For some reason which /completely eludes me/, this call keeps
841           barfing on the gcry_sexp_release inside (invalid pointer free). */
842        otrl_privkey_forget(key);
843}
844
845void cmd_otr_forget(irc_t *irc, char **args)
846{
847        if(!strcmp(args[1], "fingerprint"))
848        {
849                user_t *u;
850                ConnContext *ctx;
851                Fingerprint *fp;
852                char human[54];
853                char *s;
854               
855                if(!args[3]) {
856                        irc_usermsg(irc, "otr %s %s: not enough arguments (2 req.)", args[0], args[1]);
857                        return;
858                }
859               
860                u = user_find(irc, args[2]);
861                if(!u || !u->ic) {
862                        irc_usermsg(irc, "%s: unknown user", args[2]);
863                        return;
864                }
865               
866                ctx = otrl_context_find(irc->otr_us, u->handle, u->ic->acc->user,
867                        u->ic->acc->prpl->name, 0, NULL, NULL, NULL);
868                if(!ctx) {
869                        irc_usermsg(irc, "no otr context with %s", args[2]);
870                        return;
871                }
872               
873                fp = match_fingerprint(irc, ctx, ((const char **)args)+3);
874                if(!fp) {
875                        /* match_fingerprint does error messages */
876                        return;
877                }
878               
879                if(fp == ctx->active_fingerprint) {
880                        irc_usermsg(irc, "that fingerprint is active, terminate otr connection first");
881                        return;
882                }
883               
884                otrl_privkey_hash_to_human(human, fp->fingerprint);
885                s = g_strdup_printf("about to forget fingerprint %s, are you sure?", human);
886                query_add(irc, NULL, s, yes_forget_fingerprint, NULL, fp);
887        }
888       
889        else if(!strcmp(args[1], "context"))
890        {
891                user_t *u;
892                ConnContext *ctx;
893                char *s;
894               
895                u = user_find(irc, args[2]);
896                if(!u || !u->ic) {
897                        irc_usermsg(irc, "%s: unknown user", args[2]);
898                        return;
899                }
900               
901                ctx = otrl_context_find(irc->otr_us, u->handle, u->ic->acc->user,
902                        u->ic->acc->prpl->name, 0, NULL, NULL, NULL);
903                if(!ctx) {
904                        irc_usermsg(irc, "no otr context with %s", args[2]);
905                        return;
906                }
907               
908                if(ctx->msgstate == OTRL_MSGSTATE_ENCRYPTED) {
909                        irc_usermsg(irc, "active otr connection with %s, terminate it first", args[2]);
910                        return;
911                }
912               
913                s = g_strdup_printf("about to forget otr data about %s, are you sure?", args[2]);
914                query_add(irc, NULL, s, yes_forget_context, NULL, ctx);
915        }
916       
917        else if(!strcmp(args[1], "key"))
918        {
919                OtrlPrivKey *key;
920                char *s;
921               
922                key = match_privkey(irc, ((const char **)args)+2);
923                if(!key) {
924                        /* match_privkey does error messages */
925                        return;
926                }
927               
928                /* TODO: Find out why 'otr forget key' barfs (cf. yes_forget_key) */
929                irc_usermsg(irc, "otr %s %s: not implemented, please edit \x02%s%s.otr_keys\x02 manually :-/",
930                        args[0], args[1], global.conf->configdir, irc->nick);
931                return;
932
933                s = g_strdup_printf("about to forget the private key for %s/%s, are you sure?",
934                        key->accountname, key->protocol);
935                query_add(irc, NULL, s, yes_forget_key, NULL, key);
936        }
937       
938        else
939        {
940                irc_usermsg(irc, "otr %s: unknown subcommand \"%s\", see \x02help otr forget\x02",
941                        args[0], args[1]);
942        }
943}
944
945
946/*** local helpers / subroutines: ***/
947
948/* Socialist Millionaires' Protocol */
949void otr_handle_smp(struct im_connection *ic, const char *handle, OtrlTLV *tlvs)
950{
951        irc_t *irc = ic->irc;
952        OtrlUserState us = irc->otr_us;
953        OtrlMessageAppOps *ops = &global.otr_ops;
954        OtrlTLV *tlv = NULL;
955        ConnContext *context;
956        NextExpectedSMP nextMsg;
957        user_t *u;
958
959        u = user_findhandle(ic, handle);
960        if(!u) return;
961        context = otrl_context_find(us, handle,
962                ic->acc->user, ic->acc->prpl->name, 1, NULL, NULL, NULL);
963        if(!context) {
964                /* huh? out of memory or what? */
965                return;
966        }
967        nextMsg = context->smstate->nextExpected;
968
969        tlv = otrl_tlv_find(tlvs, OTRL_TLV_SMP1);
970        if (tlv) {
971                if (nextMsg != OTRL_SMP_EXPECT1) {
972                        irc_usermsg(irc, "smp %s: spurious SMP1 received, aborting", u->nick);
973                        otrl_message_abort_smp(us, ops, u->ic, context);
974                        otrl_sm_state_free(context->smstate);
975                } else {
976                        irc_usermsg(irc, "smp: initiated by %s"
977                                " - respond with \x02otr smp %s <secret>\x02",
978                                u->nick, u->nick);
979                        /* smp stays in EXPECT1 until user responds */
980                }
981        }
982        tlv = otrl_tlv_find(tlvs, OTRL_TLV_SMP2);
983        if (tlv) {
984                if (nextMsg != OTRL_SMP_EXPECT2) {
985                        irc_usermsg(irc, "smp %s: spurious SMP2 received, aborting", u->nick);
986                        otrl_message_abort_smp(us, ops, u->ic, context);
987                        otrl_sm_state_free(context->smstate);
988                } else {
989                        /* SMP2 received, otrl_message_receiving will have sent SMP3 */
990                        context->smstate->nextExpected = OTRL_SMP_EXPECT4;
991                }
992        }
993        tlv = otrl_tlv_find(tlvs, OTRL_TLV_SMP3);
994        if (tlv) {
995                if (nextMsg != OTRL_SMP_EXPECT3) {
996                        irc_usermsg(irc, "smp %s: spurious SMP3 received, aborting", u->nick);
997                        otrl_message_abort_smp(us, ops, u->ic, context);
998                        otrl_sm_state_free(context->smstate);
999                } else {
1000                        /* SMP3 received, otrl_message_receiving will have sent SMP4 and set fp trust */
1001                        const char *trust = context->active_fingerprint->trust;
1002                        if(!trust || trust[0]=='\0') {
1003                                irc_usermsg(irc, "smp %s: secrets did not match, fingerprint not trusted",
1004                                        u->nick);
1005                        } else {
1006                                irc_usermsg(irc, "smp %s: secrets proved equal, fingerprint trusted",
1007                                        u->nick);
1008                        }
1009                        otrl_sm_state_free(context->smstate);
1010                        /* smp is in back in EXPECT1 */
1011                }
1012        }
1013        tlv = otrl_tlv_find(tlvs, OTRL_TLV_SMP4);
1014        if (tlv) {
1015                if (nextMsg != OTRL_SMP_EXPECT4) {
1016                        irc_usermsg(irc, "smp %s: spurious SMP4 received, aborting", u->nick);
1017                        otrl_message_abort_smp(us, ops, u->ic, context);
1018                        otrl_sm_state_free(context->smstate);
1019                } else {
1020                        /* SMP4 received, otrl_message_receiving will have set fp trust */
1021                        const char *trust = context->active_fingerprint->trust;
1022                        if(!trust || trust[0]=='\0') {
1023                                irc_usermsg(irc, "smp %s: secrets did not match, fingerprint not trusted",
1024                                        u->nick);
1025                        } else {
1026                                irc_usermsg(irc, "smp %s: secrets proved equal, fingerprint trusted",
1027                                        u->nick);
1028                        }
1029                        otrl_sm_state_free(context->smstate);
1030                        /* smp is in back in EXPECT1 */
1031                }
1032        }
1033        tlv = otrl_tlv_find(tlvs, OTRL_TLV_SMP_ABORT);
1034        if (tlv) {
1035                irc_usermsg(irc, "smp: received abort from %s", u->nick);
1036                otrl_sm_state_free(context->smstate);
1037                /* smp is in back in EXPECT1 */
1038        }
1039}
1040
1041/* helper to assert that account and protocol names given to ops below always
1042   match the im_connection passed through as opdata */
1043struct im_connection *check_imc(void *opdata, const char *accountname,
1044        const char *protocol)
1045{
1046        struct im_connection *ic = (struct im_connection *)opdata;
1047
1048        if (strcmp(accountname, ic->acc->user) != 0) {
1049                log_message(LOGLVL_WARNING,
1050                        "otr: internal account name mismatch: '%s' vs '%s'",
1051                        accountname, ic->acc->user);
1052        }
1053        if (strcmp(protocol, ic->acc->prpl->name) != 0) {
1054                log_message(LOGLVL_WARNING,
1055                        "otr: internal protocol name mismatch: '%s' vs '%s'",
1056                        protocol, ic->acc->prpl->name);
1057        }
1058       
1059        return ic;
1060}
1061
1062user_t *peeruser(irc_t *irc, const char *handle, const char *protocol)
1063{
1064        user_t *u;
1065       
1066        log_message(LOGLVL_DEBUG, "peeruser '%s' '%s'", handle, protocol);
1067       
1068        for(u=irc->users; u; u=u->next) {
1069                struct prpl *prpl;
1070                if(!u->ic || !u->handle)
1071                        continue;
1072                prpl = u->ic->acc->prpl;
1073                if(strcmp(prpl->name, protocol) == 0
1074                        && prpl->handle_cmp(u->handle, handle) == 0) {
1075                        return u;
1076                }
1077        }
1078       
1079        return NULL;
1080}
1081
1082int hexval(char a)
1083{
1084        int x=tolower(a);
1085       
1086        if(x>='a' && x<='f')
1087                x = x - 'a' + 10;
1088        else if(x>='0' && x<='9')
1089                x = x - '0';
1090        else
1091                return -1;
1092       
1093        return x;
1094}
1095
1096const char *peernick(irc_t *irc, const char *handle, const char *protocol)
1097{
1098        static char fallback[512];
1099       
1100        user_t *u = peeruser(irc, handle, protocol);
1101        if(u) {
1102                return u->nick;
1103        } else {
1104                g_snprintf(fallback, 511, "%s/%s", handle, protocol);
1105                return fallback;
1106        }
1107}
1108
1109int otr_update_modeflags(irc_t *irc, user_t *u)
1110{
1111        char *vb = set_getstr(&irc->set, "voice_buddies");
1112        char *hb = set_getstr(&irc->set, "halfop_buddies");
1113        char *ob = set_getstr(&irc->set, "op_buddies");
1114        int encrypted = u->encrypted;
1115        int trusted = u->encrypted > 1;
1116        char flags[7];
1117        int nflags=0;
1118        char *p = flags;
1119        char *from;
1120        int i;
1121       
1122        if(!strcmp(vb, "encrypted")) {
1123                *(p++) = encrypted ? '+' : '-';
1124                *(p++) = 'v';
1125                nflags++;
1126        } else if(!strcmp(vb, "trusted")) {
1127                *(p++) = trusted ? '+' : '-';
1128                *(p++) = 'v';
1129                nflags++;
1130        }
1131        if(!strcmp(hb, "encrypted")) {
1132                *(p++) = encrypted ? '+' : '-';
1133                *(p++) = 'h';
1134                nflags++;
1135        } else if(!strcmp(hb, "trusted")) {
1136                *(p++) = trusted ? '+' : '-';
1137                *(p++) = 'h';
1138                nflags++;
1139        }
1140        if(!strcmp(ob, "encrypted")) {
1141                *(p++) = encrypted ? '+' : '-';
1142                *(p++) = 'o';
1143                nflags++;
1144        } else if(!strcmp(ob, "trusted")) {
1145                *(p++) = trusted ? '+' : '-';
1146                *(p++) = 'o';
1147                nflags++;
1148        }
1149        *p = '\0';
1150       
1151        p = g_malloc(nflags * (strlen(u->nick)+1) + 1);
1152        *p = '\0';
1153        if(!p)
1154                return 0;
1155        for(i=0; i<nflags; i++) {
1156                strcat(p, " ");
1157                strcat(p, u->nick);
1158        }
1159        if(set_getbool(&irc->set, "simulate_netsplit"))
1160                from = g_strdup(irc->myhost);
1161        else
1162                from = g_strdup_printf("%s!%s@%s", irc->mynick, irc->mynick, irc->myhost);
1163        irc_write(irc, ":%s MODE %s %s%s", from, irc->channel, flags, p);
1164        g_free(from);
1165        g_free(p);
1166               
1167        return 1;
1168}
1169
1170void show_fingerprints(irc_t *irc, ConnContext *ctx)
1171{
1172        char human[45];
1173        Fingerprint *fp;
1174        const char *trust;
1175        int count=0;
1176       
1177        for(fp=&ctx->fingerprint_root; fp; fp=fp->next) {
1178                if(!fp->fingerprint)
1179                        continue;
1180                count++;
1181                otrl_privkey_hash_to_human(human, fp->fingerprint);
1182                if(!fp->trust || fp->trust[0] == '\0') {
1183                        trust="untrusted";
1184                } else {
1185                        trust=fp->trust;
1186                }
1187                if(fp == ctx->active_fingerprint) {
1188                        irc_usermsg(irc, \x02%s (%s)\x02", human, trust);
1189                } else {
1190                        irc_usermsg(irc, "  %s (%s)", human, trust);
1191                }
1192        }
1193        if(count==0)
1194                irc_usermsg(irc, "  no fingerprints");
1195}
1196
1197Fingerprint *match_fingerprint(irc_t *irc, ConnContext *ctx, const char **args)
1198{
1199        Fingerprint *fp, *fp2;
1200        char human[45];
1201        char prefix[45], *p;
1202        int n;
1203        int i,j;
1204       
1205        /* assemble the args into a prefix in standard "human" form */
1206        n=0;
1207        p=prefix;
1208        for(i=0; args[i]; i++) {
1209                for(j=0; args[i][j]; j++) {
1210                        char c = toupper(args[i][j]);
1211                       
1212                        if(n>=40) {
1213                                irc_usermsg(irc, "too many fingerprint digits given, expected at most 40");
1214                                return NULL;
1215                        }
1216                       
1217                        if( (c>='A' && c<='F') || (c>='0' && c<='9') ) {
1218                                *(p++) = c;
1219                        } else {
1220                                irc_usermsg(irc, "invalid hex digit '%c' in block %d", args[i][j], i+1);
1221                                return NULL;
1222                        }
1223                       
1224                        n++;
1225                        if(n%8 == 0)
1226                                *(p++) = ' ';
1227                }
1228        }
1229        *p = '\0';
1230        log_message(LOGLVL_DEBUG, "match_fingerprint '%s'", prefix);
1231        log_message(LOGLVL_DEBUG, "n=%d strlen(prefix)=%d", n, strlen(prefix));
1232       
1233        /* find first fingerprint with the given prefix */
1234        n = strlen(prefix);
1235        for(fp=&ctx->fingerprint_root; fp; fp=fp->next) {
1236                if(!fp->fingerprint)
1237                        continue;
1238                otrl_privkey_hash_to_human(human, fp->fingerprint);
1239                if(!strncmp(prefix, human, n))
1240                        break;
1241        }
1242        if(!fp) {
1243                irc_usermsg(irc, "%s: no match", prefix);
1244                return NULL;
1245        }
1246       
1247        /* make sure the match, if any, is unique */
1248        for(fp2=fp->next; fp2; fp2=fp2->next) {
1249                if(!fp2->fingerprint)
1250                        continue;
1251                otrl_privkey_hash_to_human(human, fp2->fingerprint);
1252                if(!strncmp(prefix, human, n))
1253                        break;
1254        }
1255        if(fp2) {
1256                irc_usermsg(irc, "%s: multiple matches", prefix);
1257                return NULL;
1258        }
1259       
1260        return fp;
1261}
1262
1263OtrlPrivKey *match_privkey(irc_t *irc, const char **args)
1264{
1265        OtrlPrivKey *k, *k2;
1266        char human[45];
1267        char prefix[45], *p;
1268        int n;
1269        int i,j;
1270       
1271        /* assemble the args into a prefix in standard "human" form */
1272        n=0;
1273        p=prefix;
1274        for(i=0; args[i]; i++) {
1275                for(j=0; args[i][j]; j++) {
1276                        char c = toupper(args[i][j]);
1277                       
1278                        if(n>=40) {
1279                                irc_usermsg(irc, "too many fingerprint digits given, expected at most 40");
1280                                return NULL;
1281                        }
1282                       
1283                        if( (c>='A' && c<='F') || (c>='0' && c<='9') ) {
1284                                *(p++) = c;
1285                        } else {
1286                                irc_usermsg(irc, "invalid hex digit '%c' in block %d", args[i][j], i+1);
1287                                return NULL;
1288                        }
1289                       
1290                        n++;
1291                        if(n%8 == 0)
1292                                *(p++) = ' ';
1293                }
1294        }
1295        *p = '\0';
1296        log_message(LOGLVL_DEBUG, "match_privkey '%s'", prefix);
1297        log_message(LOGLVL_DEBUG, "n=%d strlen(prefix)=%d", n, strlen(prefix));
1298       
1299        /* find first key which matches the given prefix */
1300        n = strlen(prefix);
1301        for(k=irc->otr_us->privkey_root; k; k=k->next) {
1302                p = otrl_privkey_fingerprint(irc->otr_us, human, k->accountname, k->protocol);
1303                if(!p) /* gah! :-P */
1304                        continue;
1305                if(!strncmp(prefix, human, n))
1306                        break;
1307        }
1308        if(!k) {
1309                irc_usermsg(irc, "%s: no match", prefix);
1310                return NULL;
1311        }
1312       
1313        /* make sure the match, if any, is unique */
1314        for(k2=k->next; k2; k2=k2->next) {
1315                p = otrl_privkey_fingerprint(irc->otr_us, human, k2->accountname, k2->protocol);
1316                if(!p) /* gah! :-P */
1317                        continue;
1318                if(!strncmp(prefix, human, n))
1319                        break;
1320        }
1321        if(k2) {
1322                irc_usermsg(irc, "%s: multiple matches", prefix);
1323                return NULL;
1324        }
1325       
1326        return k;
1327}
1328
1329void show_general_otr_info(irc_t *irc)
1330{
1331        ConnContext *ctx;
1332        OtrlPrivKey *key;
1333        char human[45];
1334
1335        /* list all privkeys */
1336        irc_usermsg(irc, "\x1fprivate keys:\x1f");
1337        for(key=irc->otr_us->privkey_root; key; key=key->next) {
1338                const char *hash;
1339               
1340                switch(key->pubkey_type) {
1341                case OTRL_PUBKEY_TYPE_DSA:
1342                        irc_usermsg(irc, "  %s/%s - DSA", key->accountname, key->protocol);
1343                        break;
1344                default:
1345                        irc_usermsg(irc, "  %s/%s - type %d", key->accountname, key->protocol,
1346                                key->pubkey_type);
1347                }
1348
1349                /* No, it doesn't make much sense to search for the privkey again by
1350                   account/protocol, but libotr currently doesn't provide a direct routine
1351                   for hashing a given 'OtrlPrivKey'... */
1352                hash = otrl_privkey_fingerprint(irc->otr_us, human, key->accountname, key->protocol);
1353                if(hash) /* should always succeed */
1354                        irc_usermsg(irc, "    %s", human);
1355        }
1356
1357        /* list all contexts */
1358        irc_usermsg(irc, "%s", "");
1359        irc_usermsg(irc, "\x1f" "connection contexts:\x1f (bold=currently encrypted)");
1360        for(ctx=irc->otr_us->context_root; ctx; ctx=ctx->next) {\
1361                user_t *u;
1362                char *userstring;
1363               
1364                u = peeruser(irc, ctx->username, ctx->protocol);
1365                if(u)
1366                        userstring = g_strdup_printf("%s/%s/%s (%s)",
1367                                ctx->username, ctx->protocol, ctx->accountname, u->nick);
1368                else
1369                        userstring = g_strdup_printf("%s/%s/%s",
1370                                ctx->username, ctx->protocol, ctx->accountname);
1371               
1372                if(ctx->msgstate == OTRL_MSGSTATE_ENCRYPTED) {
1373                        irc_usermsg(irc, \x02%s\x02", userstring);
1374                } else {
1375                        irc_usermsg(irc, "  %s", userstring);
1376                }
1377               
1378                g_free(userstring);
1379        }
1380}
1381
1382void show_otr_context_info(irc_t *irc, ConnContext *ctx)
1383{
1384        switch(ctx->otr_offer) {
1385        case OFFER_NOT:
1386                irc_usermsg(irc, "  otr offer status: none sent");
1387                break;
1388        case OFFER_SENT:
1389                irc_usermsg(irc, "  otr offer status: awaiting reply");
1390                break;
1391        case OFFER_ACCEPTED:
1392                irc_usermsg(irc, "  otr offer status: accepted our offer");
1393                break;
1394        case OFFER_REJECTED:
1395                irc_usermsg(irc, "  otr offer status: ignored our offer");
1396                break;
1397        default:
1398                irc_usermsg(irc, "  otr offer status: %d", ctx->otr_offer);
1399        }
1400
1401        switch(ctx->msgstate) {
1402        case OTRL_MSGSTATE_PLAINTEXT:
1403                irc_usermsg(irc, "  connection state: cleartext");
1404                break;
1405        case OTRL_MSGSTATE_ENCRYPTED:
1406                irc_usermsg(irc, "  connection state: encrypted (v%d)", ctx->protocol_version);
1407                break;
1408        case OTRL_MSGSTATE_FINISHED:
1409                irc_usermsg(irc, "  connection state: shut down");
1410                break;
1411        default:
1412                irc_usermsg(irc, "  connection state: %d", ctx->msgstate);
1413        }
1414
1415    irc_usermsg(irc, "  fingerprints: (bold=active)"); 
1416        show_fingerprints(irc, ctx);
1417}
1418
1419void otr_keygen(irc_t *irc, const char *handle, const char *protocol)
1420{
1421        char *account_off[] = {"account", "off", NULL};
1422        GError *err;
1423        GThread *thr;
1424        struct kgdata *kg;
1425        gint ev;
1426       
1427        kg = g_new0(struct kgdata, 1);
1428        if(!kg) {
1429                irc_usermsg(irc, "otr keygen failed: out of memory");
1430                return;
1431        }
1432
1433        /* Assemble the job description to be passed to thread and handler */
1434        kg->irc = irc;
1435        kg->keyfile = g_strdup_printf("%s%s.otr_keys", global.conf->configdir, kg->irc->nick);
1436        if(!kg->keyfile) {
1437                irc_usermsg(irc, "otr keygen failed: out of memory");
1438                g_free(kg);
1439                return;
1440        }
1441        kg->handle = handle;
1442        kg->protocol = protocol;
1443        kg->mutex = g_mutex_new();
1444        if(!kg->mutex) {
1445                irc_usermsg(irc, "otr keygen failed: couldn't create mutex");
1446                g_free(kg->keyfile);
1447                g_free(kg);
1448                return;
1449        }
1450        kg->done = 0;
1451
1452        /* Poll for completion of the thread periodically. I would have preferred
1453           to just wait on a pipe but this way it's portable to Windows. *sigh*
1454        */
1455        ev = b_timeout_add(1000, &keygen_finish_handler, kg);
1456        if(!ev) {
1457                irc_usermsg(irc, "otr keygen failed: couldn't register timeout");
1458                g_free(kg->keyfile);
1459                g_mutex_free(kg->mutex);
1460                g_free(kg);
1461                return;
1462        }
1463
1464        /* tell the user what's happening, go comatose, and start the keygen */
1465        irc_usermsg(irc, "going comatose for otr key generation, this will take a moment");
1466        irc_usermsg(irc, "all accounts logging out, user commands disabled");
1467        cmd_account(irc, account_off);
1468        irc_usermsg(irc, "generating new otr privkey for %s/%s...",
1469                handle, protocol);
1470       
1471        thr = g_thread_create(&otr_keygen_thread_func, kg, FALSE, &err);
1472        if(!thr) {
1473                irc_usermsg(irc, "otr keygen failed: %s", err->message);
1474                g_free(kg->keyfile);
1475                g_mutex_free(kg->mutex);
1476                g_free(kg);
1477                b_event_remove(ev);
1478        }
1479}
1480
1481gpointer otr_keygen_thread_func(gpointer data)
1482{
1483        struct kgdata *kg = (struct kgdata *)data;
1484       
1485        /* lock OTR subsystem and do the work */
1486        g_static_rec_mutex_lock(&kg->irc->otr_mutex);
1487        kg->result = otrl_privkey_generate(kg->irc->otr_us, kg->keyfile, kg->handle,
1488                kg->protocol);
1489        g_static_rec_mutex_unlock(&kg->irc->otr_mutex);
1490        /* OTR enabled again */
1491       
1492        /* notify mainloop */
1493        g_mutex_lock(kg->mutex);
1494        kg->done = 1;
1495        g_mutex_unlock(kg->mutex);
1496       
1497        return NULL;
1498}
1499
1500gboolean keygen_finish_handler(gpointer data, gint fd, b_input_condition cond)
1501{
1502        struct kgdata *kg = (struct kgdata *)data;
1503        int done;
1504       
1505        g_mutex_lock(kg->mutex);
1506        done = kg->done;
1507        g_mutex_unlock(kg->mutex);
1508        if(kg->done) {
1509                if(kg->result) {
1510                        irc_usermsg(kg->irc, "otr keygen: %s", strerror(kg->result));
1511                } else {
1512                        irc_usermsg(kg->irc, "otr keygen for %s/%s complete", kg->handle, kg->protocol);
1513                }
1514                g_free(kg->keyfile);
1515                g_mutex_free(kg->mutex);
1516                g_free(kg);
1517                return FALSE; /* unregister timeout */
1518        }
1519
1520        return TRUE;  /* still working, continue checking */
1521}
1522
1523void yes_keygen(gpointer w, void *data)
1524{
1525        account_t *acc = (account_t *)data;
1526       
1527        otr_keygen(acc->irc, acc->user, acc->prpl->name);
1528}
1529
1530
1531#else /* WITH_OTR undefined */
1532
1533void cmd_otr(irc_t *irc, char **args)
1534{
1535        irc_usermsg(irc, "otr: n/a, compiled without OTR support");
1536}
1537
1538#endif
Note: See TracBrowser for help on using the repository browser.