source: otr.c @ e65039a

Last change on this file since e65039a was e65039a, checked in by unknown <pesco@…>, at 2013-08-03T12:49:03Z

persist instags (also works around "malformed message" bug)

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