source: otr.c @ f5ac0fb

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