source: otr.c @ 8bd697c

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