source: otr.c @ 9d6c0f2

Last change on this file since 9d6c0f2 was 9d6c0f2, checked in by jgeboski <jgeboski@…>, at 2016-05-15T16:16:37Z

Implemented ABI checking for external plugins

As of now, bitlbee will load any plugin regardless of the ABI it was
built against. This is really problematic when structures or symbols
are changed within bitlbee. This often leads to the plugin not loading
or the plugin acting in an undefined way. Typically a simple rebuild of
the plugin will resolve such issues, but many users have no idea that
this is required after they have updated bitlbee.

Each plugin must now have an init_plugin_abi() function along with its
init_plugin() function. This new function is called very early in the
plugin loading process to ensure the plugin actually matches the ABI of
bitlbee. The constant value of BITLBEE_ABI_VERSION_CODE is returned by
the function, which embeds the constant value into the plugin. This
value is then read by bitlbee upon every plugin load to ensure that the
proper ABI is use. If not, bitlbee will refuse to load the plugin.

The boiler-plate implementation of init_plugin_abi():

guint init_plugin_abi(void)
{

return BITLBEE_ABI_VERSION_CODE;

}

Third party plugins may also want to provide backwards compatibility
with older bitlbee versions by defining the missing version macro:

#ifndef BITLBEE_ABI_VERSION_CODE
#define BITLBEE_ABI_VERSION_CODE 0
#endif

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