source: otr.c @ e9a3e8c

Last change on this file since e9a3e8c was d28fe1c4, checked in by jgeboski <jgeboski@…>, at 2016-05-26T02:48:08Z

Implemented plugin information 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.

Furthermore, it is often times impossible to determine the version of
a plugin, without relying on the package manager of the system. This is
quite a problem when users are reporting bugs for external plugins, and
they have no idea what version of the plugin they are running. This is
also an opportunity to provide additional metadata for each plugin that
can then be displayed to the user.

Solving these issues is done by adding a new required function to each
plugin. The init_plugin_info() function must now be implemented along
with the init_plugin() function. This function then returns a static
structure, which retains all of the metadata for the plugin. Then this
is used by bitlbee to check the ABI version and provide information to
the user.

The introduction of the new function is required as bitlbee needs to
obtain the ABI version before calling init_plugin().

The boiler-plate implementation of init_plugin_info():

#ifdef BITLBEE_ABI_VERSION_CODE
struct plugin_info *init_plugin_info(void)
{

static struct plugin_info info = {

BITLBEE_ABI_VERSION_CODE, /* Required */
"plugin-name", /* Required */
"1.3.3.7", /* Required */
"A short description of the plugin", /* Optional */
"First Last <alias@…>", /* Optional */
"http://www.domain.tld" /* Optional */

};

return &info;

}
#endif

The example wraps the function declaration in an if block for backwards
compatibility with older bitlbee versions.

Displaying the plugin metadata is done via the newly added "plugins"
command, which simply dumps formatted data to the root channel.

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