From 764c7d1009feda7db971ea7ac6bcb2a4acef0efc Mon Sep 17 00:00:00 2001 From: Sven Moritz Hallberg Date: Sun, 3 Feb 2008 22:30:03 +0100 Subject: OTR support, first checkin --- Makefile | 4 +- bitlbee.c | 3 + bitlbee.h | 2 + configure | 27 +- irc.c | 7 + irc.h | 5 + log.c | 20 +- log.h | 12 +- otr.c | 953 +++++++++++++++++++++++++++++++++++++++++++++++++++++ otr.h | 61 ++++ protocols/nogaim.c | 19 +- root_commands.c | 12 + storage.c | 21 +- unix.c | 2 + user.c | 2 +- user.h | 2 +- 16 files changed, 1121 insertions(+), 31 deletions(-) create mode 100644 otr.c create mode 100644 otr.h diff --git a/Makefile b/Makefile index 0a988feb..c0d7aad9 100644 --- a/Makefile +++ b/Makefile @@ -9,8 +9,8 @@ -include Makefile.settings # Program variables -objects = account.o bitlbee.o conf.o crypting.o help.o ipc.o irc.o irc_commands.o log.o nick.o query.o root_commands.o set.o storage.o $(STORAGE_OBJS) unix.o user.o -headers = account.h bitlbee.h commands.h conf.h config.h crypting.h help.h ipc.h irc.h log.h nick.h query.h set.h sock.h storage.h user.h lib/events.h lib/http_client.h lib/ini.h lib/md5.h lib/misc.h lib/proxy.h lib/sha1.h lib/ssl_client.h lib/url.h protocols/nogaim.h +objects = account.o bitlbee.o conf.o crypting.o help.o ipc.o irc.o irc_commands.o log.o nick.o otr.o query.o root_commands.o set.o storage.o $(STORAGE_OBJS) unix.o user.o +headers = account.h bitlbee.h commands.h conf.h config.h crypting.h help.h ipc.h irc.h log.h nick.h otr.h query.h set.h sock.h storage.h user.h lib/events.h lib/http_client.h lib/ini.h lib/md5.h lib/misc.h lib/proxy.h lib/sha1.h lib/ssl_client.h lib/url.h protocols/nogaim.h subdirs = lib protocols # Expansion of variables diff --git a/bitlbee.c b/bitlbee.c index c4b5abb3..8785ecf0 100644 --- a/bitlbee.c +++ b/bitlbee.c @@ -138,6 +138,9 @@ int bitlbee_inetd_init() log_link( LOGLVL_ERROR, LOGOUTPUT_IRC ); log_link( LOGLVL_WARNING, LOGOUTPUT_IRC ); + /* TODO: Remove debugging log_link's */ + log_link( LOGLVL_INFO, LOGOUTPUT_IRC ); + log_link( LOGLVL_DEBUG, LOGOUTPUT_IRC ); return( 0 ); } diff --git a/bitlbee.h b/bitlbee.h index 23ec64b1..b60d4b90 100644 --- a/bitlbee.h +++ b/bitlbee.h @@ -132,6 +132,7 @@ extern char *CONF_FILE; #include "sock.h" #include "misc.h" #include "proxy.h" +#include "otr.h" typedef struct global { /* In forked mode, child processes store the fd of the IPC socket here. */ @@ -142,6 +143,7 @@ typedef struct global { GList *storage; /* The first backend in the list will be used for saving */ char *helpfile; int restart; + OtrlMessageAppOps otr_ops; /* collects interface functions required by OTR */ } global_t; int bitlbee_daemon_init( void ); diff --git a/configure b/configure index 22989f60..c0a9bdb4 100755 --- a/configure +++ b/configure @@ -29,6 +29,7 @@ debug=0 strip=1 gcov=0 plugins=1 +otr=auto events=glib ldap=0 @@ -69,6 +70,7 @@ Option Description Default --strip=0/1 Disable/enable binary stripping $strip --gcov=0/1 Disable/enable test coverage reporting $gcov --plugins=0/1 Disable/enable plugins support $plugins +--otr=0/1/auto Disable/enable OTR encryption support $otr --events=... Event handler (glib, libevent) $events --ssl=... SSL library to use (gnutls, nss, openssl, bogus, auto) @@ -173,8 +175,8 @@ fi if $PKG_CONFIG --version > /dev/null 2>/dev/null && $PKG_CONFIG glib-2.0; then if $PKG_CONFIG glib-2.0 --atleast-version=$GLIB_MIN_VERSION; then cat<>Makefile.settings -EFLAGS+=`$PKG_CONFIG --libs glib-2.0 gmodule-2.0` -CFLAGS+=`$PKG_CONFIG --cflags glib-2.0 gmodule-2.0` +EFLAGS+=`$PKG_CONFIG --libs glib-2.0 gmodule-2.0 gthread-2.0` +CFLAGS+=`$PKG_CONFIG --cflags glib-2.0 gmodule-2.0 gthread-2.0` EOF else echo @@ -384,6 +386,21 @@ else echo '#define WITH_PLUGINS' >> config.h fi +if [ "$otr" = "auto" ]; then + for i in /lib /usr/lib /usr/local/lib; do + if [ -f $i/libotr.a ]; then + otr=1 + break + fi + done +fi +if [ "$otr" = 0 ]; then + echo '#undef WITH_OTR' >> config.h +else + echo '#define WITH_OTR' >> config.h + echo "EFLAGS+=-lotr" >> Makefile.settings +fi + echo if [ -z "$BITLBEE_VERSION" -a -d .bzr ] && type bzr > /dev/null 2> /dev/null; then nick=`bzr nick` @@ -503,6 +520,12 @@ else echo ' Binary stripping disabled.' fi +if [ "$otr" = "1" ]; then + echo ' Off-the-Record (OTR) Messaging enabled.' +else + echo ' Off-the-Record (OTR) Messaging disabled.' +fi + echo ' Using event handler: '$events echo ' Using SSL library: '$ssl echo ' Building with these storage backends: '$STORAGES diff --git a/irc.c b/irc.c index 972e030e..db681175 100644 --- a/irc.c +++ b/irc.c @@ -125,6 +125,9 @@ irc_t *irc_new( int fd ) set_add( &irc->set, "typing_notice", "false", set_eval_bool, irc ); conf_loaddefaults( irc ); + + irc->otr_us = otrl_userstate_create(); + irc->otr_mutex = g_mutex_new(); return( irc ); } @@ -275,6 +278,10 @@ void irc_free(irc_t * irc) g_free(helpnodetmp); } } + + otrl_userstate_free(irc->otr_us); + g_mutex_free(irc->otr_mutex); + g_free(irc); if( global.conf->runmode == RUNMODE_INETD || global.conf->runmode == RUNMODE_FORKDAEMON ) diff --git a/irc.h b/irc.h index 8be3579e..318e263e 100644 --- a/irc.h +++ b/irc.h @@ -26,6 +26,8 @@ #ifndef _IRC_H #define _IRC_H +#include "otr.h" + #define IRC_MAX_LINE 512 #define IRC_MAX_ARGS 8 @@ -94,6 +96,9 @@ typedef struct irc gint r_watch_source_id; gint w_watch_source_id; gint ping_source_id; + + OtrlUserState otr_us; + GMutex *otr_mutex; /* for locking otr during keygen */ } irc_t; #include "user.h" diff --git a/log.c b/log.c index 4606fb88..bea046d3 100644 --- a/log.c +++ b/log.c @@ -29,10 +29,10 @@ static log_t logoutput; -static void log_null(int level, char *logmessage); -static void log_irc(int level, char *logmessage); -static void log_syslog(int level, char *logmessage); -static void log_console(int level, char *logmessage); +static void log_null(int level, const char *logmessage); +static void log_irc(int level, const char *logmessage); +static void log_syslog(int level, const char *logmessage); +static void log_console(int level, const char *logmessage); void log_init(void) { openlog("bitlbee", LOG_PID, LOG_DAEMON); @@ -96,7 +96,7 @@ void log_link(int level, int output) { } -void log_message(int level, char *message, ... ) { +void log_message(int level, const char *message, ... ) { va_list ap; char *msgstring; @@ -121,17 +121,17 @@ void log_message(int level, char *message, ... ) { return; } -void log_error(char *functionname) { +void log_error(const char *functionname) { log_message(LOGLVL_ERROR, "%s: %s", functionname, strerror(errno)); return; } -static void log_null(int level, char *message) { +static void log_null(int level, const char *message) { return; } -static void log_irc(int level, char *message) { +static void log_irc(int level, const char *message) { if(level == LOGLVL_ERROR) irc_write_all(1, "ERROR :Error: %s", message); if(level == LOGLVL_WARNING) @@ -146,7 +146,7 @@ static void log_irc(int level, char *message) { return; } -static void log_syslog(int level, char *message) { +static void log_syslog(int level, const char *message) { if(level == LOGLVL_ERROR) syslog(LOG_ERR, "%s", message); if(level == LOGLVL_WARNING) @@ -160,7 +160,7 @@ static void log_syslog(int level, char *message) { return; } -static void log_console(int level, char *message) { +static void log_console(int level, const char *message) { if(level == LOGLVL_ERROR) fprintf(stderr, "Error: %s\n", message); if(level == LOGLVL_WARNING) diff --git a/log.h b/log.h index f574ae07..b651416b 100644 --- a/log.h +++ b/log.h @@ -43,17 +43,17 @@ typedef enum { } logoutput_t; typedef struct log_t { - void (*error)(int level, char *logmessage); - void (*warning)(int level, char *logmessage); - void (*informational)(int level, char *logmessage); + void (*error)(int level, const char *logmessage); + void (*warning)(int level, const char *logmessage); + void (*informational)(int level, const char *logmessage); #ifdef DEBUG - void (*debug)(int level, char *logmessage); + void (*debug)(int level, const char *logmessage); #endif } log_t; void log_init(void); void log_link(int level, int output); -void log_message(int level, char *message, ...) G_GNUC_PRINTF( 2, 3 ); -void log_error(char *functionname); +void log_message(int level, const char *message, ...) G_GNUC_PRINTF( 2, 3 ); +void log_error(const char *functionname); #endif diff --git a/otr.c b/otr.c new file mode 100644 index 00000000..2b920f4b --- /dev/null +++ b/otr.c @@ -0,0 +1,953 @@ +#include "bitlbee.h" +#ifdef WITH_OTR +#include "irc.h" +#include "otr.h" +#include +#include + +/** +files used to store OTR data: + $configdir/$nick.otr_keys + $configdir/$nick.otr_fprints + **/ + + +/** OTR interface routines for the OtrlMessageAppOps struct: **/ + +OtrlPolicy op_policy(void *opdata, ConnContext *context); + +void op_create_privkey(void *opdata, const char *accountname, const char *protocol); + +int op_is_logged_in(void *opdata, const char *accountname, const char *protocol, + const char *recipient); + +void op_inject_message(void *opdata, const char *accountname, const char *protocol, + const char *recipient, const char *message); + +int op_display_otr_message(void *opdata, const char *accountname, const char *protocol, + const char *username, const char *msg); + +void op_new_fingerprint(void *opdata, OtrlUserState us, const char *accountname, + const char *protocol, const char *username, unsigned char fingerprint[20]); + +void op_write_fingerprints(void *opdata); + +void op_gone_secure(void *opdata, ConnContext *context); + +void op_gone_secure(void *opdata, ConnContext *context); + +void op_gone_insecure(void *opdata, ConnContext *context); + +void op_still_secure(void *opdata, ConnContext *context, int is_reply); + +void op_log_message(void *opdata, const char *message); + +/* TODO: int op_max_message_size(void *opdata, ConnContext *context); */ + +/* TODO: const char *op_account_name(void *opdata, const char *account, + const char *protocol); */ + + +/** otr sub-command handlers: **/ + +/* TODO: void cmd_otr_keygen(irc_t *irc, char **args); */ +void cmd_otr_abort(irc_t *irc, char **args); /* TODO: does this cmd even make sense? */ +void cmd_otr_request(irc_t *irc, char **args); /* TODO: do we even need this? */ +void cmd_otr_auth(irc_t *irc, char **args); +/* TODO: void cmd_otr_affirm(irc_t *irc, char **args); */ +void cmd_otr_fprints(irc_t *irc, char **args); +void cmd_otr_info(irc_t *irc, char **args); +void cmd_otr_policy(irc_t *irc, char **args); + +const command_t otr_commands[] = { + { "abort", 1, &cmd_otr_abort, 0 }, + { "request", 1, &cmd_otr_request, 0 }, + { "auth", 2, &cmd_otr_auth, 0 }, + { "fprints", 0, &cmd_otr_fprints, 0 }, + { "info", 1, &cmd_otr_info, 0 }, + { "policy", 0, &cmd_otr_policy, 0 }, + { NULL } +}; + + +/** misc. helpers/subroutines: **/ + +/* start background thread to generate a (new) key for a given account */ +void otr_keygen(irc_t *irc, const char *handle, const char *protocol); +/* keygen thread main func */ +gpointer otr_keygen_thread_func(gpointer data); +/* mainloop handler for when keygen thread finishes */ +gboolean keygen_finish_handler(gpointer data, gint fd, b_input_condition cond); +/* data to be passed to otr_keygen_thread_func */ +struct kgdata { + irc_t *irc; /* access to OTR userstate */ + char *keyfile; /* free me! */ + const char *handle; /* don't free! */ + const char *protocol; /* don't free! */ + GMutex *mutex; /* lock for the 'done' flag, free me! */ + int done; /* is the thread done? */ + gcry_error_t result; /* return value of otrl_privkey_generate */ +}; + +/* yes/no handlers for "generate key now?" */ +void yes_keygen(gpointer w, void *data); +void no_keygen(gpointer w, void *data); + +/* helper to make sure accountname and protocol match the incoming "opdata" */ +struct im_connection *check_imc(void *opdata, const char *accountname, + const char *protocol); + +/* determine the nick for a given handle/protocol pair */ +const char *peernick(irc_t *irc, const char *handle, const char *protocol); + +/* handle SMP TLVs from a received message */ +void otr_handle_smp(struct im_connection *ic, const char *handle, OtrlTLV *tlvs); + +/* show the list of fingerprints associated with a given context */ +void show_fingerprints(irc_t *irc, ConnContext *ctx); + + + +/*** routines declared in otr.h: ***/ + +void otr_init(void) +{ + if(!g_thread_supported()) g_thread_init(NULL); + OTRL_INIT; + + /* fill global OtrlMessageAppOps */ + global.otr_ops.policy = &op_policy; + global.otr_ops.create_privkey = &op_create_privkey; + global.otr_ops.is_logged_in = &op_is_logged_in; + global.otr_ops.inject_message = &op_inject_message; + global.otr_ops.notify = NULL; + global.otr_ops.display_otr_message = &op_display_otr_message; + global.otr_ops.update_context_list = NULL; + global.otr_ops.protocol_name = NULL; + global.otr_ops.protocol_name_free = NULL; + global.otr_ops.new_fingerprint = &op_new_fingerprint; + global.otr_ops.write_fingerprints = &op_write_fingerprints; + global.otr_ops.gone_secure = &op_gone_secure; + global.otr_ops.gone_insecure = &op_gone_insecure; + global.otr_ops.still_secure = &op_still_secure; + global.otr_ops.log_message = &op_log_message; + global.otr_ops.max_message_size = NULL; + global.otr_ops.account_name = NULL; + global.otr_ops.account_name_free = NULL; +} + +/* Notice on the otr_mutex: + + The incoming/outgoing message handlers try to lock the otr_mutex. If they succeed, + this will prevent a concurrent keygen (possibly spawned by that very command) + from messing up the userstate. If the lock fails, that means there already is + a keygen in progress. Instead of blocking for an unknown time, they + will bail out gracefully, informing the user of this temporary "coma". + TODO: Hold back incoming/outgoing messages and process them when keygen completes? + + The other routines do not lock the otr_mutex themselves, it is done as a + catch-all in the root command handler. Rationale: + a) it's easy to code + b) it makes it obvious that no command can get its userstate corrupted + c) the "irc" struct is readily available there for feedback to the user + */ + +void otr_load(irc_t *irc) +{ + char s[512]; + account_t *a; + gcry_error_t e; + + log_message(LOGLVL_DEBUG, "otr_load '%s'", irc->nick); + + g_snprintf(s, 511, "%s%s.otr_keys", global.conf->configdir, irc->nick); + e = otrl_privkey_read(irc->otr_us, s); + if(e && e!=ENOENT) { + log_message(LOGLVL_ERROR, "%s: %s", s, strerror(e)); + } + g_snprintf(s, 511, "%s%s.otr_fprints", global.conf->configdir, irc->nick); + e = otrl_privkey_read_fingerprints(irc->otr_us, s, NULL, NULL); + if(e && e!=ENOENT) { + log_message(LOGLVL_ERROR, "%s: %s", s, strerror(e)); + } + + /* check for otr keys on all accounts */ + for(a=irc->accounts; a; a=a->next) { + otr_check_for_key(a); + } +} + +void otr_save(irc_t *irc) +{ + char s[512]; + + log_message(LOGLVL_DEBUG, "otr_save '%s'", irc->nick); + + g_snprintf(s, 511, "%s%s.otr_fprints", global.conf->configdir, irc->nick); + otrl_privkey_write_fingerprints(irc->otr_us, s); +} + +void otr_remove(const char *nick) +{ + char s[512]; + + log_message(LOGLVL_DEBUG, "otr_remove '%s'", nick); + + g_snprintf(s, 511, "%s%s.otr_keys", global.conf->configdir, nick); + unlink(s); + g_snprintf(s, 511, "%s%s.otr_fprints", global.conf->configdir, nick); + unlink(s); +} + +void otr_rename(const char *onick, const char *nnick) +{ + char s[512], t[512]; + + log_message(LOGLVL_DEBUG, "otr_rename '%s' -> '%s'", onick, nnick); + + g_snprintf(s, 511, "%s%s.otr_keys", global.conf->configdir, onick); + g_snprintf(t, 511, "%s%s.otr_keys", global.conf->configdir, nnick); + rename(s,t); + g_snprintf(s, 511, "%s%s.otr_fprints", global.conf->configdir, onick); + g_snprintf(t, 511, "%s%s.otr_fprints", global.conf->configdir, nnick); + rename(s,t); +} + +void otr_check_for_key(account_t *a) +{ + irc_t *irc = a->irc; + char buf[45]; + char *fp; + + fp = otrl_privkey_fingerprint(irc->otr_us, buf, a->user, a->prpl->name); + if(fp) { + irc_usermsg(irc, "otr: %s/%s ready with f'print %s", + a->user, a->prpl->name, fp); + } else { + otr_keygen(irc, a->user, a->prpl->name); + } +} + +char *otr_handle_message(struct im_connection *ic, const char *handle, const char *msg) +{ + int ignore_msg; + char *newmsg = NULL; + OtrlTLV *tlvs = NULL; + char *colormsg; + + if(!g_mutex_trylock(ic->irc->otr_mutex)) { + /* TODO: queue msgs received during keygen for later */ + irc_usermsg(ic->irc, "msg from %s/%s during keygen - dropped", + handle, ic->acc->prpl->name); + return NULL; + } + + ignore_msg = otrl_message_receiving(ic->irc->otr_us, &global.otr_ops, ic, + ic->acc->user, ic->acc->prpl->name, handle, msg, &newmsg, + &tlvs, NULL, NULL); + + otr_handle_smp(ic, handle, tlvs); + + if(ignore_msg) { + /* this was an internal OTR protocol message */ + g_mutex_unlock(ic->irc->otr_mutex); + return NULL; + } else if(!newmsg) { + /* this was a non-OTR message */ + g_mutex_unlock(ic->irc->otr_mutex); + return g_strdup(msg); + } else { + /* OTR has processed this message */ + ConnContext *context = otrl_context_find(ic->irc->otr_us, handle, + ic->acc->user, ic->acc->prpl->name, 0, NULL, NULL, NULL); + if(context && context->msgstate == OTRL_MSGSTATE_ENCRYPTED) { + /* color according to f'print trust */ + char color; + const char *trust = context->active_fingerprint->trust; + if(trust && trust[0] != '\0') + color='3'; /* green */ + else + color='5'; /* red */ + colormsg = g_strdup_printf("\x03%c%s\x0F", color, newmsg); + } else { + colormsg = g_strdup(newmsg); + } + otrl_message_free(newmsg); + g_mutex_unlock(ic->irc->otr_mutex); + return colormsg; + } +} + +int otr_send_message(struct im_connection *ic, const char *handle, const char *msg, int flags) +{ + int st; + char *otrmsg = NULL; + ConnContext *ctx = NULL; + + if(!g_mutex_trylock(ic->irc->otr_mutex)) { + irc_usermsg(ic->irc, "msg to %s/%s during keygen - not sent", + handle, ic->acc->prpl->name); + return 1; + } + + st = otrl_message_sending(ic->irc->otr_us, &global.otr_ops, ic, + ic->acc->user, ic->acc->prpl->name, handle, + msg, NULL, &otrmsg, NULL, NULL); + if(st) { + g_mutex_unlock(ic->irc->otr_mutex); + return st; + } + + ctx = otrl_context_find(ic->irc->otr_us, + handle, ic->acc->user, ic->acc->prpl->name, + 1, NULL, NULL, NULL); + + if(otrmsg) { + if(!ctx) { + otrl_message_free(otrmsg); + g_mutex_unlock(ic->irc->otr_mutex); + return 1; + } + st = otrl_message_fragment_and_send(&global.otr_ops, ic, ctx, + otrmsg, OTRL_FRAGMENT_SEND_ALL, NULL); + otrl_message_free(otrmsg); + } else { + /* yeah, well, some const casts as usual... ;-) */ + st = ic->acc->prpl->buddy_msg( ic, (char *)handle, (char *)msg, flags ); + } + + g_mutex_unlock(ic->irc->otr_mutex); + return st; +} + +void cmd_otr(irc_t *irc, char **args) +{ + const command_t *cmd; + + if(!args[0]) + return; + + if(!args[1]) + return; + + for(cmd=otr_commands; cmd->command; cmd++) { + if(strcmp(cmd->command, args[1]) == 0) + break; + } + + if(!cmd->command) { + irc_usermsg(irc, "%s %s: unknown subcommand, see \x02help otr\x02", + args[0], args[1]); + return; + } + + if(!args[cmd->required_parameters+1]) { + irc_usermsg(irc, "%s %s: not enough arguments (%d req.)", + args[0], args[1], cmd->required_parameters); + return; + } + + cmd->execute(irc, args+1); +} + + +/*** OTR "MessageAppOps" callbacks for global.otr_ui: ***/ + +OtrlPolicy op_policy(void *opdata, ConnContext *context) +{ + /* TODO: OTR policy configurable */ + return OTRL_POLICY_OPPORTUNISTIC; +} + +void op_create_privkey(void *opdata, const char *accountname, + const char *protocol) +{ + struct im_connection *ic = check_imc(opdata, accountname, protocol); + char *s; + + log_message(LOGLVL_DEBUG, "op_create_privkey '%s' '%s'", accountname, protocol); + + s = g_strdup_printf("oops, no otr privkey for %s/%s - generate one now?", + accountname, protocol); + query_add(ic->irc, ic, s, yes_keygen, no_keygen, ic->acc); +} + +int op_is_logged_in(void *opdata, const char *accountname, + const char *protocol, const char *recipient) +{ + struct im_connection *ic = check_imc(opdata, accountname, protocol); + user_t *u; + + log_message(LOGLVL_DEBUG, "op_is_logged_in '%s' '%s' '%s'", accountname, protocol, recipient); + + /* lookup the user_t for the given recipient */ + u = user_findhandle(ic, recipient); + if(u) { + if(u->online) + return 1; + else + return 0; + } else { + return -1; + } +} + +void op_inject_message(void *opdata, const char *accountname, + const char *protocol, const char *recipient, const char *message) +{ + struct im_connection *ic = check_imc(opdata, accountname, protocol); + + log_message(LOGLVL_DEBUG, "op_inject_message '%s' '%s' '%s' '%s'", accountname, protocol, recipient, message); + + if (strcmp(accountname, recipient) == 0) { + /* huh? injecting messages to myself? */ + irc_usermsg(ic->irc, "note to self: %s", message); + } else { + /* need to drop some consts here :-( */ + /* TODO: get flags into op_inject_message?! */ + ic->acc->prpl->buddy_msg(ic, (char *)recipient, (char *)message, 0); + /* ignoring return value :-/ */ + } +} + +int op_display_otr_message(void *opdata, const char *accountname, + const char *protocol, const char *username, const char *msg) +{ + struct im_connection *ic = check_imc(opdata, accountname, protocol); + + log_message(LOGLVL_DEBUG, "op_display_otr_message '%s' '%s' '%s' '%s'", accountname, protocol, username, msg); + + irc_usermsg(ic->irc, "%s", msg); + + return 0; +} + +void op_new_fingerprint(void *opdata, OtrlUserState us, + const char *accountname, const char *protocol, + const char *username, unsigned char fingerprint[20]) +{ + struct im_connection *ic = check_imc(opdata, accountname, protocol); + char hunam[45]; /* anybody looking? ;-) */ + + otrl_privkey_hash_to_human(hunam, fingerprint); + log_message(LOGLVL_DEBUG, "op_new_fingerprint '%s' '%s' '%s' '%s'", accountname, protocol, username, hunam); + + irc_usermsg(ic->irc, "new fingerprint for %s: %s", + peernick(ic->irc, username, protocol), hunam); +} + +void op_write_fingerprints(void *opdata) +{ + struct im_connection *ic = (struct im_connection *)opdata; + + log_message(LOGLVL_DEBUG, "op_write_fingerprints"); + + otr_save(ic->irc); +} + +void op_gone_secure(void *opdata, ConnContext *context) +{ + struct im_connection *ic = + check_imc(opdata, context->accountname, context->protocol); + + log_message(LOGLVL_DEBUG, "op_gone_secure '%s' '%s' '%s'", context->accountname, context->protocol, context->username); + + irc_usermsg(ic->irc, "conversation with %s is now off the record", + peernick(ic->irc, context->username, context->protocol)); +} + +void op_gone_insecure(void *opdata, ConnContext *context) +{ + struct im_connection *ic = + check_imc(opdata, context->accountname, context->protocol); + + log_message(LOGLVL_DEBUG, "op_gone_insecure '%s' '%s' '%s'", context->accountname, context->protocol, context->username); + + irc_usermsg(ic->irc, "conversation with %s is now in the clear", + peernick(ic->irc, context->username, context->protocol)); +} + +void op_still_secure(void *opdata, ConnContext *context, int is_reply) +{ + struct im_connection *ic = + check_imc(opdata, context->accountname, context->protocol); + + log_message(LOGLVL_DEBUG, "op_still_secure '%s' '%s' '%s' is_reply=%d", + context->accountname, context->protocol, context->username, is_reply); + + irc_usermsg(ic->irc, "otr connection with %s has been refreshed", + peernick(ic->irc, context->username, context->protocol)); +} + +void op_log_message(void *opdata, const char *message) +{ + log_message(LOGLVL_INFO, "%s", message); +} + + +/*** OTR sub-command handlers ***/ + +void cmd_otr_abort(irc_t *irc, char **args) +{ + user_t *u; + + u = user_find(irc, args[1]); + if(!u || !u->ic) { + irc_usermsg(irc, "%s: unknown user", args[1]); + return; + } + + otrl_message_disconnect(irc->otr_us, &global.otr_ops, + u->ic, u->ic->acc->user, u->ic->acc->prpl->name, u->handle); +} + +void cmd_otr_request(irc_t *irc, char **args) +{ + user_t *u; + + u = user_find(irc, args[1]); + if(!u || !u->ic) { + irc_usermsg(irc, "%s: unknown user", args[1]); + return; + } + if(!u->online) { + irc_usermsg(irc, "%s is offline", args[1]); + return; + } + + imc_buddy_msg(u->ic, u->handle, "?OTR?", 0); +} + +void cmd_otr_auth(irc_t *irc, char **args) +{ + user_t *u; + ConnContext *ctx; + + u = user_find(irc, args[1]); + if(!u || !u->ic) { + irc_usermsg(irc, "%s: unknown user", args[1]); + return; + } + if(!u->online) { + irc_usermsg(irc, "%s is offline", args[1]); + return; + } + + ctx = otrl_context_find(irc->otr_us, u->handle, + u->ic->acc->user, u->ic->acc->prpl->name, 1, NULL, NULL, NULL); + if(!ctx) { + return; + } + + if(ctx->smstate->nextExpected != OTRL_SMP_EXPECT1) { + log_message(LOGLVL_INFO, + "SMP already in phase %d, sending abort before reinitiating", + ctx->smstate->nextExpected+1); + otrl_message_abort_smp(irc->otr_us, &global.otr_ops, u->ic, ctx); + otrl_sm_state_free(ctx->smstate); + } + + /* warning: the following assumes that smstates are cleared whenever an SMP + is completed or aborted! */ + if(ctx->smstate->secret == NULL) { + irc_usermsg(irc, "smp: initiating with %s...", u->nick); + otrl_message_initiate_smp(irc->otr_us, &global.otr_ops, + u->ic, ctx, (unsigned char *)args[2], strlen(args[2])); + /* smp is now in EXPECT2 */ + } else { + /* if we're still in EXPECT1 but smstate is initialized, we must have + received the SMP1, so let's issue a response */ + irc_usermsg(irc, "smp: responding to %s...", u->nick); + otrl_message_respond_smp(irc->otr_us, &global.otr_ops, + u->ic, ctx, (unsigned char *)args[2], strlen(args[2])); + /* smp is now in EXPECT3 */ + } +} + +void cmd_otr_fprints(irc_t *irc, char **args) +{ + if(args[1]) { + /* list given buddy's fingerprints */ + user_t *u; + ConnContext *ctx; + + u = user_find(irc, args[1]); + if(!u || !u->ic) { + irc_usermsg(irc, "%s: unknown user", args[1]); + return; + } + + ctx = otrl_context_find(irc->otr_us, u->handle, + u->ic->acc->user, u->ic->acc->prpl->name, 0, NULL, NULL, NULL); + if(!ctx) { + irc_usermsg(irc, "no fingerprints"); + } else { + show_fingerprints(irc, ctx); + } + } else { + /* list all known fingerprints */ + ConnContext *ctx; + for(ctx=irc->otr_us->context_root; ctx; ctx=ctx->next) { + irc_usermsg(irc, "[%s]", peernick(irc, ctx->username, ctx->protocol)); + show_fingerprints(irc, ctx); + } + if(!irc->otr_us->context_root) { + irc_usermsg(irc, "no fingerprints"); + } + } +} + +void cmd_otr_info(irc_t *irc, char **args) +{ + user_t *u; + ConnContext *ctx; + Fingerprint *fp; + char human[45]; + const char *offer_status; + const char *message_state; + const char *trust; + + if(!args) { + irc_usermsg(irc, "no args?!"); + return; + } + if(!args[1]) { + irc_usermsg(irc, "no args[1]?!"); + return; + } + u = user_find(irc, args[1]); + if(!u || !u->ic) { + irc_usermsg(irc, "%s: unknown user", args[1]); + return; + } + + ctx = otrl_context_find(irc->otr_us, u->handle, + u->ic->acc->user, u->ic->acc->prpl->name, 0, NULL, NULL, NULL); + if(!ctx) { + irc_usermsg(irc, "no otr info on %s", args[1]); + return; + } + + switch(ctx->otr_offer) { + case OFFER_NOT: offer_status="none sent"; break; + case OFFER_SENT: offer_status="awaiting reply"; break; + case OFFER_ACCEPTED: offer_status="accepted our offer"; break; + case OFFER_REJECTED: offer_status="ignored our offer"; break; + default: offer_status="?"; + } + + switch(ctx->msgstate) { + case OTRL_MSGSTATE_PLAINTEXT: message_state="cleartext"; break; + case OTRL_MSGSTATE_ENCRYPTED: message_state="encrypted"; break; + case OTRL_MSGSTATE_FINISHED: message_state="shut down"; break; + default: message_state="?"; + } + + irc_usermsg(irc, "%s is %s/%s; we are %s/%s to them", args[1], + ctx->username, ctx->protocol, ctx->accountname, ctx->protocol); + irc_usermsg(irc, " otr offer status: %s", offer_status); + irc_usermsg(irc, " connection state: %s", message_state); + irc_usermsg(irc, " protocol version: %d", ctx->protocol_version); + fp = ctx->active_fingerprint; + if(!fp) { + irc_usermsg(irc, " active f'print: none"); + } else { + otrl_privkey_hash_to_human(human, fp->fingerprint); + if(!fp->trust || fp->trust[0] == '\0') { + trust="untrusted"; + } else { + trust=fp->trust; + } + irc_usermsg(irc, " active f'print: %s (%s)", human, trust); + } +} + +void cmd_otr_policy(irc_t *irc, char **args) +{ + irc_usermsg(irc, "n/a: not implemented"); +} + + +/*** local helpers / subroutines: ***/ + +/* Socialist Millionaires' Protocol */ +void otr_handle_smp(struct im_connection *ic, const char *handle, OtrlTLV *tlvs) +{ + irc_t *irc = ic->irc; + OtrlUserState us = irc->otr_us; + OtrlMessageAppOps *ops = &global.otr_ops; + OtrlTLV *tlv = NULL; + ConnContext *context; + NextExpectedSMP nextMsg; + user_t *u; + + u = user_findhandle(ic, handle); + if(!u) return; + context = otrl_context_find(us, handle, + ic->acc->user, ic->acc->prpl->name, 1, NULL, NULL, NULL); + if(!context) return; + nextMsg = context->smstate->nextExpected; + + tlv = otrl_tlv_find(tlvs, OTRL_TLV_SMP1); + if (tlv) { + if (nextMsg != OTRL_SMP_EXPECT1) { + irc_usermsg(irc, "smp %s: spurious SMP1 received, aborting", u->nick); + otrl_message_abort_smp(us, ops, u->ic, context); + otrl_sm_state_free(context->smstate); + } else { + irc_usermsg(irc, "smp: initiated by %s" + " - respond with \x02otr smp %s \x02", + u->nick, u->nick); + /* smp stays in EXPECT1 until user responds */ + } + } + tlv = otrl_tlv_find(tlvs, OTRL_TLV_SMP2); + if (tlv) { + if (nextMsg != OTRL_SMP_EXPECT2) { + irc_usermsg(irc, "smp %s: spurious SMP2 received, aborting", u->nick); + otrl_message_abort_smp(us, ops, u->ic, context); + otrl_sm_state_free(context->smstate); + } else { + /* SMP2 received, otrl_message_receiving will have sent SMP3 */ + context->smstate->nextExpected = OTRL_SMP_EXPECT4; + } + } + tlv = otrl_tlv_find(tlvs, OTRL_TLV_SMP3); + if (tlv) { + if (nextMsg != OTRL_SMP_EXPECT3) { + irc_usermsg(irc, "smp %s: spurious SMP3 received, aborting", u->nick); + otrl_message_abort_smp(us, ops, u->ic, context); + otrl_sm_state_free(context->smstate); + } else { + /* SMP3 received, otrl_message_receiving will have sent SMP4 and set fp trust */ + const char *trust = context->active_fingerprint->trust; + if(!trust || trust[0]=='\0') { + irc_usermsg(irc, "smp %s: secrets did not match, fingerprint not trusted", + u->nick); + } else { + irc_usermsg(irc, "smp %s: secrets proved equal, fingerprint trusted", + u->nick); + } + otrl_sm_state_free(context->smstate); + /* smp is in back in EXPECT1 */ + } + } + tlv = otrl_tlv_find(tlvs, OTRL_TLV_SMP4); + if (tlv) { + if (nextMsg != OTRL_SMP_EXPECT4) { + irc_usermsg(irc, "smp %s: spurious SMP4 received, aborting", u->nick); + otrl_message_abort_smp(us, ops, u->ic, context); + otrl_sm_state_free(context->smstate); + } else { + /* SMP4 received, otrl_message_receiving will have set fp trust */ + const char *trust = context->active_fingerprint->trust; + if(!trust || trust[0]=='\0') { + irc_usermsg(irc, "smp %s: secrets did not match, fingerprint not trusted", + u->nick); + } else { + irc_usermsg(irc, "smp %s: secrets proved equal, fingerprint trusted", + u->nick); + } + otrl_sm_state_free(context->smstate); + /* smp is in back in EXPECT1 */ + } + } + tlv = otrl_tlv_find(tlvs, OTRL_TLV_SMP_ABORT); + if (tlv) { + irc_usermsg(irc, "smp: received abort from %s", u->nick); + otrl_sm_state_free(context->smstate); + /* smp is in back in EXPECT1 */ + } +} + +/* helper to assert that account and protocol names given to ops below always + match the im_connection passed through as opdata */ +struct im_connection *check_imc(void *opdata, const char *accountname, + const char *protocol) +{ + struct im_connection *ic = (struct im_connection *)opdata; + + if (strcmp(accountname, ic->acc->user) != 0) { + log_message(LOGLVL_WARNING, + "otr: internal account name mismatch: '%s' vs '%s'", + accountname, ic->acc->user); + } + if (strcmp(protocol, ic->acc->prpl->name) != 0) { + log_message(LOGLVL_WARNING, + "otr: internal protocol name mismatch: '%s' vs '%s'", + protocol, ic->acc->prpl->name); + } + + return ic; +} + +const char *peernick(irc_t *irc, const char *handle, const char *protocol) +{ + user_t *u; + static char fallback[512]; + + g_snprintf(fallback, 511, "%s/%s", handle, protocol); + for(u=irc->users; u; u=u->next) { + struct prpl *prpl; + if(!u->ic || !u->handle) + break; + prpl = u->ic->acc->prpl; + if(strcmp(prpl->name, protocol) == 0 + && prpl->handle_cmp(u->handle, handle) == 0) { + return u->nick; + } + } + + return fallback; +} + +void show_fingerprints(irc_t *irc, ConnContext *ctx) +{ + char human[45]; + Fingerprint *fp; + const char *trust; + int count=0; + + for(fp=&ctx->fingerprint_root; fp; fp=fp->next) { + if(!fp->fingerprint) + continue; + count++; + otrl_privkey_hash_to_human(human, fp->fingerprint); + if(!fp->trust || fp->trust[0] == '\0') { + trust="untrusted"; + } else { + trust=fp->trust; + } + if(fp == ctx->active_fingerprint) { + irc_usermsg(irc, "\x02%s (%s)\x02", human, trust); + } else { + irc_usermsg(irc, "%s (%s)", human, trust); + } + } + if(count==0) + irc_usermsg(irc, "no fingerprints"); +} + +void otr_keygen(irc_t *irc, const char *handle, const char *protocol) +{ + GError *err; + GThread *thr; + struct kgdata *kg; + gint ev; + + irc_usermsg(irc, "generating new otr privkey for %s/%s...", + handle, protocol); + + kg = g_new0(struct kgdata, 1); + if(!kg) { + irc_usermsg(irc, "otr keygen failed: out of memory"); + return; + } + + /* Assemble the job description to be passed to thread and handler */ + kg->irc = irc; + kg->keyfile = g_strdup_printf("%s%s.otr_keys", global.conf->configdir, kg->irc->nick); + if(!kg->keyfile) { + irc_usermsg(irc, "otr keygen failed: out of memory"); + g_free(kg); + return; + } + kg->handle = handle; + kg->protocol = protocol; + kg->mutex = g_mutex_new(); + if(!kg->mutex) { + irc_usermsg(irc, "otr keygen failed: couldn't create mutex"); + g_free(kg->keyfile); + g_free(kg); + return; + } + kg->done = FALSE; + + /* Poll for completion of the thread periodically. I would have preferred + to just wait on a pipe but this way it's portable to Windows. *sigh* + */ + ev = b_timeout_add(1000, &keygen_finish_handler, kg); + if(!ev) { + irc_usermsg(irc, "otr keygen failed: couldn't register timeout"); + g_free(kg->keyfile); + g_mutex_free(kg->mutex); + g_free(kg); + return; + } + + thr = g_thread_create(&otr_keygen_thread_func, kg, FALSE, &err); + if(!thr) { + irc_usermsg(irc, "otr keygen failed: %s", err->message); + g_free(kg->keyfile); + g_mutex_free(kg->mutex); + g_free(kg); + b_event_remove(ev); + } +} + +gpointer otr_keygen_thread_func(gpointer data) +{ + struct kgdata *kg = (struct kgdata *)data; + + /* lock OTR subsystem and do the work */ + g_mutex_lock(kg->irc->otr_mutex); + kg->result = otrl_privkey_generate(kg->irc->otr_us, kg->keyfile, kg->handle, + kg->protocol); + g_mutex_unlock(kg->irc->otr_mutex); + /* OTR enabled again */ + + /* notify mainloop */ + g_mutex_lock(kg->mutex); + kg->done = TRUE; + g_mutex_unlock(kg->mutex); + + return NULL; +} + +gboolean keygen_finish_handler(gpointer data, gint fd, b_input_condition cond) +{ + struct kgdata *kg = (struct kgdata *)data; + int done; + + g_mutex_lock(kg->mutex); + done = kg->done; + g_mutex_unlock(kg->mutex); + if(kg->done) { + if(kg->result) { + irc_usermsg(kg->irc, "otr keygen failed: libgcrypt error"); /* TODO: diagnostics */ + } else { + irc_usermsg(kg->irc, "otr keygen for %s/%s complete", kg->handle, kg->protocol); + } + g_free(kg->keyfile); + g_mutex_free(kg->mutex); + g_free(kg); + return FALSE; /* unregister timeout */ + } + + return TRUE; /* still working, continue checking */ +} + +void yes_keygen(gpointer w, void *data) +{ + account_t *acc = (account_t *)data; + + otr_keygen(acc->irc, acc->user, acc->prpl->name); +} + +void no_keygen(gpointer w, void *data) +{ + account_t *acc = (account_t *)data; + + irc_usermsg(acc->irc, "proceeding without key, otr inoperable on %s/%s", + acc->user, acc->prpl->name); +} + + +#else /* WITH_OTR undefined */ + +void cmd_otr(irc_t *irc, char **args) +{ + irc_usermsg(irc, "otr: n/a, compiled without OTR support"); +} + +#endif diff --git a/otr.h b/otr.h new file mode 100644 index 00000000..ef5453ef --- /dev/null +++ b/otr.h @@ -0,0 +1,61 @@ +#ifndef BITLBEE_PROTOCOLS_OTR_H +#define BITLBEE_PROTOCOLS_OTR_H + +#include "bitlbee.h" + + +// forward decls to avoid mutual dependencies +struct irc; +struct im_connection; +struct account; + +// 'otr' root command, hooked up in root_commands.c +void cmd_otr(struct irc *, char **args); + + +#ifdef WITH_OTR +#include +#include +#include + +/* called from main() */ +void otr_init(void); + +/* called by storage_* functions */ +void otr_load(struct irc *irc); +void otr_save(struct irc *irc); +void otr_remove(const char *nick); +void otr_rename(const char *onick, const char *nnick); + +/* called from account_add() */ +void otr_check_for_key(struct account *a); + +/* called from imcb_buddy_msg() */ +char *otr_handle_message(struct im_connection *ic, const char *handle, + const char *msg); + +/* called from imc_buddy_msg() */ +int otr_send_message(struct im_connection *ic, const char *handle, const char *msg, + int flags); + +#else + +typedef void *OtrlUserState; +typedef void *OtrlMessageAppOps; + +#define otrl_userstate_create() (NULL) +#define otrl_userstate_free(us) {} + +#define otr_init() {} +#define otr_load(irc) {} +#define otr_save(irc) {} +#define otr_remove(nick) {} +#define otr_rename(onick,nnick) {} +#define otr_check_for_key(acc) {} +#define otr_handle_msg(ic,handle,msg) (g_strdup(msg)) +#define otr_send_message(ic,h,m,f) (ic->acc->prpl->buddy_msg(ic,h,m,f)) + +void cmd_otr_nosupport(void *, char **); + +#endif +#endif diff --git a/protocols/nogaim.c b/protocols/nogaim.c index 3ce15166..d8bfad14 100644 --- a/protocols/nogaim.c +++ b/protocols/nogaim.c @@ -629,9 +629,15 @@ void imcb_buddy_msg( struct im_connection *ic, char *handle, char *msg, uint32_t irc_t *irc = ic->irc; char *wrapped; user_t *u; - + + /* pass the message through OTR */ + msg = otr_handle_message(ic, handle, msg); + if(!msg) { + /* this was an internal OTR protocol message */ + return; + } + u = user_findhandle( ic, handle ); - if( !u ) { char *h = set_getstr( &irc->set, "handle_unknown" ); @@ -641,6 +647,7 @@ void imcb_buddy_msg( struct im_connection *ic, char *handle, char *msg, uint32_t if( set_getbool( &irc->set, "debug" ) ) imcb_log( ic, "Ignoring message from unknown handle %s", handle ); + g_free(msg); return; } else if( g_strncasecmp( h, "add", 3 ) == 0 ) @@ -673,6 +680,7 @@ void imcb_buddy_msg( struct im_connection *ic, char *handle, char *msg, uint32_t wrapped = word_wrap( msg, 425 ); irc_msgfrom( irc, u->nick, wrapped ); g_free( wrapped ); + g_free( msg ); } void imcb_buddy_typing( struct im_connection *ic, char *handle, uint32_t flags ) @@ -990,10 +998,11 @@ int imc_buddy_msg( struct im_connection *ic, char *handle, char *msg, int flags buf = escape_html( msg ); msg = buf; } + + /* if compiled without otr support, this just calls the prpl buddy_msg */ + st = otr_send_message(ic, handle, msg, flags); - st = ic->acc->prpl->buddy_msg( ic, handle, msg, flags ); - g_free( buf ); - + g_free(buf); return st; } diff --git a/root_commands.c b/root_commands.c index 26a42a25..61bb5da9 100644 --- a/root_commands.c +++ b/root_commands.c @@ -28,6 +28,7 @@ #include "crypting.h" #include "bitlbee.h" #include "help.h" +#include "otr.h" #include @@ -84,19 +85,27 @@ void root_command( irc_t *irc, char *cmd[] ) if( !cmd[0] ) return; + if(!g_mutex_trylock(irc->otr_mutex)) { + irc_usermsg(irc, "keygen in progress, bitlbee comatose - please wait"); + return; + } + for( i = 0; commands[i].command; i++ ) if( g_strcasecmp( commands[i].command, cmd[0] ) == 0 ) { if( !cmd[commands[i].required_parameters] ) { irc_usermsg( irc, "Not enough parameters given (need %d)", commands[i].required_parameters ); + g_mutex_unlock(irc->otr_mutex); return; } commands[i].execute( irc, cmd ); + g_mutex_unlock(irc->otr_mutex); return; } irc_usermsg( irc, "Unknown command: %s. Please use \x02help commands\x02 to get a list of available commands.", cmd[0] ); + g_mutex_unlock(irc->otr_mutex); } static void cmd_help( irc_t *irc, char **cmd ) @@ -240,6 +249,8 @@ static void cmd_account( irc_t *irc, char **cmd ) } irc_usermsg( irc, "Account successfully added" ); + + otr_check_for_key(a); } else if( g_strcasecmp( cmd[1], "del" ) == 0 ) { @@ -990,5 +1001,6 @@ const command_t commands[] = { { "nick", 1, cmd_nick, 0 }, { "qlist", 0, cmd_qlist, 0 }, { "join_chat", 2, cmd_join_chat, 0 }, + { "otr", 1, cmd_otr, 0 }, { NULL } }; diff --git a/storage.c b/storage.c index 06044f80..6a62af83 100644 --- a/storage.c +++ b/storage.c @@ -28,6 +28,7 @@ #define BITLBEE_CORE #include "bitlbee.h" #include "crypting.h" +#include "otr.h" extern storage_t storage_text; extern storage_t storage_xml; @@ -114,11 +115,13 @@ storage_status_t storage_load (const char *nick, const char *password, irc_t * i status = st->load(nick, password, irc); if (status == STORAGE_OK) { irc_setpass(irc, password); + otr_load(irc); /* load our OTR userstate */ return status; } - if (status != STORAGE_NO_SUCH_USER) + if (status != STORAGE_NO_SUCH_USER) { return status; + } } return STORAGE_NO_SUCH_USER; @@ -126,7 +129,11 @@ storage_status_t storage_load (const char *nick, const char *password, irc_t * i storage_status_t storage_save (irc_t *irc, int overwrite) { - return ((storage_t *)global.storage->data)->save(irc, overwrite); + storage_status_t st; + + otr_save(irc); + st = ((storage_t *)global.storage->data)->save(irc, overwrite); + return st; } storage_status_t storage_remove (const char *nick, const char *password) @@ -146,6 +153,9 @@ storage_status_t storage_remove (const char *nick, const char *password) status != STORAGE_OK) ret = status; } + if (ret == STORAGE_OK) { + otr_remove(nick); + } return ret; } @@ -156,12 +166,14 @@ storage_status_t storage_rename (const char *onick, const char *nnick, const cha GList *gl = global.storage; storage_t *primary_storage = gl->data; irc_t *irc; - + /* First, try to rename in the current write backend, assuming onick * is stored there */ status = primary_storage->rename(onick, nnick, password); - if (status != STORAGE_NO_SUCH_USER) + if (status != STORAGE_NO_SUCH_USER) { + otr_rename(onick, nnick); return status; + } /* Try to load from a migration backend and save to the current backend. * Explicitly remove the account from the migration backend as otherwise @@ -185,6 +197,7 @@ storage_status_t storage_rename (const char *onick, const char *nnick, const cha irc_free(irc); storage_remove(onick, password); + otr_rename(onick, nnick); return STORAGE_OK; } diff --git a/unix.c b/unix.c index 0aaf505a..63ef7fae 100644 --- a/unix.c +++ b/unix.c @@ -26,6 +26,7 @@ #include "bitlbee.h" #include "commands.h" #include "crypting.h" +#include "otr.h" #include "protocols/nogaim.h" #include "help.h" #include "ipc.h" @@ -53,6 +54,7 @@ int main( int argc, char *argv[], char **envp ) b_main_init(); nogaim_init(); + otr_init(); srand( time( NULL ) ^ getpid() ); global.helpfile = g_strdup( HELP_FILE ); diff --git a/user.c b/user.c index 26676dd4..4d58f56b 100644 --- a/user.c +++ b/user.c @@ -140,7 +140,7 @@ user_t *user_find( irc_t *irc, char *nick ) return( NULL ); } -user_t *user_findhandle( struct im_connection *ic, char *handle ) +user_t *user_findhandle( struct im_connection *ic, const char *handle ) { user_t *u; char *nick; diff --git a/user.h b/user.h index c6b933bc..9d8a41a0 100644 --- a/user.h +++ b/user.h @@ -55,7 +55,7 @@ typedef struct __USER user_t *user_add( struct irc *irc, char *nick ); int user_del( irc_t *irc, char *nick ); G_MODULE_EXPORT user_t *user_find( irc_t *irc, char *nick ); -G_MODULE_EXPORT user_t *user_findhandle( struct im_connection *ic, char *handle ); +G_MODULE_EXPORT user_t *user_findhandle( struct im_connection *ic, const char *handle ); void user_rename( irc_t *irc, char *oldnick, char *newnick ); #endif /* __USER_H__ */ -- cgit v1.2.3 From 3c80a9de52771f14e8aa947688fbf1dfc6ef5895 Mon Sep 17 00:00:00 2001 From: Sven Moritz Hallberg Date: Sun, 3 Feb 2008 23:28:13 +0100 Subject: otr: check some error conditions --- otr.c | 18 +++++++++++++----- 1 file changed, 13 insertions(+), 5 deletions(-) diff --git a/otr.c b/otr.c index 2b920f4b..2ac40e60 100644 --- a/otr.c +++ b/otr.c @@ -163,12 +163,12 @@ void otr_load(irc_t *irc) g_snprintf(s, 511, "%s%s.otr_keys", global.conf->configdir, irc->nick); e = otrl_privkey_read(irc->otr_us, s); if(e && e!=ENOENT) { - log_message(LOGLVL_ERROR, "%s: %s", s, strerror(e)); + log_message(LOGLVL_ERROR, "otr load: %s: %s", s, strerror(e)); } g_snprintf(s, 511, "%s%s.otr_fprints", global.conf->configdir, irc->nick); e = otrl_privkey_read_fingerprints(irc->otr_us, s, NULL, NULL); if(e && e!=ENOENT) { - log_message(LOGLVL_ERROR, "%s: %s", s, strerror(e)); + log_message(LOGLVL_ERROR, "otr load: %s: %s", s, strerror(e)); } /* check for otr keys on all accounts */ @@ -180,11 +180,15 @@ void otr_load(irc_t *irc) void otr_save(irc_t *irc) { char s[512]; + gcry_error_t e; log_message(LOGLVL_DEBUG, "otr_save '%s'", irc->nick); g_snprintf(s, 511, "%s%s.otr_fprints", global.conf->configdir, irc->nick); - otrl_privkey_write_fingerprints(irc->otr_us, s); + e = otrl_privkey_write_fingerprints(irc->otr_us, s); + if(e) { + log_message(LOGLVL_ERROR, "otr save: %s: %s", s, strerror(e)); + } } void otr_remove(const char *nick) @@ -536,6 +540,7 @@ void cmd_otr_auth(irc_t *irc, char **args) ctx = otrl_context_find(irc->otr_us, u->handle, u->ic->acc->user, u->ic->acc->prpl->name, 1, NULL, NULL, NULL); if(!ctx) { + /* huh? out of memory or what? */ return; } @@ -685,7 +690,10 @@ void otr_handle_smp(struct im_connection *ic, const char *handle, OtrlTLV *tlvs) if(!u) return; context = otrl_context_find(us, handle, ic->acc->user, ic->acc->prpl->name, 1, NULL, NULL, NULL); - if(!context) return; + if(!context) { + /* huh? out of memory or what? */ + return; + } nextMsg = context->smstate->nextExpected; tlv = otrl_tlv_find(tlvs, OTRL_TLV_SMP1); @@ -914,7 +922,7 @@ gboolean keygen_finish_handler(gpointer data, gint fd, b_input_condition cond) g_mutex_unlock(kg->mutex); if(kg->done) { if(kg->result) { - irc_usermsg(kg->irc, "otr keygen failed: libgcrypt error"); /* TODO: diagnostics */ + irc_usermsg(kg->irc, "otr keygen: %s", strerror(kg->result)); } else { irc_usermsg(kg->irc, "otr keygen for %s/%s complete", kg->handle, kg->protocol); } -- cgit v1.2.3 From a13855a57daae6eba05c9600b69f640c2949e944 Mon Sep 17 00:00:00 2001 From: Sven Moritz Hallberg Date: Wed, 6 Feb 2008 17:42:23 +0100 Subject: use peernicks and try to guess max message size --- otr.c | 50 +++++++++++++++++++++++++++++++++++--------------- 1 file changed, 35 insertions(+), 15 deletions(-) diff --git a/otr.c b/otr.c index 2ac40e60..78952699 100644 --- a/otr.c +++ b/otr.c @@ -42,10 +42,9 @@ void op_still_secure(void *opdata, ConnContext *context, int is_reply); void op_log_message(void *opdata, const char *message); -/* TODO: int op_max_message_size(void *opdata, ConnContext *context); */ +int op_max_message_size(void *opdata, ConnContext *context); -/* TODO: const char *op_account_name(void *opdata, const char *account, - const char *protocol); */ +const char *op_account_name(void *opdata, const char *account, const char *protocol); /** otr sub-command handlers: **/ @@ -131,8 +130,8 @@ void otr_init(void) global.otr_ops.gone_insecure = &op_gone_insecure; global.otr_ops.still_secure = &op_still_secure; global.otr_ops.log_message = &op_log_message; - global.otr_ops.max_message_size = NULL; - global.otr_ops.account_name = NULL; + global.otr_ops.max_message_size = &op_max_message_size; + global.otr_ops.account_name = &op_account_name; global.otr_ops.account_name_free = NULL; } @@ -220,13 +219,12 @@ void otr_rename(const char *onick, const char *nnick) void otr_check_for_key(account_t *a) { irc_t *irc = a->irc; - char buf[45]; - char *fp; + OtrlPrivKey *k; - fp = otrl_privkey_fingerprint(irc->otr_us, buf, a->user, a->prpl->name); - if(fp) { - irc_usermsg(irc, "otr: %s/%s ready with f'print %s", - a->user, a->prpl->name, fp); + k = otrl_privkey_find(irc->otr_us, a->user, a->prpl->name); + if(k) { + irc_usermsg(irc, "otr: %s/%s ready", + a->user, a->prpl->name); } else { otr_keygen(irc, a->user, a->prpl->name); } @@ -241,8 +239,8 @@ char *otr_handle_message(struct im_connection *ic, const char *handle, const cha if(!g_mutex_trylock(ic->irc->otr_mutex)) { /* TODO: queue msgs received during keygen for later */ - irc_usermsg(ic->irc, "msg from %s/%s during keygen - dropped", - handle, ic->acc->prpl->name); + irc_usermsg(ic->irc, "msg from %s during keygen - dropped", + peernick(ic->irc, handle, ic->acc->prpl->name)); return NULL; } @@ -289,8 +287,8 @@ int otr_send_message(struct im_connection *ic, const char *handle, const char *m ConnContext *ctx = NULL; if(!g_mutex_trylock(ic->irc->otr_mutex)) { - irc_usermsg(ic->irc, "msg to %s/%s during keygen - not sent", - handle, ic->acc->prpl->name); + irc_usermsg(ic->irc, "msg to %s during keygen - not sent", + peernick(ic->irc, handle, ic->acc->prpl->name)); return 1; } @@ -488,6 +486,27 @@ void op_log_message(void *opdata, const char *message) log_message(LOGLVL_INFO, "%s", message); } +int op_max_message_size(void *opdata, ConnContext *context) +{ + /* TODO: make max_message_size a property of the prpl. + the values here are taken from the libotr UPGRADING file */ + if(!strcmp(context->protocol, "msn")) + return 1409; + if(!strcmp(context->protocol, "yahoo")) + return 832; + if(!strcmp(context->protocol, "oscar")) + return 2343; +} + +const char *op_account_name(void *opdata, const char *account, const char *protocol) +{ + struct im_connection *ic = (struct im_connection *)opdata; + + log_message(LOGLVL_DEBUG, "op_account_name '%s' '%s'", account, protocol); + + return peernick(ic->irc, account, protocol); +} + /*** OTR sub-command handlers ***/ @@ -946,6 +965,7 @@ void no_keygen(gpointer w, void *data) { account_t *acc = (account_t *)data; + /* TODO: remember that we didn't want a key? */ irc_usermsg(acc->irc, "proceeding without key, otr inoperable on %s/%s", acc->user, acc->prpl->name); } -- cgit v1.2.3 From 5a71d9c5b14aa749b532666b71b25ce2afcdc5bb Mon Sep 17 00:00:00 2001 From: Sven Moritz Hallberg Date: Sat, 9 Feb 2008 18:58:13 +0100 Subject: - add support for setting ops/voice according to OTR msgstate - add 'otr trust' user command - support non-otr messages during keygen - run otr messages through strip_html - interpret and tags in html messages - record max message size in prpl - add 'encrypted' flag to user_t - cosmetics --- irc.c | 25 ++-- irc.h | 2 +- lib/misc.c | 12 +- otr.c | 286 ++++++++++++++++++++++++++++++++++++++-------- protocols/jabber/jabber.c | 1 + protocols/msn/msn.c | 1 + protocols/nogaim.c | 17 ++- protocols/nogaim.h | 4 + protocols/oscar/oscar.c | 1 + protocols/yahoo/yahoo.c | 1 + set.c | 145 ++++++++++++++++++++--- set.h | 5 +- user.h | 1 + 13 files changed, 420 insertions(+), 81 deletions(-) diff --git a/irc.c b/irc.c index db681175..279401d0 100644 --- a/irc.c +++ b/irc.c @@ -101,8 +101,7 @@ irc_t *irc_new( int fd ) irc_write( irc, ":%s NOTICE AUTH :%s", irc->myhost, "BitlBee-IRCd initialized, please go on" ); irc_connection_list = g_slist_append( irc_connection_list, irc ); - - set_add( &irc->set, "away_devoice", "true", set_eval_away_devoice, irc ); + set_add( &irc->set, "auto_connect", "true", set_eval_bool, irc ); set_add( &irc->set, "auto_reconnect", "false", set_eval_bool, irc ); set_add( &irc->set, "auto_reconnect_delay", "300", set_eval_int, irc ); @@ -114,7 +113,9 @@ irc_t *irc_new( int fd ) set_add( &irc->set, "display_namechanges", "false", set_eval_bool, irc ); set_add( &irc->set, "handle_unknown", "root", NULL, irc ); set_add( &irc->set, "lcnicks", "true", set_eval_bool, irc ); - set_add( &irc->set, "ops", "both", set_eval_ops, irc ); + set_add( &irc->set, "op_buddies", "false", set_eval_op_buddies, irc ); + set_add( &irc->set, "op_root", "true", set_eval_op_root, irc ); + set_add( &irc->set, "op_user", "true", set_eval_op_user, irc ); set_add( &irc->set, "password", NULL, passchange, irc ); set_add( &irc->set, "private", "true", set_eval_bool, irc ); set_add( &irc->set, "query_order", "lifo", NULL, irc ); @@ -123,6 +124,7 @@ irc_t *irc_new( int fd ) set_add( &irc->set, "strip_html", "true", NULL, irc ); set_add( &irc->set, "to_char", ": ", set_eval_to_char, irc ); set_add( &irc->set, "typing_notice", "false", set_eval_bool, irc ); + set_add( &irc->set, "voice_buddies", "notaway", set_eval_voice_buddies, irc ); conf_loaddefaults( irc ); @@ -642,7 +644,8 @@ void irc_names( irc_t *irc, char *channel ) user_t *u; char namelist[385] = ""; struct groupchat *c = NULL; - char *ops = set_getstr( &irc->set, "ops" ); + char *oo = set_getstr(&irc->set, "op_buddies"); + char *vo = set_getstr(&irc->set, "voice_buddies"); /* RFCs say there is no error reply allowed on NAMES, so when the channel is invalid, just give an empty reply. */ @@ -657,10 +660,13 @@ void irc_names( irc_t *irc, char *channel ) *namelist = 0; } - if( u->ic && !u->away && set_getbool( &irc->set, "away_devoice" ) ) + if( u->ic && !u->away && !strcmp(vo, "notaway") ) strcat( namelist, "+" ); - else if( ( strcmp( u->nick, irc->mynick ) == 0 && ( strcmp( ops, "root" ) == 0 || strcmp( ops, "both" ) == 0 ) ) || - ( strcmp( u->nick, irc->nick ) == 0 && ( strcmp( ops, "user" ) == 0 || strcmp( ops, "both" ) == 0 ) ) ) + else if( ( strcmp( u->nick, irc->mynick ) == 0 && set_getbool(&irc->set, "op_root") ) || + ( strcmp( u->nick, irc->nick ) == 0 && set_getbool(&irc->set, "op_user") ) || + ( !u->away && !strcmp(oo, "notaway") ) || + ( u->encrypted>1 && !strcmp(oo, "trusted") ) || + ( u->encrypted && !strcmp(oo, "encrypted") ) ) strcat( namelist, "@" ); strcat( namelist, u->nick ); @@ -673,9 +679,10 @@ void irc_names( irc_t *irc, char *channel ) /* root and the user aren't in the channel userlist but should show up in /NAMES, so list them first: */ - sprintf( namelist, "%s%s %s%s ", strcmp( ops, "root" ) == 0 || strcmp( ops, "both" ) ? "@" : "", irc->mynick, - strcmp( ops, "user" ) == 0 || strcmp( ops, "both" ) ? "@" : "", irc->nick ); + sprintf( namelist, "%s%s %s%s ", set_getbool(&irc->set, "op_root") ? "@" : "", irc->mynick, + set_getbool(&irc->set, "op_user") ? "@" : "", irc->nick ); + /* TODO: Honor op/voice_buddies in chats?! */ for( l = c->in_room; l; l = l->next ) if( ( u = user_findhandle( c->ic, l->data ) ) ) { if( strlen( namelist ) + strlen( u->nick ) > sizeof( namelist ) - 4 ) diff --git a/irc.h b/irc.h index 318e263e..77738640 100644 --- a/irc.h +++ b/irc.h @@ -29,7 +29,7 @@ #include "otr.h" #define IRC_MAX_LINE 512 -#define IRC_MAX_ARGS 8 +#define IRC_MAX_ARGS 16 #define IRC_LOGIN_TIMEOUT 60 #define IRC_PING_STRING "PinglBee" diff --git a/lib/misc.c b/lib/misc.c index 18d98f9e..a8df2354 100644 --- a/lib/misc.c +++ b/lib/misc.c @@ -142,6 +142,7 @@ void strip_html( char *in ) char *out = g_malloc( strlen( in ) + 1 ); char *s = out, *cs; int i, matched; + int taglen; memset( out, 0, strlen( in ) + 1 ); @@ -158,9 +159,18 @@ void strip_html( char *in ) while( *in && *in != '>' ) in ++; + taglen = in-cs-1; /* not <0 because the above loop runs at least once */ if( *in ) { - if( g_strncasecmp( cs+1, "br", 2) == 0 ) + if( g_strncasecmp( cs+1, "b", taglen) == 0 ) + *(s++) = '\x02'; + else if( g_strncasecmp( cs+1, "/b", taglen) == 0 ) + *(s++) = '\x02'; + else if( g_strncasecmp( cs+1, "i", taglen) == 0 ) + *(s++) = '\x1f'; + else if( g_strncasecmp( cs+1, "/i", taglen) == 0 ) + *(s++) = '\x1f'; + else if( g_strncasecmp( cs+1, "br", 2) == 0 ) *(s++) = '\n'; in ++; } diff --git a/otr.c b/otr.c index 78952699..60581ee6 100644 --- a/otr.c +++ b/otr.c @@ -34,8 +34,6 @@ void op_write_fingerprints(void *opdata); void op_gone_secure(void *opdata, ConnContext *context); -void op_gone_secure(void *opdata, ConnContext *context); - void op_gone_insecure(void *opdata, ConnContext *context); void op_still_secure(void *opdata, ConnContext *context, int is_reply); @@ -52,7 +50,8 @@ const char *op_account_name(void *opdata, const char *account, const char *proto /* TODO: void cmd_otr_keygen(irc_t *irc, char **args); */ void cmd_otr_abort(irc_t *irc, char **args); /* TODO: does this cmd even make sense? */ void cmd_otr_request(irc_t *irc, char **args); /* TODO: do we even need this? */ -void cmd_otr_auth(irc_t *irc, char **args); +void cmd_otr_smp(irc_t *irc, char **args); +void cmd_otr_trust(irc_t *irc, char **args); /* TODO: void cmd_otr_affirm(irc_t *irc, char **args); */ void cmd_otr_fprints(irc_t *irc, char **args); void cmd_otr_info(irc_t *irc, char **args); @@ -61,7 +60,8 @@ void cmd_otr_policy(irc_t *irc, char **args); const command_t otr_commands[] = { { "abort", 1, &cmd_otr_abort, 0 }, { "request", 1, &cmd_otr_request, 0 }, - { "auth", 2, &cmd_otr_auth, 0 }, + { "smp", 2, &cmd_otr_smp, 0 }, + { "trust", 6, &cmd_otr_trust, 0 }, { "fprints", 0, &cmd_otr_fprints, 0 }, { "info", 1, &cmd_otr_info, 0 }, { "policy", 0, &cmd_otr_policy, 0 }, @@ -96,12 +96,22 @@ void no_keygen(gpointer w, void *data); struct im_connection *check_imc(void *opdata, const char *accountname, const char *protocol); -/* determine the nick for a given handle/protocol pair */ +/* determine the nick for a given handle/protocol pair + returns "handle/protocol" if not found */ const char *peernick(irc_t *irc, const char *handle, const char *protocol); +/* determine the user_t for a given handle/protocol pair + returns NULL if not found */ +user_t *peeruser(irc_t *irc, const char *handle, const char *protocol); + /* handle SMP TLVs from a received message */ void otr_handle_smp(struct im_connection *ic, const char *handle, OtrlTLV *tlvs); +/* update op/voice flag of given user according to encryption state and settings + returns 0 if neither op_buddies nor voice_buddies is set to "encrypted", + i.e. msgstate should be announced seperately */ +int otr_update_modeflags(irc_t *irc, user_t *u); + /* show the list of fingerprints associated with a given context */ void show_fingerprints(irc_t *irc, ConnContext *ctx); @@ -238,8 +248,15 @@ char *otr_handle_message(struct im_connection *ic, const char *handle, const cha char *colormsg; if(!g_mutex_trylock(ic->irc->otr_mutex)) { - /* TODO: queue msgs received during keygen for later */ - irc_usermsg(ic->irc, "msg from %s during keygen - dropped", + user_t *u = user_findhandle(ic, handle); + + /* fallback for non-otr clients */ + if(u && !u->encrypted) { + return g_strdup(msg); + } + + /* TODO: queue msgs received during keygen for later? */ + irc_usermsg(ic->irc, "otr msg from %s during keygen - dropped", peernick(ic->irc, handle, ic->acc->prpl->name)); return NULL; } @@ -282,14 +299,24 @@ char *otr_handle_message(struct im_connection *ic, const char *handle, const cha int otr_send_message(struct im_connection *ic, const char *handle, const char *msg, int flags) { - int st; - char *otrmsg = NULL; - ConnContext *ctx = NULL; - + int st; + char *otrmsg = NULL; + ConnContext *ctx = NULL; + if(!g_mutex_trylock(ic->irc->otr_mutex)) { - irc_usermsg(ic->irc, "msg to %s during keygen - not sent", - peernick(ic->irc, handle, ic->acc->prpl->name)); - return 1; + user_t *u = user_findhandle(ic, handle); + + /* Fallback for non-otr clients. + Yes, we must be very sure this doesn't send stuff in the clear where it + shouldn't... */ + if(u && !u->encrypted) { + return ic->acc->prpl->buddy_msg(ic, (char *)handle, (char *)msg, flags); + } + + /* otherwise refuse to send */ + irc_usermsg(ic->irc, "otr msg to %s not sent during keygen", + peernick(ic->irc, handle, ic->acc->prpl->name)); + return 1; } st = otrl_message_sending(ic->irc->otr_us, &global.otr_ops, ic, @@ -413,14 +440,17 @@ void op_inject_message(void *opdata, const char *accountname, } int op_display_otr_message(void *opdata, const char *accountname, - const char *protocol, const char *username, const char *msg) + const char *protocol, const char *username, const char *message) { struct im_connection *ic = check_imc(opdata, accountname, protocol); + char *msg = g_strdup(message); - log_message(LOGLVL_DEBUG, "op_display_otr_message '%s' '%s' '%s' '%s'", accountname, protocol, username, msg); + log_message(LOGLVL_DEBUG, "op_display_otr_message '%s' '%s' '%s' '%s'", accountname, protocol, username, message); - irc_usermsg(ic->irc, "%s", msg); + strip_html(msg); + irc_usermsg(ic->irc, "otr: %s", msg); + g_free(msg); return 0; } @@ -451,51 +481,84 @@ void op_gone_secure(void *opdata, ConnContext *context) { struct im_connection *ic = check_imc(opdata, context->accountname, context->protocol); + user_t *u; log_message(LOGLVL_DEBUG, "op_gone_secure '%s' '%s' '%s'", context->accountname, context->protocol, context->username); - irc_usermsg(ic->irc, "conversation with %s is now off the record", - peernick(ic->irc, context->username, context->protocol)); + u = peeruser(ic->irc, context->username, context->protocol); + if(!u) { + log_message(LOGLVL_ERROR, + "BUG: otr.c: op_gone_secure: user_t for %s/%s not found!", + context->username, context->protocol); + return; + } + if(context->active_fingerprint->trust[0]) + u->encrypted = 2; + else + u->encrypted = 1; + if(!otr_update_modeflags(ic->irc, u)) + irc_usermsg(ic->irc, "conversation with %s is now off the record", u->nick); } void op_gone_insecure(void *opdata, ConnContext *context) { struct im_connection *ic = check_imc(opdata, context->accountname, context->protocol); + user_t *u; log_message(LOGLVL_DEBUG, "op_gone_insecure '%s' '%s' '%s'", context->accountname, context->protocol, context->username); - irc_usermsg(ic->irc, "conversation with %s is now in the clear", - peernick(ic->irc, context->username, context->protocol)); + u = peeruser(ic->irc, context->username, context->protocol); + if(!u) { + log_message(LOGLVL_ERROR, + "BUG: otr.c: op_gone_insecure: user_t for %s/%s not found!", + context->username, context->protocol); + return; + } + u->encrypted = 0; + if(!otr_update_modeflags(ic->irc, u)) + irc_usermsg(ic->irc, "conversation with %s is now in the clear", u->nick); } void op_still_secure(void *opdata, ConnContext *context, int is_reply) { struct im_connection *ic = check_imc(opdata, context->accountname, context->protocol); + user_t *u; log_message(LOGLVL_DEBUG, "op_still_secure '%s' '%s' '%s' is_reply=%d", context->accountname, context->protocol, context->username, is_reply); - irc_usermsg(ic->irc, "otr connection with %s has been refreshed", - peernick(ic->irc, context->username, context->protocol)); + u = peeruser(ic->irc, context->username, context->protocol); + if(!u) { + log_message(LOGLVL_ERROR, + "BUG: otr.c: op_still_secure: user_t for %s/%s not found!", + context->username, context->protocol); + return; + } + if(context->active_fingerprint->trust[0]) + u->encrypted = 2; + else + u->encrypted = 1; + if(!otr_update_modeflags(ic->irc, u)) + irc_usermsg(ic->irc, "otr connection with %s has been refreshed", u->nick); } void op_log_message(void *opdata, const char *message) { - log_message(LOGLVL_INFO, "%s", message); + char *msg = g_strdup(message); + + strip_html(msg); + log_message(LOGLVL_INFO, "otr: %s", msg); + g_free(msg); } int op_max_message_size(void *opdata, ConnContext *context) { - /* TODO: make max_message_size a property of the prpl. - the values here are taken from the libotr UPGRADING file */ - if(!strcmp(context->protocol, "msn")) - return 1409; - if(!strcmp(context->protocol, "yahoo")) - return 832; - if(!strcmp(context->protocol, "oscar")) - return 2343; + struct im_connection *ic = + check_imc(opdata, context->accountname, context->protocol); + + return ic->acc->prpl->mms; } const char *op_account_name(void *opdata, const char *account, const char *protocol) @@ -541,7 +604,7 @@ void cmd_otr_request(irc_t *irc, char **args) imc_buddy_msg(u->ic, u->handle, "?OTR?", 0); } -void cmd_otr_auth(irc_t *irc, char **args) +void cmd_otr_smp(irc_t *irc, char **args) { user_t *u; ConnContext *ctx; @@ -588,6 +651,80 @@ void cmd_otr_auth(irc_t *irc, char **args) } } +int hexval(char a) +{ + int x=tolower(a); + + if(x>='a' && x<='f') + x = x - 'a' + 10; + else if(x>='0' && x<='9') + x = x - '0'; + else + return -1; + + return x; +} + +void cmd_otr_trust(irc_t *irc, char **args) +{ + user_t *u; + ConnContext *ctx; + unsigned char raw[20]; + Fingerprint *fp; + int i,j; + + u = user_find(irc, args[1]); + if(!u || !u->ic) { + irc_usermsg(irc, "%s: unknown user", args[1]); + return; + } + + ctx = otrl_context_find(irc->otr_us, u->handle, + u->ic->acc->user, u->ic->acc->prpl->name, 0, NULL, NULL, NULL); + if(!ctx) { + irc_usermsg(irc, "%s: no otr context with user", args[1]); + return; + } + + /* convert given fingerprint to raw representation */ + for(i=0; i<5; i++) { + for(j=0; j<4; j++) { + char *p = args[2+i]+(2*j); + char *q = p+1; + int x, y; + + if(!*p || !*q) { + irc_usermsg(irc, "failed: truncated fingerprint block %d", i+1); + return; + } + + x = hexval(*p); + y = hexval(*q); + if(x<0) { + irc_usermsg(irc, "failed: %d. hex digit of block %d out of range", 2*j+1, i+1); + return; + } + if(y<0) { + irc_usermsg(irc, "failed: %d. hex digit of block %d out of range", 2*j+2, i+1); + return; + } + + raw[i*4+j] = x*16 + y; + } + } + fp = otrl_context_find_fingerprint(ctx, raw, 0, NULL); + if(!fp) { + irc_usermsg(irc, "failed: no such fingerprint for %s", args[1]); + } else { + char *trust = args[7] ? args[7] : "affirmed"; + otrl_context_set_trust(fp, trust); + irc_usermsg(irc, "fingerprint match, trust set to \"%s\"", trust); + if(u->encrypted) + u->encrypted = 2; + otr_update_modeflags(irc, u); + } +} + void cmd_otr_fprints(irc_t *irc, char **args) { if(args[1]) { @@ -648,7 +785,7 @@ void cmd_otr_info(irc_t *irc, char **args) ctx = otrl_context_find(irc->otr_us, u->handle, u->ic->acc->user, u->ic->acc->prpl->name, 0, NULL, NULL, NULL); if(!ctx) { - irc_usermsg(irc, "no otr info on %s", args[1]); + irc_usermsg(irc, "no otr context with %s", args[1]); return; } @@ -671,18 +808,21 @@ void cmd_otr_info(irc_t *irc, char **args) ctx->username, ctx->protocol, ctx->accountname, ctx->protocol); irc_usermsg(irc, " otr offer status: %s", offer_status); irc_usermsg(irc, " connection state: %s", message_state); - irc_usermsg(irc, " protocol version: %d", ctx->protocol_version); - fp = ctx->active_fingerprint; - if(!fp) { - irc_usermsg(irc, " active f'print: none"); - } else { - otrl_privkey_hash_to_human(human, fp->fingerprint); - if(!fp->trust || fp->trust[0] == '\0') { - trust="untrusted"; + + if(ctx->msgstate == OTRL_MSGSTATE_ENCRYPTED) { + irc_usermsg(irc, " protocol version: %d", ctx->protocol_version); + fp = ctx->active_fingerprint; + if(!fp) { + irc_usermsg(irc, " active f'print: none?"); } else { - trust=fp->trust; + otrl_privkey_hash_to_human(human, fp->fingerprint); + if(!fp->trust || fp->trust[0] == '\0') { + trust="untrusted"; + } else { + trust=fp->trust; + } + irc_usermsg(irc, " active f'print: %s (%s)", human, trust); } - irc_usermsg(irc, " active f'print: %s (%s)", human, trust); } } @@ -808,12 +948,10 @@ struct im_connection *check_imc(void *opdata, const char *accountname, return ic; } -const char *peernick(irc_t *irc, const char *handle, const char *protocol) +user_t *peeruser(irc_t *irc, const char *handle, const char *protocol) { user_t *u; - static char fallback[512]; - g_snprintf(fallback, 511, "%s/%s", handle, protocol); for(u=irc->users; u; u=u->next) { struct prpl *prpl; if(!u->ic || !u->handle) @@ -821,11 +959,55 @@ const char *peernick(irc_t *irc, const char *handle, const char *protocol) prpl = u->ic->acc->prpl; if(strcmp(prpl->name, protocol) == 0 && prpl->handle_cmp(u->handle, handle) == 0) { - return u->nick; + return u; } } - return fallback; + return NULL; +} + +const char *peernick(irc_t *irc, const char *handle, const char *protocol) +{ + static char fallback[512]; + + user_t *u = peeruser(irc, handle, protocol); + if(u) { + return u->nick; + } else { + g_snprintf(fallback, 511, "%s/%s", handle, protocol); + return fallback; + } +} + +int otr_update_modeflags(irc_t *irc, user_t *u) +{ + char *vo = set_getstr(&irc->set, "voice_buddies"); + char *oo = set_getstr(&irc->set, "op_buddies"); + char eflag=0, tflag=0; + int e = u->encrypted; + int t = (u->encrypted > 1); + + if(!strcmp(vo, "encrypted")) + eflag='v'; + else if(!strcmp(oo, "encrypted")) + eflag='o'; + if(!strcmp(vo, "trusted")) + tflag='v'; + else if(!strcmp(oo, "trusted")) + tflag='o'; + + if(!eflag) + return 0; + + if(tflag) { + irc_write( irc, ":%s!%s@%s MODE %s %c%c%c%c %s %s", irc->mynick, irc->mynick, irc->myhost, + irc->channel, e?'+':'-', eflag, t?'+':'-', tflag, u->nick, u->nick ); + } else { + irc_write( irc, ":%s!%s@%s MODE %s %c%c %s", irc->mynick, irc->mynick, irc->myhost, + irc->channel, e?'+':'-', eflag, u->nick ); + } + + return 1; } void show_fingerprints(irc_t *irc, ConnContext *ctx) @@ -965,9 +1147,13 @@ void no_keygen(gpointer w, void *data) { account_t *acc = (account_t *)data; - /* TODO: remember that we didn't want a key? */ irc_usermsg(acc->irc, "proceeding without key, otr inoperable on %s/%s", acc->user, acc->prpl->name); + /* TODO: + irc_usermsg(acc->irc, "setting otr policy for %s/%s to \"never\"", + acc->user, acc->prpl->name); + set_setstr(acc->set, "otr_policy", "never"); + */ } diff --git a/protocols/jabber/jabber.c b/protocols/jabber/jabber.c index 243ed7fd..dac18bae 100644 --- a/protocols/jabber/jabber.c +++ b/protocols/jabber/jabber.c @@ -510,6 +510,7 @@ void jabber_initmodule() struct prpl *ret = g_new0( struct prpl, 1 ); ret->name = "jabber"; + ret->mms = 0; /* no limit */ ret->login = jabber_login; ret->init = jabber_init; ret->logout = jabber_logout; diff --git a/protocols/msn/msn.c b/protocols/msn/msn.c index a2e8519a..335ae894 100644 --- a/protocols/msn/msn.c +++ b/protocols/msn/msn.c @@ -371,6 +371,7 @@ void msn_initmodule() struct prpl *ret = g_new0(struct prpl, 1); ret->name = "msn"; + ret->mms = 1409; /* this guess taken from libotr UPGRADING file */ ret->login = msn_login; ret->init = msn_init; ret->logout = msn_logout; diff --git a/protocols/nogaim.c b/protocols/nogaim.c index d8bfad14..138e2ce1 100644 --- a/protocols/nogaim.c +++ b/protocols/nogaim.c @@ -602,10 +602,9 @@ void imcb_buddy_status( struct im_connection *ic, const char *handle, int flags, /* else waste_any_state_information_for_now(); */ /* LISPy... */ - if( ( set_getbool( &ic->irc->set, "away_devoice" ) ) && /* Don't do a thing when user doesn't want it */ - ( u->online ) && /* Don't touch offline people */ - ( ( ( u->online != oo ) && !u->away ) || /* Voice joining people */ - ( ( u->online == oo ) && ( oa == !u->away ) ) ) ) /* (De)voice people changing state */ + if( ( u->online ) && /* Don't touch offline people */ + ( ( ( u->online != oo ) && !u->away ) || /* Do joining people */ + ( ( u->online == oo ) && ( oa == !u->away ) ) ) ) /* Do people changing state */ { char *from; @@ -618,8 +617,14 @@ void imcb_buddy_status( struct im_connection *ic, const char *handle, int flags, from = g_strdup_printf( "%s!%s@%s", ic->irc->mynick, ic->irc->mynick, ic->irc->myhost ); } - irc_write( ic->irc, ":%s MODE %s %cv %s", from, ic->irc->channel, - u->away?'-':'+', u->nick ); + if(!strcmp(set_getstr(&ic->irc->set, "voice_buddies"), "notaway")) { + irc_write( ic->irc, ":%s MODE %s %cv %s", from, ic->irc->channel, + u->away?'-':'+', u->nick ); + } + if(!strcmp(set_getstr(&ic->irc->set, "op_buddies"), "notaway")) { + irc_write( ic->irc, ":%s MODE %s %co %s", from, ic->irc->channel, + u->away?'-':'+', u->nick ); + } g_free( from ); } } diff --git a/protocols/nogaim.h b/protocols/nogaim.h index 7d391edd..3eb640e2 100644 --- a/protocols/nogaim.h +++ b/protocols/nogaim.h @@ -135,6 +135,10 @@ struct prpl { /* You should set this to the name of your protocol. * - The user sees this name ie. when imcb_log() is used. */ const char *name; + /* Maximum Message Size of this protocol. + * - Introduced for OTR, in order to fragment large protocol messages. + * - 0 means "unlimited". */ + unsigned int mms; /* Added this one to be able to add per-account settings, don't think * it should be used for anything else. You are supposed to use the diff --git a/protocols/oscar/oscar.c b/protocols/oscar/oscar.c index 30ad4b68..9167f6a3 100644 --- a/protocols/oscar/oscar.c +++ b/protocols/oscar/oscar.c @@ -2602,6 +2602,7 @@ void oscar_initmodule() { struct prpl *ret = g_new0(struct prpl, 1); ret->name = "oscar"; + ret->mms = 2343; /* this guess taken from libotr UPGRADING file */ ret->away_states = oscar_away_states; ret->init = oscar_init; ret->login = oscar_login; diff --git a/protocols/yahoo/yahoo.c b/protocols/yahoo/yahoo.c index 9f9ffcf7..52747bb4 100644 --- a/protocols/yahoo/yahoo.c +++ b/protocols/yahoo/yahoo.c @@ -353,6 +353,7 @@ void byahoo_initmodule( ) { struct prpl *ret = g_new0(struct prpl, 1); ret->name = "yahoo"; + ret->mms = 832; /* this guess taken from libotr UPGRADING file */ ret->init = byahoo_init; ret->login = byahoo_login; diff --git a/set.c b/set.c index 6f09843b..fa1933d5 100644 --- a/set.c +++ b/set.c @@ -208,28 +208,147 @@ char *set_eval_to_char( set_t *set, char *value ) return s; } -char *set_eval_ops( set_t *set, char *value ) +char *set_eval_op_root( set_t *set, char *value ) { irc_t *irc = set->data; + char *ret = set_eval_bool(set, value); + int b = bool2int(ret); - if( g_strcasecmp( value, "user" ) == 0 ) - irc_write( irc, ":%s!%s@%s MODE %s %s %s %s", irc->mynick, irc->mynick, irc->myhost, - irc->channel, "+o-o", irc->nick, irc->mynick ); - else if( g_strcasecmp( value, "root" ) == 0 ) - irc_write( irc, ":%s!%s@%s MODE %s %s %s %s", irc->mynick, irc->mynick, irc->myhost, - irc->channel, "-o+o", irc->nick, irc->mynick ); - else if( g_strcasecmp( value, "both" ) == 0 ) - irc_write( irc, ":%s!%s@%s MODE %s %s %s %s", irc->mynick, irc->mynick, irc->myhost, - irc->channel, "+oo", irc->nick, irc->mynick ); - else if( g_strcasecmp( value, "none" ) == 0 ) - irc_write( irc, ":%s!%s@%s MODE %s %s %s %s", irc->mynick, irc->mynick, irc->myhost, - irc->channel, "-oo", irc->nick, irc->mynick ); + irc_write( irc, ":%s!%s@%s MODE %s %s %s", irc->mynick, irc->mynick, irc->myhost, + irc->channel, b?"+o":"-o", irc->mynick ); + return ret; +} + +char *set_eval_op_user( set_t *set, char *value ) +{ + irc_t *irc = set->data; + char *ret = set_eval_bool(set, value); + int b = bool2int(ret); + + irc_write( irc, ":%s!%s@%s MODE %s %s %s", irc->mynick, irc->mynick, irc->myhost, + irc->channel, b?"+o":"-o", irc->nick ); + return ret; +} + +/* generalized version of set_eval_op/voice_buddies */ +char *set_eval_mode_buddies( set_t *set, char *value, char modeflag ) +{ + irc_t *irc = set->data; + char op[64], deop[64]; + int nop=0, ndeop=0; + user_t *u; + int mode; + + if(!strcmp(value, "false")) + mode=0; + else if(!strcmp(value, "encrypted")) + mode=1; + else if(!strcmp(value, "trusted")) + mode=2; + else if(!strcmp(value, "notaway")) + mode=3; else return NULL; + /* sorry for calling them op/deop - too lazy for search+replace :P */ + op[0]='\0'; + deop[0]='\0'; + for(u=irc->users; u; u=u->next) { + /* we're only concerned with online buddies */ + if(!u->ic || !u->online) + continue; + + /* just in case... */ + if(strlen(u->nick) >= 64) + continue; + + /* dump out ops/deops when the corresponding name list fills up */ + if(strlen(op)+strlen(u->nick)+2 > 64) { + char *flags = g_strnfill(nop, modeflag); + irc_write( irc, ":%s!%s@%s MODE %s +%s%s", irc->mynick, irc->mynick, irc->myhost, + irc->channel, flags, op ); + op[0]='\0'; + g_free(flags); + } + if(strlen(deop)+strlen(u->nick)+2 > 64) { + char *flags = g_strnfill(ndeop, modeflag); + irc_write( irc, ":%s!%s@%s MODE %s -%s%s", irc->mynick, irc->mynick, irc->myhost, + irc->channel, flags, deop ); + deop[0]='\0'; + g_free(flags); + } + + switch(mode) { + /* "false" */ + case 0: + g_strlcat(deop, " ", 64); + g_strlcat(deop, u->nick, 64); + ndeop++; + break; + /* "encrypted" */ + case 1: + if(u->encrypted) { + g_strlcat(op, " ", 64); + g_strlcat(op, u->nick, 64); + nop++; + } else { + g_strlcat(deop, " ", 64); + g_strlcat(deop, u->nick, 64); + ndeop++; + } + break; + /* "trusted" */ + case 2: + if(u->encrypted > 1) { + g_strlcat(op, " ", 64); + g_strlcat(op, u->nick, 64); + nop++; + } else { + g_strlcat(deop, " ", 64); + g_strlcat(deop, u->nick, 64); + ndeop++; + } + break; + /* "notaway" */ + case 3: + if(u->away) { + g_strlcat(deop, " ", 64); + g_strlcat(deop, u->nick, 64); + ndeop++; + } else { + g_strlcat(op, " ", 64); + g_strlcat(op, u->nick, 64); + nop++; + } + } + } + /* dump anything left in op/deop lists */ + if(*op) { + char *flags = g_strnfill(nop, modeflag); + irc_write( irc, ":%s!%s@%s MODE %s +%s%s", irc->mynick, irc->mynick, irc->myhost, + irc->channel, flags, op ); + g_free(flags); + } + if(*deop) { + char *flags = g_strnfill(ndeop, modeflag); + irc_write( irc, ":%s!%s@%s MODE %s -%s%s", irc->mynick, irc->mynick, irc->myhost, + irc->channel, flags, deop ); + g_free(flags); + } + return value; } +char *set_eval_op_buddies( set_t *set, char *value ) +{ + return set_eval_mode_buddies(set, value, 'o'); +} + +char *set_eval_voice_buddies( set_t *set, char *value ) +{ + return set_eval_mode_buddies(set, value, 'v'); +} + char *set_eval_charset( set_t *set, char *value ) { GIConv cd; diff --git a/set.h b/set.h index 7dcbb869..b572cc2b 100644 --- a/set.h +++ b/set.h @@ -95,7 +95,10 @@ char *set_eval_bool( set_t *set, char *value ); /* Some not very generic evaluators that really shouldn't be here... */ char *set_eval_to_char( set_t *set, char *value ); -char *set_eval_ops( set_t *set, char *value ); +char *set_eval_op_root( set_t *set, char *value ); +char *set_eval_op_user( set_t *set, char *value ); +char *set_eval_op_buddies( set_t *set, char *value ); +char *set_eval_voice_buddies( set_t *set, char *value ); char *set_eval_charset( set_t *set, char *value ); #endif /* __SET_H__ */ diff --git a/user.h b/user.h index 9d8a41a0..c2d6869d 100644 --- a/user.h +++ b/user.h @@ -36,6 +36,7 @@ typedef struct __USER char is_private; char online; + char encrypted; char *handle; char *group; -- cgit v1.2.3 From f55cfe93aeae5c24fd067e28733826d7e3029085 Mon Sep 17 00:00:00 2001 From: Sven Moritz Hallberg Date: Sat, 9 Feb 2008 23:24:39 +0100 Subject: remove old away_devoice stuff --- protocols/nogaim.c | 64 ------------------------------------------------------ protocols/nogaim.h | 1 - 2 files changed, 65 deletions(-) diff --git a/protocols/nogaim.c b/protocols/nogaim.c index 138e2ce1..542a4269 100644 --- a/protocols/nogaim.c +++ b/protocols/nogaim.c @@ -926,70 +926,6 @@ static int remove_chat_buddy_silent( struct groupchat *b, const char *handle ) } -/* Misc. BitlBee stuff which shouldn't really be here */ - -char *set_eval_away_devoice( set_t *set, char *value ) -{ - irc_t *irc = set->data; - int st; - - if( ( g_strcasecmp( value, "true" ) == 0 ) || ( g_strcasecmp( value, "yes" ) == 0 ) || ( g_strcasecmp( value, "on" ) == 0 ) ) - st = 1; - else if( ( g_strcasecmp( value, "false" ) == 0 ) || ( g_strcasecmp( value, "no" ) == 0 ) || ( g_strcasecmp( value, "off" ) == 0 ) ) - st = 0; - else if( sscanf( value, "%d", &st ) != 1 ) - return( NULL ); - - st = st != 0; - - /* Horror.... */ - - if( st != set_getbool( &irc->set, "away_devoice" ) ) - { - char list[80] = ""; - user_t *u = irc->users; - int i = 0, count = 0; - char pm; - char v[80]; - - if( st ) - pm = '+'; - else - pm = '-'; - - while( u ) - { - if( u->ic && u->online && !u->away ) - { - if( ( strlen( list ) + strlen( u->nick ) ) >= 79 ) - { - for( i = 0; i < count; v[i++] = 'v' ); v[i] = 0; - irc_write( irc, ":%s MODE %s %c%s%s", - irc->myhost, - irc->channel, pm, v, list ); - - *list = 0; - count = 0; - } - - sprintf( list + strlen( list ), " %s", u->nick ); - count ++; - } - u = u->next; - } - - /* $v = 'v' x $i */ - for( i = 0; i < count; v[i++] = 'v' ); v[i] = 0; - irc_write( irc, ":%s MODE %s %c%s%s", irc->myhost, - irc->channel, pm, v, list ); - } - - return( set_eval_bool( set, value ) ); -} - - - - /* The plan is to not allow straight calls to prpl functions anymore, but do them all from some wrappers. We'll start to define some down here: */ diff --git a/protocols/nogaim.h b/protocols/nogaim.h index 3eb640e2..3caefe2b 100644 --- a/protocols/nogaim.h +++ b/protocols/nogaim.h @@ -320,7 +320,6 @@ void imc_add_block( struct im_connection *ic, char *handle ); void imc_rem_block( struct im_connection *ic, char *handle ); /* Misc. stuff */ -char *set_eval_away_devoice( set_t *set, char *value ); gboolean auto_reconnect( gpointer data, gint fd, b_input_condition cond ); void cancel_auto_reconnect( struct account *a ); -- cgit v1.2.3 From 52e6e17d3b3610afd6d6b997257b72e798d95c47 Mon Sep 17 00:00:00 2001 From: Sven Moritz Hallberg Date: Sun, 10 Feb 2008 14:36:43 +0100 Subject: Support halfops for 'notaway' status etc. --- irc.c | 1 + otr.c | 67 +++++++++++++++++++++++++++++++++++++----------------- protocols/nogaim.c | 4 ++++ set.c | 5 ++++ set.h | 1 + 5 files changed, 57 insertions(+), 21 deletions(-) diff --git a/irc.c b/irc.c index 279401d0..6205c631 100644 --- a/irc.c +++ b/irc.c @@ -112,6 +112,7 @@ irc_t *irc_new( int fd ) set_add( &irc->set, "default_target", "root", NULL, irc ); set_add( &irc->set, "display_namechanges", "false", set_eval_bool, irc ); set_add( &irc->set, "handle_unknown", "root", NULL, irc ); + set_add( &irc->set, "halfop_buddies", "false", set_eval_halfop_buddies, irc ); set_add( &irc->set, "lcnicks", "true", set_eval_bool, irc ); set_add( &irc->set, "op_buddies", "false", set_eval_op_buddies, irc ); set_add( &irc->set, "op_root", "true", set_eval_op_root, irc ); diff --git a/otr.c b/otr.c index 60581ee6..ccb65887 100644 --- a/otr.c +++ b/otr.c @@ -981,31 +981,56 @@ const char *peernick(irc_t *irc, const char *handle, const char *protocol) int otr_update_modeflags(irc_t *irc, user_t *u) { - char *vo = set_getstr(&irc->set, "voice_buddies"); - char *oo = set_getstr(&irc->set, "op_buddies"); - char eflag=0, tflag=0; - int e = u->encrypted; - int t = (u->encrypted > 1); + char *vb = set_getstr(&irc->set, "voice_buddies"); + char *hb = set_getstr(&irc->set, "halfop_buddies"); + char *ob = set_getstr(&irc->set, "op_buddies"); + int encrypted = u->encrypted; + int trusted = u->encrypted > 1; + char flags[7]; + int nflags; + char *p = flags; + int i; - if(!strcmp(vo, "encrypted")) - eflag='v'; - else if(!strcmp(oo, "encrypted")) - eflag='o'; - if(!strcmp(vo, "trusted")) - tflag='v'; - else if(!strcmp(oo, "trusted")) - tflag='o'; + if(!strcmp(vb, "encrypted")) { + *(p++) = encrypted ? '+' : '-'; + *(p++) = 'v'; + nflags++; + } else if(!strcmp(vb, "trusted")) { + *(p++) = trusted ? '+' : '-'; + *(p++) = 'v'; + nflags++; + } + if(!strcmp(hb, "encrypted")) { + *(p++) = encrypted ? '+' : '-'; + *(p++) = 'h'; + nflags++; + } else if(!strcmp(hb, "trusted")) { + *(p++) = trusted ? '+' : '-'; + *(p++) = 'h'; + nflags++; + } + if(!strcmp(ob, "encrypted")) { + *(p++) = encrypted ? '+' : '-'; + *(p++) = 'o'; + nflags++; + } else if(!strcmp(ob, "trusted")) { + *(p++) = trusted ? '+' : '-'; + *(p++) = 'o'; + nflags++; + } + *p = '\0'; - if(!eflag) + p = g_malloc(nflags * (strlen(u->nick)+1) + 1); + *p = '\0'; + if(!p) return 0; - - if(tflag) { - irc_write( irc, ":%s!%s@%s MODE %s %c%c%c%c %s %s", irc->mynick, irc->mynick, irc->myhost, - irc->channel, e?'+':'-', eflag, t?'+':'-', tflag, u->nick, u->nick ); - } else { - irc_write( irc, ":%s!%s@%s MODE %s %c%c %s", irc->mynick, irc->mynick, irc->myhost, - irc->channel, e?'+':'-', eflag, u->nick ); + for(i=0; inick); } + irc_write( irc, ":%s!%s@%s MODE %s %s%s", irc->mynick, irc->mynick, irc->myhost, + irc->channel, flags, p ); + g_free(p); return 1; } diff --git a/protocols/nogaim.c b/protocols/nogaim.c index 542a4269..c2966323 100644 --- a/protocols/nogaim.c +++ b/protocols/nogaim.c @@ -621,6 +621,10 @@ void imcb_buddy_status( struct im_connection *ic, const char *handle, int flags, irc_write( ic->irc, ":%s MODE %s %cv %s", from, ic->irc->channel, u->away?'-':'+', u->nick ); } + if(!strcmp(set_getstr(&ic->irc->set, "halfop_buddies"), "notaway")) { + irc_write( ic->irc, ":%s MODE %s %ch %s", from, ic->irc->channel, + u->away?'-':'+', u->nick ); + } if(!strcmp(set_getstr(&ic->irc->set, "op_buddies"), "notaway")) { irc_write( ic->irc, ":%s MODE %s %co %s", from, ic->irc->channel, u->away?'-':'+', u->nick ); diff --git a/set.c b/set.c index fa1933d5..0e9ae5dc 100644 --- a/set.c +++ b/set.c @@ -344,6 +344,11 @@ char *set_eval_op_buddies( set_t *set, char *value ) return set_eval_mode_buddies(set, value, 'o'); } +char *set_eval_halfop_buddies( set_t *set, char *value ) +{ + return set_eval_mode_buddies(set, value, 'h'); +} + char *set_eval_voice_buddies( set_t *set, char *value ) { return set_eval_mode_buddies(set, value, 'v'); diff --git a/set.h b/set.h index b572cc2b..3e1b4f66 100644 --- a/set.h +++ b/set.h @@ -98,6 +98,7 @@ char *set_eval_to_char( set_t *set, char *value ); char *set_eval_op_root( set_t *set, char *value ); char *set_eval_op_user( set_t *set, char *value ); char *set_eval_op_buddies( set_t *set, char *value ); +char *set_eval_halfop_buddies( set_t *set, char *value ); char *set_eval_voice_buddies( set_t *set, char *value ); char *set_eval_charset( set_t *set, char *value ); -- cgit v1.2.3 From 8521b02a157c92b9f6cddca043d30f7659ec2c25 Mon Sep 17 00:00:00 2001 From: Sven Moritz Hallberg Date: Sun, 10 Feb 2008 17:46:10 +0100 Subject: - rename request/abort commands to connect/disconnect - support 'otr info' without argument for general info - some cosmetics --- otr.c | 319 ++++++++++++++++++++++++++++++++++++++---------------------------- 1 file changed, 183 insertions(+), 136 deletions(-) diff --git a/otr.c b/otr.c index ccb65887..aaab8def 100644 --- a/otr.c +++ b/otr.c @@ -47,24 +47,22 @@ const char *op_account_name(void *opdata, const char *account, const char *proto /** otr sub-command handlers: **/ -/* TODO: void cmd_otr_keygen(irc_t *irc, char **args); */ -void cmd_otr_abort(irc_t *irc, char **args); /* TODO: does this cmd even make sense? */ -void cmd_otr_request(irc_t *irc, char **args); /* TODO: do we even need this? */ +void cmd_otr_connect(irc_t *irc, char **args); +void cmd_otr_disconnect(irc_t *irc, char **args); void cmd_otr_smp(irc_t *irc, char **args); void cmd_otr_trust(irc_t *irc, char **args); -/* TODO: void cmd_otr_affirm(irc_t *irc, char **args); */ -void cmd_otr_fprints(irc_t *irc, char **args); void cmd_otr_info(irc_t *irc, char **args); -void cmd_otr_policy(irc_t *irc, char **args); +/* void cmd_otr_forget(irc_t *irc, char **args); */ const command_t otr_commands[] = { - { "abort", 1, &cmd_otr_abort, 0 }, - { "request", 1, &cmd_otr_request, 0 }, - { "smp", 2, &cmd_otr_smp, 0 }, - { "trust", 6, &cmd_otr_trust, 0 }, - { "fprints", 0, &cmd_otr_fprints, 0 }, - { "info", 1, &cmd_otr_info, 0 }, - { "policy", 0, &cmd_otr_policy, 0 }, + { "connect", 1, &cmd_otr_connect, 0 }, + { "disconnect", 1, &cmd_otr_disconnect, 0 }, + { "smp", 2, &cmd_otr_smp, 0 }, + { "trust", 6, &cmd_otr_trust, 0 }, + { "info", 0, &cmd_otr_info, 0 }, + /* + { "forget", 1, &cmd_otr_forget, 0 }, + */ { NULL } }; @@ -100,6 +98,9 @@ struct im_connection *check_imc(void *opdata, const char *accountname, returns "handle/protocol" if not found */ const char *peernick(irc_t *irc, const char *handle, const char *protocol); +/* turn a hexadecimal digit into its numerical value */ +int hexval(char a); + /* determine the user_t for a given handle/protocol pair returns NULL if not found */ user_t *peeruser(irc_t *irc, const char *handle, const char *protocol); @@ -112,11 +113,16 @@ void otr_handle_smp(struct im_connection *ic, const char *handle, OtrlTLV *tlvs) i.e. msgstate should be announced seperately */ int otr_update_modeflags(irc_t *irc, user_t *u); +/* show general info about the OTR subsystem; called by 'otr info' */ +void show_general_otr_info(irc_t *irc); + +/* show info about a given OTR context */ +void show_otr_context_info(irc_t *irc, ConnContext *ctx); + /* show the list of fingerprints associated with a given context */ void show_fingerprints(irc_t *irc, ConnContext *ctx); - /*** routines declared in otr.h: ***/ void otr_init(void) @@ -488,8 +494,8 @@ void op_gone_secure(void *opdata, ConnContext *context) u = peeruser(ic->irc, context->username, context->protocol); if(!u) { log_message(LOGLVL_ERROR, - "BUG: otr.c: op_gone_secure: user_t for %s/%s not found!", - context->username, context->protocol); + "BUG: otr.c: op_gone_secure: user_t for %s/%s/%s not found!", + context->username, context->protocol, context->accountname); return; } if(context->active_fingerprint->trust[0]) @@ -511,8 +517,8 @@ void op_gone_insecure(void *opdata, ConnContext *context) u = peeruser(ic->irc, context->username, context->protocol); if(!u) { log_message(LOGLVL_ERROR, - "BUG: otr.c: op_gone_insecure: user_t for %s/%s not found!", - context->username, context->protocol); + "BUG: otr.c: op_gone_insecure: user_t for %s/%s/%s not found!", + context->username, context->protocol, context->accountname); return; } u->encrypted = 0; @@ -532,8 +538,8 @@ void op_still_secure(void *opdata, ConnContext *context, int is_reply) u = peeruser(ic->irc, context->username, context->protocol); if(!u) { log_message(LOGLVL_ERROR, - "BUG: otr.c: op_still_secure: user_t for %s/%s not found!", - context->username, context->protocol); + "BUG: otr.c: op_still_secure: user_t for %s/%s/%s not found!", + context->username, context->protocol, context->accountname); return; } if(context->active_fingerprint->trust[0]) @@ -573,7 +579,7 @@ const char *op_account_name(void *opdata, const char *account, const char *proto /*** OTR sub-command handlers ***/ -void cmd_otr_abort(irc_t *irc, char **args) +void cmd_otr_disconnect(irc_t *irc, char **args) { user_t *u; @@ -587,7 +593,7 @@ void cmd_otr_abort(irc_t *irc, char **args) u->ic, u->ic->acc->user, u->ic->acc->prpl->name, u->handle); } -void cmd_otr_request(irc_t *irc, char **args) +void cmd_otr_connect(irc_t *irc, char **args) { user_t *u; @@ -651,20 +657,6 @@ void cmd_otr_smp(irc_t *irc, char **args) } } -int hexval(char a) -{ - int x=tolower(a); - - if(x>='a' && x<='f') - x = x - 'a' + 10; - else if(x>='0' && x<='9') - x = x - '0'; - else - return -1; - - return x; -} - void cmd_otr_trust(irc_t *irc, char **args) { user_t *u; @@ -725,112 +717,59 @@ void cmd_otr_trust(irc_t *irc, char **args) } } -void cmd_otr_fprints(irc_t *irc, char **args) +void cmd_otr_info(irc_t *irc, char **args) { - if(args[1]) { - /* list given buddy's fingerprints */ - user_t *u; - ConnContext *ctx; - - u = user_find(irc, args[1]); - if(!u || !u->ic) { - irc_usermsg(irc, "%s: unknown user", args[1]); - return; - } - - ctx = otrl_context_find(irc->otr_us, u->handle, - u->ic->acc->user, u->ic->acc->prpl->name, 0, NULL, NULL, NULL); - if(!ctx) { - irc_usermsg(irc, "no fingerprints"); - } else { - show_fingerprints(irc, ctx); - } + if(!args[1]) { + show_general_otr_info(irc); } else { - /* list all known fingerprints */ + char *arg = g_strdup(args[1]); + char *myhandle, *handle, *protocol; ConnContext *ctx; - for(ctx=irc->otr_us->context_root; ctx; ctx=ctx->next) { - irc_usermsg(irc, "[%s]", peernick(irc, ctx->username, ctx->protocol)); - show_fingerprints(irc, ctx); - } - if(!irc->otr_us->context_root) { - irc_usermsg(irc, "no fingerprints"); + + /* interpret arg as 'user/protocol/account' if possible */ + protocol = strchr(arg, '/'); + if(protocol) { + *(protocol++) = '\0'; + myhandle = strchr(protocol, '/'); + if(!myhandle) { + /* TODO: try to find a unique account for this context */ + } } - } -} - -void cmd_otr_info(irc_t *irc, char **args) -{ - user_t *u; - ConnContext *ctx; - Fingerprint *fp; - char human[45]; - const char *offer_status; - const char *message_state; - const char *trust; - - if(!args) { - irc_usermsg(irc, "no args?!"); - return; - } - if(!args[1]) { - irc_usermsg(irc, "no args[1]?!"); - return; - } - u = user_find(irc, args[1]); - if(!u || !u->ic) { - irc_usermsg(irc, "%s: unknown user", args[1]); - return; - } - - ctx = otrl_context_find(irc->otr_us, u->handle, - u->ic->acc->user, u->ic->acc->prpl->name, 0, NULL, NULL, NULL); - if(!ctx) { - irc_usermsg(irc, "no otr context with %s", args[1]); - return; - } - - switch(ctx->otr_offer) { - case OFFER_NOT: offer_status="none sent"; break; - case OFFER_SENT: offer_status="awaiting reply"; break; - case OFFER_ACCEPTED: offer_status="accepted our offer"; break; - case OFFER_REJECTED: offer_status="ignored our offer"; break; - default: offer_status="?"; - } - - switch(ctx->msgstate) { - case OTRL_MSGSTATE_PLAINTEXT: message_state="cleartext"; break; - case OTRL_MSGSTATE_ENCRYPTED: message_state="encrypted"; break; - case OTRL_MSGSTATE_FINISHED: message_state="shut down"; break; - default: message_state="?"; - } - - irc_usermsg(irc, "%s is %s/%s; we are %s/%s to them", args[1], - ctx->username, ctx->protocol, ctx->accountname, ctx->protocol); - irc_usermsg(irc, " otr offer status: %s", offer_status); - irc_usermsg(irc, " connection state: %s", message_state); - - if(ctx->msgstate == OTRL_MSGSTATE_ENCRYPTED) { - irc_usermsg(irc, " protocol version: %d", ctx->protocol_version); - fp = ctx->active_fingerprint; - if(!fp) { - irc_usermsg(irc, " active f'print: none?"); + if(protocol && myhandle) { + *(myhandle++) = '\0'; + handle = arg; + ctx = otrl_context_find(irc->otr_us, handle, myhandle, protocol, 0, NULL, NULL, NULL); + if(!ctx) { + irc_usermsg(irc, "no such context (%s %s %s)", handle, protocol, myhandle); + g_free(arg); + return; + } } else { - otrl_privkey_hash_to_human(human, fp->fingerprint); - if(!fp->trust || fp->trust[0] == '\0') { - trust="untrusted"; - } else { - trust=fp->trust; + user_t *u = user_find(irc, args[1]); + if(!u || !u->ic) { + irc_usermsg(irc, "%s: unknown user", args[1]); + g_free(arg); + return; + } + ctx = otrl_context_find(irc->otr_us, u->handle, u->ic->acc->user, + u->ic->acc->prpl->name, 0, NULL, NULL, NULL); + if(!ctx) { + irc_usermsg(irc, "no otr context with %s", args[1]); + g_free(arg); + return; } - irc_usermsg(irc, " active f'print: %s (%s)", human, trust); } + + /* show how we resolved the (nick) argument, if we did */ + if(handle!=arg) { + irc_usermsg(irc, "%s is %s/%s; we are %s/%s to them", args[1], + ctx->username, ctx->protocol, ctx->accountname, ctx->protocol); + } + show_otr_context_info(irc, ctx); + g_free(arg); } } -void cmd_otr_policy(irc_t *irc, char **args) -{ - irc_usermsg(irc, "n/a: not implemented"); -} - /*** local helpers / subroutines: ***/ @@ -952,10 +891,12 @@ user_t *peeruser(irc_t *irc, const char *handle, const char *protocol) { user_t *u; + log_message(LOGLVL_DEBUG, "peeruser '%s' '%s'", handle, protocol); + for(u=irc->users; u; u=u->next) { struct prpl *prpl; if(!u->ic || !u->handle) - break; + continue; prpl = u->ic->acc->prpl; if(strcmp(prpl->name, protocol) == 0 && prpl->handle_cmp(u->handle, handle) == 0) { @@ -966,6 +907,20 @@ user_t *peeruser(irc_t *irc, const char *handle, const char *protocol) return NULL; } +int hexval(char a) +{ + int x=tolower(a); + + if(x>='a' && x<='f') + x = x - 'a' + 10; + else if(x>='0' && x<='9') + x = x - '0'; + else + return -1; + + return x; +} + const char *peernick(irc_t *irc, const char *handle, const char *protocol) { static char fallback[512]; @@ -1053,13 +1008,105 @@ void show_fingerprints(irc_t *irc, ConnContext *ctx) trust=fp->trust; } if(fp == ctx->active_fingerprint) { - irc_usermsg(irc, "\x02%s (%s)\x02", human, trust); + irc_usermsg(irc, " \x02%s (%s)\x02", human, trust); } else { - irc_usermsg(irc, "%s (%s)", human, trust); + irc_usermsg(irc, " %s (%s)", human, trust); } } if(count==0) - irc_usermsg(irc, "no fingerprints"); + irc_usermsg(irc, " no fingerprints"); +} + +void show_general_otr_info(irc_t *irc) +{ + ConnContext *ctx; + OtrlPrivKey *key; + char human[45]; + + /* list all privkeys */ + irc_usermsg(irc, "\x1fprivate keys:\x1f"); + for(key=irc->otr_us->privkey_root; key; key=key->next) { + const char *hash; + + switch(key->pubkey_type) { + case OTRL_PUBKEY_TYPE_DSA: + irc_usermsg(irc, " %s/%s - DSA", key->accountname, key->protocol); + break; + default: + irc_usermsg(irc, " %s/%s - type %d", key->accountname, key->protocol, + key->pubkey_type); + } + + /* No, it doesn't make much sense to search for the privkey again by + account/protocol, but libotr currently doesn't provide a direct routine + for hashing a given 'OtrlPrivKey'... */ + hash = otrl_privkey_fingerprint(irc->otr_us, human, key->accountname, key->protocol); + if(hash) /* should always succeed */ + irc_usermsg(irc, " %s", human); + } + + /* list all contexts */ + irc_usermsg(irc, "%s", ""); + irc_usermsg(irc, "\x1f" "connection contexts:\x1f (bold=currently encrypted)"); + for(ctx=irc->otr_us->context_root; ctx; ctx=ctx->next) {\ + user_t *u; + char *userstring; + + u = peeruser(irc, ctx->username, ctx->protocol); + if(u) + userstring = g_strdup_printf("%s/%s/%s (%s)", + ctx->username, ctx->protocol, ctx->accountname, u->nick); + else + userstring = g_strdup_printf("%s/%s/%s", + ctx->username, ctx->protocol, ctx->accountname); + + if(ctx->msgstate == OTRL_MSGSTATE_ENCRYPTED) { + otrl_privkey_hash_to_human(human, ctx->active_fingerprint->fingerprint); + irc_usermsg(irc, " \x02%s\x02", userstring); + irc_usermsg(irc, " %s", human); + } else { + irc_usermsg(irc, " %s", userstring); + } + + g_free(userstring); + } +} + +void show_otr_context_info(irc_t *irc, ConnContext *ctx) +{ + switch(ctx->otr_offer) { + case OFFER_NOT: + irc_usermsg(irc, " otr offer status: none sent"); + break; + case OFFER_SENT: + irc_usermsg(irc, " otr offer status: awaiting reply"); + break; + case OFFER_ACCEPTED: + irc_usermsg(irc, " otr offer status: accepted our offer"); + break; + case OFFER_REJECTED: + irc_usermsg(irc, " otr offer status: ignored our offer"); + break; + default: + irc_usermsg(irc, " otr offer status: %d", ctx->otr_offer); + } + + switch(ctx->msgstate) { + case OTRL_MSGSTATE_PLAINTEXT: + irc_usermsg(irc, " connection state: cleartext"); + break; + case OTRL_MSGSTATE_ENCRYPTED: + irc_usermsg(irc, " connection state: encrypted (v%d)", ctx->protocol_version); + break; + case OTRL_MSGSTATE_FINISHED: + irc_usermsg(irc, " connection state: shut down"); + break; + default: + irc_usermsg(irc, " connection state: %d", ctx->msgstate); + } + + irc_usermsg(irc, " known fingerprints: (bold=active)"); + show_fingerprints(irc, ctx); } void otr_keygen(irc_t *irc, const char *handle, const char *protocol) -- cgit v1.2.3 From 94e7eb3aaefc753ea81ea8c9e38570407d4c9790 Mon Sep 17 00:00:00 2001 From: Sven Moritz Hallberg Date: Sun, 10 Feb 2008 18:56:59 +0100 Subject: add 'otr keygen' command --- otr.c | 35 +++++++++++++++++++++++++++++------ query.c | 10 ++++++++-- 2 files changed, 37 insertions(+), 8 deletions(-) diff --git a/otr.c b/otr.c index aaab8def..6320b3fc 100644 --- a/otr.c +++ b/otr.c @@ -52,6 +52,7 @@ void cmd_otr_disconnect(irc_t *irc, char **args); void cmd_otr_smp(irc_t *irc, char **args); void cmd_otr_trust(irc_t *irc, char **args); void cmd_otr_info(irc_t *irc, char **args); +void cmd_otr_keygen(irc_t *irc, char **args); /* void cmd_otr_forget(irc_t *irc, char **args); */ const command_t otr_commands[] = { @@ -60,6 +61,7 @@ const command_t otr_commands[] = { { "smp", 2, &cmd_otr_smp, 0 }, { "trust", 6, &cmd_otr_trust, 0 }, { "info", 0, &cmd_otr_info, 0 }, + { "keygen", 1, &cmd_otr_keygen, 0 }, /* { "forget", 1, &cmd_otr_forget, 0 }, */ @@ -770,6 +772,32 @@ void cmd_otr_info(irc_t *irc, char **args) } } +void cmd_otr_keygen(irc_t *irc, char **args) +{ + int i, n; + account_t *a; + + n = atoi(args[1]); + if(n<0 || (!n && strcmp(args[1], "0"))) { + irc_usermsg(irc, "%s: invalid account number", args[1]); + return; + } + + a = irc->accounts; + for(i=0; inext); + if(!a) { + irc_usermsg(irc, "%s: no such account", args[1]); + return; + } + + if(otrl_privkey_find(irc->otr_us, a->user, a->prpl->name)) { + char *s = g_strdup_printf("account %d already has a key, replace it?", n); + query_add(irc, a->ic, s, yes_keygen, no_keygen, a); + } else { + otr_keygen(irc, a->user, a->prpl->name); + } +} + /*** local helpers / subroutines: ***/ @@ -1219,13 +1247,8 @@ void no_keygen(gpointer w, void *data) { account_t *acc = (account_t *)data; - irc_usermsg(acc->irc, "proceeding without key, otr inoperable on %s/%s", + irc_usermsg(acc->irc, "keygen cancelled for %s/%s", acc->user, acc->prpl->name); - /* TODO: - irc_usermsg(acc->irc, "setting otr policy for %s/%s to \"never\"", - acc->user, acc->prpl->name); - set_setstr(acc->set, "otr_policy", "never"); - */ } diff --git a/query.c b/query.c index 99be2bee..3ae1ce31 100644 --- a/query.c +++ b/query.c @@ -139,12 +139,18 @@ void query_answer( irc_t *irc, query_t *q, int ans ) } if( ans ) { - imcb_log( q->ic, "Accepted: %s", q->question ); + if(q->ic) + imcb_log( q->ic, "Accepted: %s", q->question ); + else + irc_usermsg( irc, "Accepted: %s", q->question ); q->yes( NULL, q->data ); } else { - imcb_log( q->ic, "Rejected: %s", q->question ); + if(q->ic) + imcb_log( q->ic, "Rejected: %s", q->question ); + else + irc_usermsg( irc, "Rejected: %s", q->question ); q->no( NULL, q->data ); } q->data = NULL; -- cgit v1.2.3 From 5d6204020ac3439388666df54600ad65a384046b Mon Sep 17 00:00:00 2001 From: Sven Moritz Hallberg Date: Sun, 10 Feb 2008 22:12:13 +0100 Subject: remove cleartext fallbacks during keygen --- otr.c | 22 ++-------------------- 1 file changed, 2 insertions(+), 20 deletions(-) diff --git a/otr.c b/otr.c index 6320b3fc..0f441e3d 100644 --- a/otr.c +++ b/otr.c @@ -256,15 +256,7 @@ char *otr_handle_message(struct im_connection *ic, const char *handle, const cha char *colormsg; if(!g_mutex_trylock(ic->irc->otr_mutex)) { - user_t *u = user_findhandle(ic, handle); - - /* fallback for non-otr clients */ - if(u && !u->encrypted) { - return g_strdup(msg); - } - - /* TODO: queue msgs received during keygen for later? */ - irc_usermsg(ic->irc, "otr msg from %s during keygen - dropped", + irc_usermsg(ic->irc, "otr keygen in progress - msg from %s dropped", peernick(ic->irc, handle, ic->acc->prpl->name)); return NULL; } @@ -312,17 +304,7 @@ int otr_send_message(struct im_connection *ic, const char *handle, const char *m ConnContext *ctx = NULL; if(!g_mutex_trylock(ic->irc->otr_mutex)) { - user_t *u = user_findhandle(ic, handle); - - /* Fallback for non-otr clients. - Yes, we must be very sure this doesn't send stuff in the clear where it - shouldn't... */ - if(u && !u->encrypted) { - return ic->acc->prpl->buddy_msg(ic, (char *)handle, (char *)msg, flags); - } - - /* otherwise refuse to send */ - irc_usermsg(ic->irc, "otr msg to %s not sent during keygen", + irc_usermsg(ic->irc, "otr keygen in progress - msg to %s not sent", peernick(ic->irc, handle, ic->acc->prpl->name)); return 1; } -- cgit v1.2.3 From 5bf5edfde81640c1d918a2a1a1bd8bcd450805e1 Mon Sep 17 00:00:00 2001 From: Sven Moritz Hallberg Date: Sun, 10 Feb 2008 22:54:28 +0100 Subject: log out all accounts when going into keygen --- otr.c | 14 +++++++++++--- root_commands.c | 4 ++-- 2 files changed, 13 insertions(+), 5 deletions(-) diff --git a/otr.c b/otr.c index 0f441e3d..66cdb645 100644 --- a/otr.c +++ b/otr.c @@ -124,6 +124,9 @@ void show_otr_context_info(irc_t *irc, ConnContext *ctx); /* show the list of fingerprints associated with a given context */ void show_fingerprints(irc_t *irc, ConnContext *ctx); +/* to log out accounts during keygen */ +extern void cmd_account(irc_t *irc, char **cmd); + /*** routines declared in otr.h: ***/ @@ -1121,14 +1124,12 @@ void show_otr_context_info(irc_t *irc, ConnContext *ctx) void otr_keygen(irc_t *irc, const char *handle, const char *protocol) { + char *account_off[] = {"account", "off", NULL}; GError *err; GThread *thr; struct kgdata *kg; gint ev; - irc_usermsg(irc, "generating new otr privkey for %s/%s...", - handle, protocol); - kg = g_new0(struct kgdata, 1); if(!kg) { irc_usermsg(irc, "otr keygen failed: out of memory"); @@ -1166,6 +1167,13 @@ void otr_keygen(irc_t *irc, const char *handle, const char *protocol) return; } + /* tell the user what's happening, go comatose, and start the keygen */ + irc_usermsg(irc, "going comatose for otr key generation, this will take a moment"); + irc_usermsg(irc, "all accounts logging out, user commands disabled"); + cmd_account(irc, account_off); + irc_usermsg(irc, "generating new otr privkey for %s/%s...", + handle, protocol); + thr = g_thread_create(&otr_keygen_thread_func, kg, FALSE, &err); if(!thr) { irc_usermsg(irc, "otr keygen failed: %s", err->message); diff --git a/root_commands.c b/root_commands.c index 61bb5da9..7352b638 100644 --- a/root_commands.c +++ b/root_commands.c @@ -135,7 +135,7 @@ static void cmd_help( irc_t *irc, char **cmd ) } } -static void cmd_account( irc_t *irc, char **cmd ); +void cmd_account( irc_t *irc, char **cmd ); static void cmd_identify( irc_t *irc, char **cmd ) { @@ -212,7 +212,7 @@ static void cmd_drop( irc_t *irc, char **cmd ) } } -static void cmd_account( irc_t *irc, char **cmd ) +void cmd_account( irc_t *irc, char **cmd ) { account_t *a; -- cgit v1.2.3 From 2a2db6f4876e16309ec3238b4c118db778778781 Mon Sep 17 00:00:00 2001 From: Sven Moritz Hallberg Date: Mon, 11 Feb 2008 15:33:53 +0100 Subject: announce that we support halfops --- irc.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/irc.c b/irc.c index 6205c631..291b0f5b 100644 --- a/irc.c +++ b/irc.c @@ -733,7 +733,7 @@ void irc_login( irc_t *irc ) irc_reply( irc, 2, ":Host %s is running BitlBee " BITLBEE_VERSION " " ARCH "/" CPU ".", irc->myhost ); irc_reply( irc, 3, ":%s", IRCD_INFO ); irc_reply( irc, 4, "%s %s %s %s", irc->myhost, BITLBEE_VERSION, UMODES UMODES_PRIV, CMODES ); - irc_reply( irc, 5, "PREFIX=(ov)@+ CHANTYPES=#& CHANMODES=,,,%s NICKLEN=%d NETWORK=BitlBee CASEMAPPING=rfc1459 MAXTARGETS=1 WATCH=128 :are supported by this server", CMODES, MAX_NICK_LENGTH - 1 ); + irc_reply( irc, 5, "PREFIX=(ohv)@%%+ CHANTYPES=#& CHANMODES=,,,%s NICKLEN=%d NETWORK=BitlBee CASEMAPPING=rfc1459 MAXTARGETS=1 WATCH=128 :are supported by this server", CMODES, MAX_NICK_LENGTH - 1 ); irc_motd( irc ); irc->umode[0] = '\0'; irc_umode_set( irc, "+" UMODE, 1 ); -- cgit v1.2.3 From 8c2b1c32386b9cbb06e1e6f03b3f2926e7761461 Mon Sep 17 00:00:00 2001 From: Sven Moritz Hallberg Date: Mon, 11 Feb 2008 15:36:19 +0100 Subject: honor simulate_netsplit for encrypted/trusted mode changes --- otr.c | 9 +++++++-- 1 file changed, 7 insertions(+), 2 deletions(-) diff --git a/otr.c b/otr.c index 66cdb645..eeef55bb 100644 --- a/otr.c +++ b/otr.c @@ -957,6 +957,7 @@ int otr_update_modeflags(irc_t *irc, user_t *u) char flags[7]; int nflags; char *p = flags; + char *from; int i; if(!strcmp(vb, "encrypted")) { @@ -996,8 +997,12 @@ int otr_update_modeflags(irc_t *irc, user_t *u) strcat(p, " "); strcat(p, u->nick); } - irc_write( irc, ":%s!%s@%s MODE %s %s%s", irc->mynick, irc->mynick, irc->myhost, - irc->channel, flags, p ); + if(set_getbool(&irc->set, "simulate_netsplit")) + from = g_strdup(irc->myhost); + else + from = g_strdup_printf("%s!%s@%s", irc->mynick, irc->mynick, irc->myhost); + irc_write(irc, ":%s MODE %s %s%s", from, irc->channel, flags, p); + g_free(from); g_free(p); return 1; -- cgit v1.2.3 From 7f4016b5acc44085621f7e077883f969a08f5548 Mon Sep 17 00:00:00 2001 From: Sven Moritz Hallberg Date: Mon, 11 Feb 2008 16:14:57 +0100 Subject: display ops/halfops/voices correctly in /names --- irc.c | 46 +++++++++++++++++++++++++++++++++++----------- 1 file changed, 35 insertions(+), 11 deletions(-) diff --git a/irc.c b/irc.c index 291b0f5b..89a07710 100644 --- a/irc.c +++ b/irc.c @@ -640,13 +640,45 @@ void irc_write_all( int now, char *format, ... ) return; } +const char *user_mode_prefix( irc_t *irc, user_t *u ) +{ + static char op[] = "@"; + static char halfop[] = "%"; + static char voice[] = "+"; + static char none[] = ""; + + int or = set_getbool(&irc->set, "op_root"); + int ou = set_getbool(&irc->set, "op_user"); + char *ob = set_getstr(&irc->set, "op_buddies"); + char *hb = set_getstr(&irc->set, "halfop_buddies"); + char *vb = set_getstr(&irc->set, "voice_buddies"); + + if( (!strcmp(u->nick, irc->mynick) && or) || + (!strcmp(u->nick, irc->nick) && ou) || + (!u->away && !strcmp(ob, "notaway")) || + (u->encrypted && !strcmp(ob, "encrypted")) || + (u->encrypted>1 && !strcmp(ob, "trusted")) + ) + return op; + else if( (!u->away && !strcmp(hb, "notaway")) || + (u->encrypted && !strcmp(hb, "encrypted")) || + (u->encrypted>1 && !strcmp(hb, "trusted")) + ) + return halfop; + else if( (!u->away && !strcmp(vb, "notaway")) || + (u->encrypted && !strcmp(vb, "encrypted")) || + (u->encrypted>1 && !strcmp(vb, "trusted")) + ) + return voice; + else + return none; +} + void irc_names( irc_t *irc, char *channel ) { user_t *u; char namelist[385] = ""; struct groupchat *c = NULL; - char *oo = set_getstr(&irc->set, "op_buddies"); - char *vo = set_getstr(&irc->set, "voice_buddies"); /* RFCs say there is no error reply allowed on NAMES, so when the channel is invalid, just give an empty reply. */ @@ -661,15 +693,7 @@ void irc_names( irc_t *irc, char *channel ) *namelist = 0; } - if( u->ic && !u->away && !strcmp(vo, "notaway") ) - strcat( namelist, "+" ); - else if( ( strcmp( u->nick, irc->mynick ) == 0 && set_getbool(&irc->set, "op_root") ) || - ( strcmp( u->nick, irc->nick ) == 0 && set_getbool(&irc->set, "op_user") ) || - ( !u->away && !strcmp(oo, "notaway") ) || - ( u->encrypted>1 && !strcmp(oo, "trusted") ) || - ( u->encrypted && !strcmp(oo, "encrypted") ) ) - strcat( namelist, "@" ); - + strcat( namelist, user_mode_prefix(irc, u) ); strcat( namelist, u->nick ); strcat( namelist, " " ); } -- cgit v1.2.3 From 3b8e810996df506a5acca9ac5a46a23a4229250e Mon Sep 17 00:00:00 2001 From: Sven Moritz Hallberg Date: Mon, 11 Feb 2008 16:16:14 +0100 Subject: honor voice/op/halfop buddies in chats --- irc.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/irc.c b/irc.c index 89a07710..79270d33 100644 --- a/irc.c +++ b/irc.c @@ -707,7 +707,6 @@ void irc_names( irc_t *irc, char *channel ) sprintf( namelist, "%s%s %s%s ", set_getbool(&irc->set, "op_root") ? "@" : "", irc->mynick, set_getbool(&irc->set, "op_user") ? "@" : "", irc->nick ); - /* TODO: Honor op/voice_buddies in chats?! */ for( l = c->in_room; l; l = l->next ) if( ( u = user_findhandle( c->ic, l->data ) ) ) { if( strlen( namelist ) + strlen( u->nick ) > sizeof( namelist ) - 4 ) @@ -716,6 +715,7 @@ void irc_names( irc_t *irc, char *channel ) *namelist = 0; } + strcat( namelist, user_mode_prefix(irc, u) ); strcat( namelist, u->nick ); strcat( namelist, " " ); } -- cgit v1.2.3 From e6cb1101cbbf6a4c2751f20c2072c9ca3127ad98 Mon Sep 17 00:00:00 2001 From: Sven Moritz Hallberg Date: Mon, 11 Feb 2008 21:31:27 +0100 Subject: allow NULL for yes/no handlers --- query.c | 6 ++++-- 1 file changed, 4 insertions(+), 2 deletions(-) diff --git a/query.c b/query.c index 3ae1ce31..99a579bc 100644 --- a/query.c +++ b/query.c @@ -143,7 +143,8 @@ void query_answer( irc_t *irc, query_t *q, int ans ) imcb_log( q->ic, "Accepted: %s", q->question ); else irc_usermsg( irc, "Accepted: %s", q->question ); - q->yes( NULL, q->data ); + if(q->yes) + q->yes( NULL, q->data ); } else { @@ -151,7 +152,8 @@ void query_answer( irc_t *irc, query_t *q, int ans ) imcb_log( q->ic, "Rejected: %s", q->question ); else irc_usermsg( irc, "Rejected: %s", q->question ); - q->no( NULL, q->data ); + if(q->no) + q->no( NULL, q->data ); } q->data = NULL; -- cgit v1.2.3 From d6fdf44c54ac2aecfd0c94a2d56be04e44a51eb3 Mon Sep 17 00:00:00 2001 From: Sven Moritz Hallberg Date: Mon, 11 Feb 2008 21:58:33 +0100 Subject: pass the im_connection as first argument to yes/no handlers --- query.c | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/query.c b/query.c index 99a579bc..49865c86 100644 --- a/query.c +++ b/query.c @@ -144,7 +144,7 @@ void query_answer( irc_t *irc, query_t *q, int ans ) else irc_usermsg( irc, "Accepted: %s", q->question ); if(q->yes) - q->yes( NULL, q->data ); + q->yes( q->ic, q->data ); } else { @@ -153,7 +153,7 @@ void query_answer( irc_t *irc, query_t *q, int ans ) else irc_usermsg( irc, "Rejected: %s", q->question ); if(q->no) - q->no( NULL, q->data ); + q->no( q->ic, q->data ); } q->data = NULL; -- cgit v1.2.3 From c59530844d25ae814141cf56f0fa810968e8df55 Mon Sep 17 00:00:00 2001 From: Sven Moritz Hallberg Date: Mon, 11 Feb 2008 22:20:35 +0100 Subject: - use a recursive otr_mutex - implement 'otr forget fingerprint' and 'otr forget context' commands --- irc.c | 4 +- irc.h | 2 +- otr.c | 261 +++++++++++++++++++++++++++++++++++++++++++++++--------- root_commands.c | 8 +- 4 files changed, 230 insertions(+), 45 deletions(-) diff --git a/irc.c b/irc.c index 79270d33..dd44876f 100644 --- a/irc.c +++ b/irc.c @@ -130,7 +130,7 @@ irc_t *irc_new( int fd ) conf_loaddefaults( irc ); irc->otr_us = otrl_userstate_create(); - irc->otr_mutex = g_mutex_new(); + g_static_rec_mutex_init(&irc->otr_mutex); return( irc ); } @@ -283,7 +283,7 @@ void irc_free(irc_t * irc) } otrl_userstate_free(irc->otr_us); - g_mutex_free(irc->otr_mutex); + g_static_rec_mutex_free(&irc->otr_mutex); g_free(irc); diff --git a/irc.h b/irc.h index 77738640..8c993e13 100644 --- a/irc.h +++ b/irc.h @@ -98,7 +98,7 @@ typedef struct irc gint ping_source_id; OtrlUserState otr_us; - GMutex *otr_mutex; /* for locking otr during keygen */ + GStaticRecMutex otr_mutex; /* for locking otr during keygen */ } irc_t; #include "user.h" diff --git a/otr.c b/otr.c index eeef55bb..95c5289e 100644 --- a/otr.c +++ b/otr.c @@ -53,7 +53,7 @@ void cmd_otr_smp(irc_t *irc, char **args); void cmd_otr_trust(irc_t *irc, char **args); void cmd_otr_info(irc_t *irc, char **args); void cmd_otr_keygen(irc_t *irc, char **args); -/* void cmd_otr_forget(irc_t *irc, char **args); */ +void cmd_otr_forget(irc_t *irc, char **args); const command_t otr_commands[] = { { "connect", 1, &cmd_otr_connect, 0 }, @@ -62,9 +62,7 @@ const command_t otr_commands[] = { { "trust", 6, &cmd_otr_trust, 0 }, { "info", 0, &cmd_otr_info, 0 }, { "keygen", 1, &cmd_otr_keygen, 0 }, - /* - { "forget", 1, &cmd_otr_forget, 0 }, - */ + { "forget", 2, &cmd_otr_forget, 0 }, { NULL } }; @@ -73,10 +71,13 @@ const command_t otr_commands[] = { /* start background thread to generate a (new) key for a given account */ void otr_keygen(irc_t *irc, const char *handle, const char *protocol); + /* keygen thread main func */ gpointer otr_keygen_thread_func(gpointer data); + /* mainloop handler for when keygen thread finishes */ gboolean keygen_finish_handler(gpointer data, gint fd, b_input_condition cond); + /* data to be passed to otr_keygen_thread_func */ struct kgdata { irc_t *irc; /* access to OTR userstate */ @@ -88,9 +89,10 @@ struct kgdata { gcry_error_t result; /* return value of otrl_privkey_generate */ }; -/* yes/no handlers for "generate key now?" */ +/* some yes/no handlers */ void yes_keygen(gpointer w, void *data); -void no_keygen(gpointer w, void *data); +void yes_forget_fingerprint(gpointer w, void *data); +void yes_forget_context(gpointer w, void *data); /* helper to make sure accountname and protocol match the incoming "opdata" */ struct im_connection *check_imc(void *opdata, const char *accountname, @@ -124,6 +126,9 @@ void show_otr_context_info(irc_t *irc, ConnContext *ctx); /* show the list of fingerprints associated with a given context */ void show_fingerprints(irc_t *irc, ConnContext *ctx); +/* find a fingerprint by prefix (given as any number of hex strings) */ +Fingerprint *match_fingerprint(irc_t *irc, ConnContext *ctx, const char **args); + /* to log out accounts during keygen */ extern void cmd_account(irc_t *irc, char **cmd); @@ -258,7 +263,7 @@ char *otr_handle_message(struct im_connection *ic, const char *handle, const cha OtrlTLV *tlvs = NULL; char *colormsg; - if(!g_mutex_trylock(ic->irc->otr_mutex)) { + if(!g_static_rec_mutex_trylock(&ic->irc->otr_mutex)) { irc_usermsg(ic->irc, "otr keygen in progress - msg from %s dropped", peernick(ic->irc, handle, ic->acc->prpl->name)); return NULL; @@ -272,11 +277,11 @@ char *otr_handle_message(struct im_connection *ic, const char *handle, const cha if(ignore_msg) { /* this was an internal OTR protocol message */ - g_mutex_unlock(ic->irc->otr_mutex); + g_static_rec_mutex_unlock(&ic->irc->otr_mutex); return NULL; } else if(!newmsg) { /* this was a non-OTR message */ - g_mutex_unlock(ic->irc->otr_mutex); + g_static_rec_mutex_unlock(&ic->irc->otr_mutex); return g_strdup(msg); } else { /* OTR has processed this message */ @@ -295,7 +300,7 @@ char *otr_handle_message(struct im_connection *ic, const char *handle, const cha colormsg = g_strdup(newmsg); } otrl_message_free(newmsg); - g_mutex_unlock(ic->irc->otr_mutex); + g_static_rec_mutex_unlock(&ic->irc->otr_mutex); return colormsg; } } @@ -306,7 +311,7 @@ int otr_send_message(struct im_connection *ic, const char *handle, const char *m char *otrmsg = NULL; ConnContext *ctx = NULL; - if(!g_mutex_trylock(ic->irc->otr_mutex)) { + if(!g_static_rec_mutex_trylock(&ic->irc->otr_mutex)) { irc_usermsg(ic->irc, "otr keygen in progress - msg to %s not sent", peernick(ic->irc, handle, ic->acc->prpl->name)); return 1; @@ -316,7 +321,7 @@ int otr_send_message(struct im_connection *ic, const char *handle, const char *m ic->acc->user, ic->acc->prpl->name, handle, msg, NULL, &otrmsg, NULL, NULL); if(st) { - g_mutex_unlock(ic->irc->otr_mutex); + g_static_rec_mutex_unlock(&ic->irc->otr_mutex); return st; } @@ -327,7 +332,7 @@ int otr_send_message(struct im_connection *ic, const char *handle, const char *m if(otrmsg) { if(!ctx) { otrl_message_free(otrmsg); - g_mutex_unlock(ic->irc->otr_mutex); + g_static_rec_mutex_unlock(&ic->irc->otr_mutex); return 1; } st = otrl_message_fragment_and_send(&global.otr_ops, ic, ctx, @@ -338,7 +343,7 @@ int otr_send_message(struct im_connection *ic, const char *handle, const char *m st = ic->acc->prpl->buddy_msg( ic, (char *)handle, (char *)msg, flags ); } - g_mutex_unlock(ic->irc->otr_mutex); + g_static_rec_mutex_unlock(&ic->irc->otr_mutex); return st; } @@ -358,7 +363,7 @@ void cmd_otr(irc_t *irc, char **args) } if(!cmd->command) { - irc_usermsg(irc, "%s %s: unknown subcommand, see \x02help otr\x02", + irc_usermsg(irc, "%s: unknown subcommand \"%s\", see \x02help otr\x02", args[0], args[1]); return; } @@ -391,7 +396,7 @@ void op_create_privkey(void *opdata, const char *accountname, s = g_strdup_printf("oops, no otr privkey for %s/%s - generate one now?", accountname, protocol); - query_add(ic->irc, ic, s, yes_keygen, no_keygen, ic->acc); + query_add(ic->irc, ic, s, yes_keygen, NULL, ic->acc); } int op_is_logged_in(void *opdata, const char *accountname, @@ -475,6 +480,7 @@ void op_gone_secure(void *opdata, ConnContext *context) struct im_connection *ic = check_imc(opdata, context->accountname, context->protocol); user_t *u; + const char *trust; log_message(LOGLVL_DEBUG, "op_gone_secure '%s' '%s' '%s'", context->accountname, context->protocol, context->username); @@ -485,7 +491,9 @@ void op_gone_secure(void *opdata, ConnContext *context) context->username, context->protocol, context->accountname); return; } - if(context->active_fingerprint->trust[0]) + + trust = context->active_fingerprint->trust; + if(trust && trust[0]) u->encrypted = 2; else u->encrypted = 1; @@ -578,6 +586,17 @@ void cmd_otr_disconnect(irc_t *irc, char **args) otrl_message_disconnect(irc->otr_us, &global.otr_ops, u->ic, u->ic->acc->user, u->ic->acc->prpl->name, u->handle); + + /* for some reason, libotr (3.1.0) doesn't do this itself: */ + if(u->encrypted) { + ConnContext *ctx; + ctx = otrl_context_find(irc->otr_us, u->handle, u->ic->acc->user, + u->ic->acc->prpl->name, 0, NULL, NULL, NULL); + if(ctx) + op_gone_insecure(u->ic, ctx); + else /* huh? */ + u->encrypted = 0; + } } void cmd_otr_connect(irc_t *irc, char **args) @@ -718,16 +737,13 @@ void cmd_otr_info(irc_t *irc, char **args) if(protocol) { *(protocol++) = '\0'; myhandle = strchr(protocol, '/'); - if(!myhandle) { - /* TODO: try to find a unique account for this context */ - } } if(protocol && myhandle) { *(myhandle++) = '\0'; handle = arg; ctx = otrl_context_find(irc->otr_us, handle, myhandle, protocol, 0, NULL, NULL, NULL); if(!ctx) { - irc_usermsg(irc, "no such context (%s %s %s)", handle, protocol, myhandle); + irc_usermsg(irc, "no such context"); g_free(arg); return; } @@ -777,12 +793,125 @@ void cmd_otr_keygen(irc_t *irc, char **args) if(otrl_privkey_find(irc->otr_us, a->user, a->prpl->name)) { char *s = g_strdup_printf("account %d already has a key, replace it?", n); - query_add(irc, a->ic, s, yes_keygen, no_keygen, a); + query_add(irc, a->ic, s, yes_keygen, NULL, a); } else { otr_keygen(irc, a->user, a->prpl->name); } } +void yes_forget_fingerprint(gpointer w, void *data) +{ + struct im_connection *ic = (struct im_connection *)w; + Fingerprint *fp = (Fingerprint *)data; + + if(fp == fp->context->active_fingerprint) { + irc_usermsg(ic->irc, "that fingerprint is active, terminate otr connection first"); + return; + } + + otrl_context_forget_fingerprint(fp, 0); +} + +void yes_forget_context(gpointer w, void *data) +{ + struct im_connection *ic = (struct im_connection *)w; + ConnContext *ctx = (ConnContext *)data; + + if(ctx->msgstate == OTRL_MSGSTATE_ENCRYPTED) { + irc_usermsg(ic->irc, "active otr connection with %s, terminate it first", + peernick(ic->irc, ctx->username, ctx->protocol)); + return; + } + + if(ctx->msgstate == OTRL_MSGSTATE_FINISHED) + otrl_context_force_plaintext(ctx); + otrl_context_forget(ctx); +} + +void cmd_otr_forget(irc_t *irc, char **args) +{ + if(!strcmp(args[1], "fingerprint")) + { + user_t *u; + ConnContext *ctx; + Fingerprint *fp; + char human[54]; + char *s; + + if(!args[3]) { + irc_usermsg(irc, "otr %s %s: not enough arguments (2 req.)", args[0], args[1]); + return; + } + + u = user_find(irc, args[2]); + if(!u || !u->ic) { + irc_usermsg(irc, "%s: unknown user", args[2]); + return; + } + + ctx = otrl_context_find(irc->otr_us, u->handle, u->ic->acc->user, + u->ic->acc->prpl->name, 0, NULL, NULL, NULL); + if(!ctx) { + irc_usermsg(irc, "no otr context with %s", args[2]); + return; + } + + fp = match_fingerprint(irc, ctx, ((const char **)args)+3); + if(!fp) { + /* match_fingerprint does error messages */ + return; + } + + if(fp == ctx->active_fingerprint) { + irc_usermsg(irc, "that fingerprint is active, terminate otr connection first"); + return; + } + + otrl_privkey_hash_to_human(human, fp->fingerprint); + s = g_strdup_printf("about to forget fingerprint %s, are you sure?", human); + query_add(irc, u->ic, s, yes_forget_fingerprint, NULL, fp); + } + + else if(!strcmp(args[1], "context")) + { + user_t *u; + ConnContext *ctx; + char *s; + + u = user_find(irc, args[2]); + if(!u || !u->ic) { + irc_usermsg(irc, "%s: unknown user", args[2]); + return; + } + + ctx = otrl_context_find(irc->otr_us, u->handle, u->ic->acc->user, + u->ic->acc->prpl->name, 0, NULL, NULL, NULL); + if(!ctx) { + irc_usermsg(irc, "no otr context with %s", args[2]); + return; + } + + if(ctx->msgstate == OTRL_MSGSTATE_ENCRYPTED) { + irc_usermsg(irc, "active otr connection with %s, terminate it first", args[2]); + return; + } + + s = g_strdup_printf("about to forget otr data about %s, are you sure?", args[2]); + query_add(irc, u->ic, s, yes_forget_context, NULL, ctx); + } + + else if(!strcmp(args[1], "key")) + { + irc_usermsg(irc, "n/a: TODO"); + } + + else + { + irc_usermsg(irc, "otr %s: unknown subcommand \"%s\", see \x02help otr forget\x02", + args[0], args[1]); + } +} + /*** local helpers / subroutines: ***/ @@ -955,7 +1084,7 @@ int otr_update_modeflags(irc_t *irc, user_t *u) int encrypted = u->encrypted; int trusted = u->encrypted > 1; char flags[7]; - int nflags; + int nflags=0; char *p = flags; char *from; int i; @@ -1035,6 +1164,72 @@ void show_fingerprints(irc_t *irc, ConnContext *ctx) irc_usermsg(irc, " no fingerprints"); } +Fingerprint *match_fingerprint(irc_t *irc, ConnContext *ctx, const char **args) +{ + Fingerprint *fp, *fp2; + char human[45]; + char prefix[45], *p; + int n; + int i,j; + + /* assemble the args into a prefix in standard "human" form */ + n=0; + p=prefix; + for(i=0; args[i]; i++) { + for(j=0; args[i][j]; j++) { + char c = toupper(args[i][j]); + + if(n>=40) { + irc_usermsg(irc, "too many fingerprint digits given, expected at most 40"); + return NULL; + } + + if( (c>='A' && c<='F') || (c>='0' && c<='9') ) { + *(p++) = c; + } else { + irc_usermsg(irc, "invalid hex digit '%c' in block %d", args[i][j], i+1); + return NULL; + } + + n++; + if(n%8 == 0) + *(p++) = ' '; + } + } + *p = '\0'; + log_message(LOGLVL_DEBUG, "match_fingerprint '%s'", prefix); + log_message(LOGLVL_DEBUG, "n=%d strlen(prefix)=%d", n, strlen(prefix)); + + /* find first fingerprint with the given prefix */ + n = strlen(prefix); + for(fp=&ctx->fingerprint_root; fp; fp=fp->next) { + if(!fp->fingerprint) + continue; + otrl_privkey_hash_to_human(human, fp->fingerprint); + if(!strncmp(prefix, human, n)) + break; + } + if(!fp) { + irc_usermsg(irc, "%s: no match", prefix); + return NULL; + } + + /* make sure the match, if any, is unique */ + for(fp2=fp->next; fp2; fp2=fp2->next) { + if(!fp2->fingerprint) + continue; + otrl_privkey_hash_to_human(human, fp2->fingerprint); + if(!strncmp(prefix, human, n)) + break; + } + if(fp2) { + irc_usermsg(irc, "%s: multiple matches", prefix); + return NULL; + } + + return fp; +} + void show_general_otr_info(irc_t *irc) { ConnContext *ctx; @@ -1079,9 +1274,7 @@ void show_general_otr_info(irc_t *irc) ctx->username, ctx->protocol, ctx->accountname); if(ctx->msgstate == OTRL_MSGSTATE_ENCRYPTED) { - otrl_privkey_hash_to_human(human, ctx->active_fingerprint->fingerprint); irc_usermsg(irc, " \x02%s\x02", userstring); - irc_usermsg(irc, " %s", human); } else { irc_usermsg(irc, " %s", userstring); } @@ -1123,7 +1316,7 @@ void show_otr_context_info(irc_t *irc, ConnContext *ctx) irc_usermsg(irc, " connection state: %d", ctx->msgstate); } - irc_usermsg(irc, " known fingerprints: (bold=active)"); + irc_usermsg(irc, " fingerprints: (bold=active)"); show_fingerprints(irc, ctx); } @@ -1158,7 +1351,7 @@ void otr_keygen(irc_t *irc, const char *handle, const char *protocol) g_free(kg); return; } - kg->done = FALSE; + kg->done = 0; /* Poll for completion of the thread periodically. I would have preferred to just wait on a pipe but this way it's portable to Windows. *sigh* @@ -1194,15 +1387,15 @@ gpointer otr_keygen_thread_func(gpointer data) struct kgdata *kg = (struct kgdata *)data; /* lock OTR subsystem and do the work */ - g_mutex_lock(kg->irc->otr_mutex); + g_static_rec_mutex_lock(&kg->irc->otr_mutex); kg->result = otrl_privkey_generate(kg->irc->otr_us, kg->keyfile, kg->handle, kg->protocol); - g_mutex_unlock(kg->irc->otr_mutex); + g_static_rec_mutex_unlock(&kg->irc->otr_mutex); /* OTR enabled again */ /* notify mainloop */ g_mutex_lock(kg->mutex); - kg->done = TRUE; + kg->done = 1; g_mutex_unlock(kg->mutex); return NULL; @@ -1238,14 +1431,6 @@ void yes_keygen(gpointer w, void *data) otr_keygen(acc->irc, acc->user, acc->prpl->name); } -void no_keygen(gpointer w, void *data) -{ - account_t *acc = (account_t *)data; - - irc_usermsg(acc->irc, "keygen cancelled for %s/%s", - acc->user, acc->prpl->name); -} - #else /* WITH_OTR undefined */ diff --git a/root_commands.c b/root_commands.c index 7352b638..03b579a1 100644 --- a/root_commands.c +++ b/root_commands.c @@ -85,7 +85,7 @@ void root_command( irc_t *irc, char *cmd[] ) if( !cmd[0] ) return; - if(!g_mutex_trylock(irc->otr_mutex)) { + if(!g_static_rec_mutex_trylock(&irc->otr_mutex)) { irc_usermsg(irc, "keygen in progress, bitlbee comatose - please wait"); return; } @@ -96,16 +96,16 @@ void root_command( irc_t *irc, char *cmd[] ) if( !cmd[commands[i].required_parameters] ) { irc_usermsg( irc, "Not enough parameters given (need %d)", commands[i].required_parameters ); - g_mutex_unlock(irc->otr_mutex); + g_static_rec_mutex_unlock(&irc->otr_mutex); return; } commands[i].execute( irc, cmd ); - g_mutex_unlock(irc->otr_mutex); + g_static_rec_mutex_unlock(&irc->otr_mutex); return; } irc_usermsg( irc, "Unknown command: %s. Please use \x02help commands\x02 to get a list of available commands.", cmd[0] ); - g_mutex_unlock(irc->otr_mutex); + g_static_rec_mutex_unlock(&irc->otr_mutex); } static void cmd_help( irc_t *irc, char **cmd ) -- cgit v1.2.3 From 8bd697c80fe304b9a63f9e87d4d4a4e3b1a6bebd Mon Sep 17 00:00:00 2001 From: Sven Moritz Hallberg Date: Mon, 11 Feb 2008 22:53:10 +0100 Subject: query_add: allow NULL for ic and pass irc to handler in that case --- query.c | 7 +++++-- 1 file changed, 5 insertions(+), 2 deletions(-) diff --git a/query.c b/query.c index 49865c86..7cfad745 100644 --- a/query.c +++ b/query.c @@ -101,6 +101,9 @@ void query_del_by_conn( irc_t *irc, struct im_connection *ic ) query_t *q, *n, *def; int count = 0; + if(!ic) + return; + q = irc->queries; def = query_default( irc ); @@ -144,7 +147,7 @@ void query_answer( irc_t *irc, query_t *q, int ans ) else irc_usermsg( irc, "Accepted: %s", q->question ); if(q->yes) - q->yes( q->ic, q->data ); + q->yes( q->ic ? q->ic : irc, q->data ); } else { @@ -153,7 +156,7 @@ void query_answer( irc_t *irc, query_t *q, int ans ) else irc_usermsg( irc, "Rejected: %s", q->question ); if(q->no) - q->no( q->ic, q->data ); + q->no( q->ic ? q->ic : irc, q->data ); } q->data = NULL; -- cgit v1.2.3 From 5f4eedefd2a444c213f65283a3d942d6287e6ea2 Mon Sep 17 00:00:00 2001 From: Sven Moritz Hallberg Date: Tue, 12 Feb 2008 00:16:23 +0100 Subject: - add nonfunctional 'otr forget key' implementation - add 'color_encrypted' setting --- irc.c | 5 +-- otr.c | 120 +++++++++++++++++++++++++++++++++++++++++++++++++++++++++------- query.c | 4 +-- 3 files changed, 113 insertions(+), 16 deletions(-) diff --git a/irc.c b/irc.c index dd44876f..aea21934 100644 --- a/irc.c +++ b/irc.c @@ -108,13 +108,14 @@ irc_t *irc_new( int fd ) set_add( &irc->set, "buddy_sendbuffer", "false", set_eval_bool, irc ); set_add( &irc->set, "buddy_sendbuffer_delay", "200", set_eval_int, irc ); set_add( &irc->set, "charset", "utf-8", set_eval_charset, irc ); + set_add( &irc->set, "color_encrypted", "true", set_eval_bool, irc ); set_add( &irc->set, "debug", "false", set_eval_bool, irc ); set_add( &irc->set, "default_target", "root", NULL, irc ); set_add( &irc->set, "display_namechanges", "false", set_eval_bool, irc ); set_add( &irc->set, "handle_unknown", "root", NULL, irc ); - set_add( &irc->set, "halfop_buddies", "false", set_eval_halfop_buddies, irc ); + set_add( &irc->set, "halfop_buddies", "encrypted", set_eval_halfop_buddies, irc ); set_add( &irc->set, "lcnicks", "true", set_eval_bool, irc ); - set_add( &irc->set, "op_buddies", "false", set_eval_op_buddies, irc ); + set_add( &irc->set, "op_buddies", "trusted", set_eval_op_buddies, irc ); set_add( &irc->set, "op_root", "true", set_eval_op_root, irc ); set_add( &irc->set, "op_user", "true", set_eval_op_user, irc ); set_add( &irc->set, "password", NULL, passchange, irc ); diff --git a/otr.c b/otr.c index 95c5289e..adba7b68 100644 --- a/otr.c +++ b/otr.c @@ -93,6 +93,7 @@ struct kgdata { void yes_keygen(gpointer w, void *data); void yes_forget_fingerprint(gpointer w, void *data); void yes_forget_context(gpointer w, void *data); +void yes_forget_key(gpointer w, void *data); /* helper to make sure accountname and protocol match the incoming "opdata" */ struct im_connection *check_imc(void *opdata, const char *accountname, @@ -129,6 +130,9 @@ void show_fingerprints(irc_t *irc, ConnContext *ctx); /* find a fingerprint by prefix (given as any number of hex strings) */ Fingerprint *match_fingerprint(irc_t *irc, ConnContext *ctx, const char **args); +/* find a private key by fingerprint prefix (given as any number of hex strings) */ +OtrlPrivKey *match_privkey(irc_t *irc, const char **args); + /* to log out accounts during keygen */ extern void cmd_account(irc_t *irc, char **cmd); @@ -287,7 +291,8 @@ char *otr_handle_message(struct im_connection *ic, const char *handle, const cha /* OTR has processed this message */ ConnContext *context = otrl_context_find(ic->irc->otr_us, handle, ic->acc->user, ic->acc->prpl->name, 0, NULL, NULL, NULL); - if(context && context->msgstate == OTRL_MSGSTATE_ENCRYPTED) { + if(context && context->msgstate == OTRL_MSGSTATE_ENCRYPTED && + set_getbool(&ic->irc->set, "color_encrypted")) { /* color according to f'print trust */ char color; const char *trust = context->active_fingerprint->trust; @@ -394,8 +399,8 @@ void op_create_privkey(void *opdata, const char *accountname, log_message(LOGLVL_DEBUG, "op_create_privkey '%s' '%s'", accountname, protocol); - s = g_strdup_printf("oops, no otr privkey for %s/%s - generate one now?", - accountname, protocol); + s = g_strdup_printf("oops, no otr privkey for %s - generate one now?", + accountname); query_add(ic->irc, ic, s, yes_keygen, NULL, ic->acc); } @@ -793,7 +798,7 @@ void cmd_otr_keygen(irc_t *irc, char **args) if(otrl_privkey_find(irc->otr_us, a->user, a->prpl->name)) { char *s = g_strdup_printf("account %d already has a key, replace it?", n); - query_add(irc, a->ic, s, yes_keygen, NULL, a); + query_add(irc, NULL, s, yes_keygen, NULL, a); } else { otr_keygen(irc, a->user, a->prpl->name); } @@ -801,11 +806,11 @@ void cmd_otr_keygen(irc_t *irc, char **args) void yes_forget_fingerprint(gpointer w, void *data) { - struct im_connection *ic = (struct im_connection *)w; + irc_t *irc = (irc_t *)w; Fingerprint *fp = (Fingerprint *)data; if(fp == fp->context->active_fingerprint) { - irc_usermsg(ic->irc, "that fingerprint is active, terminate otr connection first"); + irc_usermsg(irc, "that fingerprint is active, terminate otr connection first"); return; } @@ -814,12 +819,12 @@ void yes_forget_fingerprint(gpointer w, void *data) void yes_forget_context(gpointer w, void *data) { - struct im_connection *ic = (struct im_connection *)w; + irc_t *irc = (irc_t *)w; ConnContext *ctx = (ConnContext *)data; if(ctx->msgstate == OTRL_MSGSTATE_ENCRYPTED) { - irc_usermsg(ic->irc, "active otr connection with %s, terminate it first", - peernick(ic->irc, ctx->username, ctx->protocol)); + irc_usermsg(irc, "active otr connection with %s, terminate it first", + peernick(irc, ctx->username, ctx->protocol)); return; } @@ -828,6 +833,15 @@ void yes_forget_context(gpointer w, void *data) otrl_context_forget(ctx); } +void yes_forget_key(gpointer w, void *data) +{ + OtrlPrivKey *key = (OtrlPrivKey *)data; + + /* FIXME: For some reason which /completely eludes me/, this call keeps + barfing on the gcry_sexp_release inside (invalid pointer free). */ + otrl_privkey_forget(key); +} + void cmd_otr_forget(irc_t *irc, char **args) { if(!strcmp(args[1], "fingerprint")) @@ -869,7 +883,7 @@ void cmd_otr_forget(irc_t *irc, char **args) otrl_privkey_hash_to_human(human, fp->fingerprint); s = g_strdup_printf("about to forget fingerprint %s, are you sure?", human); - query_add(irc, u->ic, s, yes_forget_fingerprint, NULL, fp); + query_add(irc, NULL, s, yes_forget_fingerprint, NULL, fp); } else if(!strcmp(args[1], "context")) @@ -897,12 +911,28 @@ void cmd_otr_forget(irc_t *irc, char **args) } s = g_strdup_printf("about to forget otr data about %s, are you sure?", args[2]); - query_add(irc, u->ic, s, yes_forget_context, NULL, ctx); + query_add(irc, NULL, s, yes_forget_context, NULL, ctx); } else if(!strcmp(args[1], "key")) { - irc_usermsg(irc, "n/a: TODO"); + OtrlPrivKey *key; + char *s; + + key = match_privkey(irc, ((const char **)args)+2); + if(!key) { + /* match_privkey does error messages */ + return; + } + + /* TODO: Find out why 'otr forget key' barfs (cf. yes_forget_key) */ + irc_usermsg(irc, "otr %s %s: not implemented, please edit \x02%s%s.otr_keys\x02 manually :-/", + args[0], args[1], global.conf->configdir, irc->nick); + return; + + s = g_strdup_printf("about to forget the private key for %s/%s, are you sure?", + key->accountname, key->protocol); + query_add(irc, NULL, s, yes_forget_key, NULL, key); } else @@ -1230,6 +1260,72 @@ Fingerprint *match_fingerprint(irc_t *irc, ConnContext *ctx, const char **args) return fp; } +OtrlPrivKey *match_privkey(irc_t *irc, const char **args) +{ + OtrlPrivKey *k, *k2; + char human[45]; + char prefix[45], *p; + int n; + int i,j; + + /* assemble the args into a prefix in standard "human" form */ + n=0; + p=prefix; + for(i=0; args[i]; i++) { + for(j=0; args[i][j]; j++) { + char c = toupper(args[i][j]); + + if(n>=40) { + irc_usermsg(irc, "too many fingerprint digits given, expected at most 40"); + return NULL; + } + + if( (c>='A' && c<='F') || (c>='0' && c<='9') ) { + *(p++) = c; + } else { + irc_usermsg(irc, "invalid hex digit '%c' in block %d", args[i][j], i+1); + return NULL; + } + + n++; + if(n%8 == 0) + *(p++) = ' '; + } + } + *p = '\0'; + log_message(LOGLVL_DEBUG, "match_privkey '%s'", prefix); + log_message(LOGLVL_DEBUG, "n=%d strlen(prefix)=%d", n, strlen(prefix)); + + /* find first key which matches the given prefix */ + n = strlen(prefix); + for(k=irc->otr_us->privkey_root; k; k=k->next) { + p = otrl_privkey_fingerprint(irc->otr_us, human, k->accountname, k->protocol); + if(!p) /* gah! :-P */ + continue; + if(!strncmp(prefix, human, n)) + break; + } + if(!k) { + irc_usermsg(irc, "%s: no match", prefix); + return NULL; + } + + /* make sure the match, if any, is unique */ + for(k2=k->next; k2; k2=k2->next) { + p = otrl_privkey_fingerprint(irc->otr_us, human, k2->accountname, k2->protocol); + if(!p) /* gah! :-P */ + continue; + if(!strncmp(prefix, human, n)) + break; + } + if(k2) { + irc_usermsg(irc, "%s: multiple matches", prefix); + return NULL; + } + + return k; +} + void show_general_otr_info(irc_t *irc) { ConnContext *ctx; diff --git a/query.c b/query.c index 7cfad745..d167abe8 100644 --- a/query.c +++ b/query.c @@ -147,7 +147,7 @@ void query_answer( irc_t *irc, query_t *q, int ans ) else irc_usermsg( irc, "Accepted: %s", q->question ); if(q->yes) - q->yes( q->ic ? q->ic : irc, q->data ); + q->yes( q->ic ? (gpointer)q->ic : (gpointer)irc, q->data ); } else { @@ -156,7 +156,7 @@ void query_answer( irc_t *irc, query_t *q, int ans ) else irc_usermsg( irc, "Rejected: %s", q->question ); if(q->no) - q->no( q->ic ? q->ic : irc, q->data ); + q->no( q->ic ? (gpointer)q->ic : (gpointer)irc, q->data ); } q->data = NULL; -- cgit v1.2.3 From e2b15bb7ebd347dd044167575068ef87ffd9301c Mon Sep 17 00:00:00 2001 From: Sven Moritz Hallberg Date: Tue, 12 Feb 2008 01:01:35 +0100 Subject: - add global policy setting - add copyright and author notices to otr.h and otr.c --- irc.c | 1 + otr.c | 62 ++++++++++++++++++++++++++++++++++++++++++++++++++++++-------- otr.h | 28 ++++++++++++++++++++++++++++ set.c | 14 ++++++++++++++ set.h | 1 + 5 files changed, 98 insertions(+), 8 deletions(-) diff --git a/irc.c b/irc.c index aea21934..b7046e59 100644 --- a/irc.c +++ b/irc.c @@ -118,6 +118,7 @@ irc_t *irc_new( int fd ) set_add( &irc->set, "op_buddies", "trusted", set_eval_op_buddies, irc ); set_add( &irc->set, "op_root", "true", set_eval_op_root, irc ); set_add( &irc->set, "op_user", "true", set_eval_op_user, irc ); + set_add( &irc->set, "otr_policy", "opportunistic", set_eval_otr_policy, irc ); set_add( &irc->set, "password", NULL, passchange, irc ); set_add( &irc->set, "private", "true", set_eval_bool, irc ); set_add( &irc->set, "query_order", "lifo", NULL, irc ); diff --git a/otr.c b/otr.c index adba7b68..757caca3 100644 --- a/otr.c +++ b/otr.c @@ -1,3 +1,40 @@ + /********************************************************************\ + * BitlBee -- An IRC to other IM-networks gateway * + * * + * Copyright 2002-2008 Wilmer van der Gaast and others * + \********************************************************************/ + +/* + OTR support (cf. http://www.cypherpunks.ca/otr/) + 2008, Sven Moritz Hallberg + + files used to store OTR data: + /.otr_keys + /.otr_fprints + + top-level todos: (search for TODO for more ;-)) + integrate otr_load/otr_save with existing storage backends + per-account policy settings + per-user policy settings +*/ + +/* + This program is free software; you can redistribute it and/or modify + it under the terms of the GNU General Public License as published by + the Free Software Foundation; either version 2 of the License, or + (at your option) any later version. + + This program is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU General Public License for more details. + + You should have received a copy of the GNU General Public License with + the Debian GNU/Linux distribution in /usr/share/common-licenses/GPL; + if not, write to the Free Software Foundation, Inc., 59 Temple Place, + Suite 330, Boston, MA 02111-1307 USA +*/ + #include "bitlbee.h" #ifdef WITH_OTR #include "irc.h" @@ -5,12 +42,6 @@ #include #include -/** -files used to store OTR data: - $configdir/$nick.otr_keys - $configdir/$nick.otr_fprints - **/ - /** OTR interface routines for the OtrlMessageAppOps struct: **/ @@ -344,7 +375,8 @@ int otr_send_message(struct im_connection *ic, const char *handle, const char *m otrmsg, OTRL_FRAGMENT_SEND_ALL, NULL); otrl_message_free(otrmsg); } else { - /* yeah, well, some const casts as usual... ;-) */ + /* note: otrl_message_sending handles policy, so that if REQUIRE_ENCRYPTION is set, + this case does not occur */ st = ic->acc->prpl->buddy_msg( ic, (char *)handle, (char *)msg, flags ); } @@ -387,7 +419,19 @@ void cmd_otr(irc_t *irc, char **args) OtrlPolicy op_policy(void *opdata, ConnContext *context) { - /* TODO: OTR policy configurable */ + struct im_connection *ic = check_imc(opdata, context->accountname, context->protocol); + const char *p; + + p = set_getstr(&ic->irc->set, "otr_policy"); + if(!strcmp(p, "never")) + return OTRL_POLICY_NEVER; + if(!strcmp(p, "opportunistic")) + return OTRL_POLICY_OPPORTUNISTIC; + if(!strcmp(p, "manual")) + return OTRL_POLICY_MANUAL; + if(!strcmp(p, "always")) + return OTRL_POLICY_ALWAYS; + return OTRL_POLICY_OPPORTUNISTIC; } @@ -857,6 +901,7 @@ void cmd_otr_forget(irc_t *irc, char **args) return; } + /* TODO: allow context specs ("user/proto/account") in 'otr forget fingerprint'? */ u = user_find(irc, args[2]); if(!u || !u->ic) { irc_usermsg(irc, "%s: unknown user", args[2]); @@ -892,6 +937,7 @@ void cmd_otr_forget(irc_t *irc, char **args) ConnContext *ctx; char *s; + /* TODO: allow context specs ("user/proto/account") in 'otr forget contex'? */ u = user_find(irc, args[2]); if(!u || !u->ic) { irc_usermsg(irc, "%s: unknown user", args[2]); diff --git a/otr.h b/otr.h index ef5453ef..ad6c6874 100644 --- a/otr.h +++ b/otr.h @@ -1,3 +1,31 @@ + /********************************************************************\ + * BitlBee -- An IRC to other IM-networks gateway * + * * + * Copyright 2002-2008 Wilmer van der Gaast and others * + \********************************************************************/ + +/* + OTR support (cf. http://www.cypherpunks.ca/otr/) + 2008, Sven Moritz Hallberg +*/ + +/* + This program is free software; you can redistribute it and/or modify + it under the terms of the GNU General Public License as published by + the Free Software Foundation; either version 2 of the License, or + (at your option) any later version. + + This program is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU General Public License for more details. + + You should have received a copy of the GNU General Public License with + the Debian GNU/Linux distribution in /usr/share/common-licenses/GPL; + if not, write to the Free Software Foundation, Inc., 59 Temple Place, + Suite 330, Boston, MA 02111-1307 USA +*/ + #ifndef BITLBEE_PROTOCOLS_OTR_H #define BITLBEE_PROTOCOLS_OTR_H diff --git a/set.c b/set.c index 0e9ae5dc..c65953f2 100644 --- a/set.c +++ b/set.c @@ -368,3 +368,17 @@ char *set_eval_charset( set_t *set, char *value ) g_iconv_close( cd ); return value; } + +/* possible values: never, opportunistic, manual, always */ +char *set_eval_otr_policy( set_t *set, char *value ) +{ + if ( !strcmp(value, "never") ) + return value; + if ( !strcmp(value, "opportunistic") ) + return value; + if ( !strcmp(value, "manual") ) + return value; + if ( !strcmp(value, "always") ) + return value; + return NULL; +} diff --git a/set.h b/set.h index 3e1b4f66..d8d772a9 100644 --- a/set.h +++ b/set.h @@ -101,5 +101,6 @@ char *set_eval_op_buddies( set_t *set, char *value ); char *set_eval_halfop_buddies( set_t *set, char *value ); char *set_eval_voice_buddies( set_t *set, char *value ); char *set_eval_charset( set_t *set, char *value ); +char *set_eval_otr_policy( set_t *set, char *value ); #endif /* __SET_H__ */ -- cgit v1.2.3 From 0529a7f8d987072756ac727472cc1f8c3dc07035 Mon Sep 17 00:00:00 2001 From: Sven Moritz Hallberg Date: Tue, 12 Feb 2008 01:08:43 +0100 Subject: bugfix for compiling with otr disabled --- otr.h | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/otr.h b/otr.h index ad6c6874..cd01aba2 100644 --- a/otr.h +++ b/otr.h @@ -80,7 +80,7 @@ typedef void *OtrlMessageAppOps; #define otr_remove(nick) {} #define otr_rename(onick,nnick) {} #define otr_check_for_key(acc) {} -#define otr_handle_msg(ic,handle,msg) (g_strdup(msg)) +#define otr_handle_message(ic,handle,msg) (g_strdup(msg)) #define otr_send_message(ic,h,m,f) (ic->acc->prpl->buddy_msg(ic,h,m,f)) void cmd_otr_nosupport(void *, char **); -- cgit v1.2.3 From 3ffc53e13e7d153312ef1daa4c798ed0eed977eb Mon Sep 17 00:00:00 2001 From: Sven Moritz Hallberg Date: Tue, 12 Feb 2008 01:25:41 +0100 Subject: add a blurb about OTR to the README --- doc/README | 8 ++++++-- 1 file changed, 6 insertions(+), 2 deletions(-) diff --git a/doc/README b/doc/README index bb6596ba..10f21501 100644 --- a/doc/README +++ b/doc/README @@ -48,6 +48,10 @@ DEPENDENCIES BitlBee's only real dependency is GLib. This is available on virtually every platform. Any recent version of GLib (2.4 or higher) will work. +Off-the-Record encryption support will be included by default if the +configure script finds libotr in one of the usual places. You can pass +--otr=1 or --otr=0 to force it on or off, respectively. + These days, MSN Messenger clients have to connect to the MS Passport servers through HTTPS. BitlBee can use several SSL libraries for this: GnuTLS, NSS (which comes with Mozilla) and OpenSSL. OpenSSL is not GPL-compatible in some @@ -151,8 +155,8 @@ Our version control system is Bazaar-NG. Our repository is at: http://code.bitlbee.org/ -A NOTE ON ENCRYPTION -==================== +A NOTE ON PASSWORD ENCRYPTION +============================= There used to be a note here about the simple obfuscation method used to make the passwords in the configuration files unreadable. However, BitlBee -- cgit v1.2.3 From f5ac0fb078ee98bd836f1527dc90952f74ab0c18 Mon Sep 17 00:00:00 2001 From: Sven Moritz Hallberg Date: Tue, 12 Feb 2008 02:32:28 +0100 Subject: document otr commands --- doc/user-guide/commands.xml | 149 ++++++++++++++++++++++++++++++++++++++++++++ 1 file changed, 149 insertions(+) diff --git a/doc/user-guide/commands.xml b/doc/user-guide/commands.xml index 5a1e398c..4f4e44b3 100644 --- a/doc/user-guide/commands.xml +++ b/doc/user-guide/commands.xml @@ -243,6 +243,155 @@ + + + Off-the-Record encryption control + otr <subcommand> [<arguments>] + + + + + Available subcommands: connect, disconnect, smp, trust, info, keygen, and forget. See help otr <subcommand> for more information. + + + + + + otr connect <nick> + + + + + Attempts to establish an encrypted connection with the specified user by sending a magic string. + + + + + + + + otr disconnect <nick> + + + + + Resets the connection with the specified user to cleartext. + + + + + + + + otr smp <nick> <secret> + + + + + Attempts to authenticate the given user's active fingerprint via the Socialist Millionaires' Protocol. + + + + If an SMP challenge has already been received from the given user, responds with the specified secret. Otherwise, a challenge for the secret will be sent. If the protocol succeeds (i.e. both parties gave the same secret), the fingerprint will be trusted. + + + + + + + + otr trust <nick> <fp1> <fp2> <fp3> <fp4> <fp5> + + + + + Manually affirms trust in the specified fingerprint, given as five blocks of precisely eight (hexadecimal) digits each. + + + + + + + + otr info + otr info <nick> + + + + + Shows information about the OTR state. The first form lists our private keys and current OTR contexts. The second form displays information about the connection with a given user, including the list of their known fingerprints. + + + + + + + + otr keygen <account-no> + + + + + Generates a new OTR private key for the given account. + + + + + + + + otr forget <thing> <arguments> + + + + + Forgets some part of our OTR userstate. Available things: fingerprint, context, and key. See help otr forget <thing> for more information. + + + + + + otr forget fingerprint <nick> <fingerprint> + + + + + Drops the specified fingerprint from the given user's OTR connection context. It is allowed to specify only a (unique) prefix of the desired fingerprint. + + + + + + + + otr forget context <nick> + + + + + Forgets the entire OTR context associated with the given user. This includes current message and protocol states, as well as any fingerprints for that user. + + + + + + + + otr forget key <fingerprint> + + + + + Forgets an OTR private key matching the specified fingerprint. It is allowed to specify only a (unique) prefix of the fingerprint. + + + + + + + + + Miscellaneous settings -- cgit v1.2.3 From 1c18ce17500c2952ca1dcae4aa903e20574b3fca Mon Sep 17 00:00:00 2001 From: Sven Moritz Hallberg Date: Tue, 12 Feb 2008 03:09:57 +0100 Subject: update settings documentation --- doc/user-guide/commands.xml | 88 ++++++++++++++++++++++++++++++++++++++++++--- 1 file changed, 83 insertions(+), 5 deletions(-) diff --git a/doc/user-guide/commands.xml b/doc/user-guide/commands.xml index 4f4e44b3..8c2a30ca 100644 --- a/doc/user-guide/commands.xml +++ b/doc/user-guide/commands.xml @@ -546,6 +546,17 @@ + + true + + + + If set to true, BitlBee will color incoming encrypted messages according to their fingerprint trust level: untrusted=red, trusted=green. + + + + + false @@ -612,6 +623,22 @@ + + encrypted + encrypted, trusted, notaway, false + + + + Specifies under which circumstances BitlBee should give the "halfop" mode flag (+h) to buddies. + + + + If "false", the flag is never set. On "notaway", the flag is removed for users marked as "away" and set for all others. On "encrypted", the flag is set for users with whom we have an encrypted connection. On "trusted", it is set only for encrypted connections using a trusted key. + + + + + true @@ -634,17 +661,52 @@ - - both - both, root, user, none + + trusted + encrypted, trusted, notaway, false - Some people prefer themself and root to have operator status in &bitlbee, other people don't. You can change these states using this setting. + Specifies under which circumstances BitlBee should give the "op" mode flag (+o) to buddies. + + + + If "false", the flag is never set. On "notaway", the flag is removed for users marked as "away" and set for all others. On "encrypted", the flag is set for users with whom we have an encrypted connection. On "trusted", it is set only for encrypted connections using a trusted key. + + + + + true + + - The value "both" means both user and root get ops. "root" means, well, just root. "user" means just the user. "none" means nobody will get operator status. + Some people prefer themself and root to have operator status in &bitlbee, other people don't. You can set the desired state for root using this setting. + + + + + + true + + + + Some people prefer themself and root to have operator status in &bitlbee, other people don't. You can set the desired state for yourself using this setting. + + + + + + opportunistic + never, opportunistic, manual, always + + + + This setting controls the policy for establishing Off-the-Record connections. + + + A value of "never" effectively disables the OTR subsystem. In "opportunistic" mode, a magic whitespace pattern will be appended to the first message sent to any user. If the peer is also running opportunistic OTR, an encrypted connection will be set up automatically. On "manual", on the other hand, OTR connections must be established explicitly using otr connect. Finally, the setting "always" enforces encrypted communication by causing BitlBee to refuse to send any cleartext messages at all. @@ -830,6 +892,22 @@ + + trusted + encrypted, trusted, notaway, false + + + + Specifies under which circumstances BitlBee should give the "voice" mode flag (+v) to buddies. + + + + If "false", the flag is never set. On "notaway", the flag is removed for users marked as "away" and set for all others. On "encrypted", the flag is set for users with whom we have an encrypted connection. On "trusted", it is set only for encrypted connections using a trusted key. + + + + + false -- cgit v1.2.3 From 1a1faa077c3c4ffc2bfe88a4466d748919fcdc45 Mon Sep 17 00:00:00 2001 From: Sven Moritz Hallberg Date: Wed, 13 Feb 2008 20:05:21 +0100 Subject: fix a bug in set_eval_mode_buddies --- set.c | 2 ++ 1 file changed, 2 insertions(+) diff --git a/set.c b/set.c index c65953f2..5f3e7b49 100644 --- a/set.c +++ b/set.c @@ -268,6 +268,7 @@ char *set_eval_mode_buddies( set_t *set, char *value, char modeflag ) irc_write( irc, ":%s!%s@%s MODE %s +%s%s", irc->mynick, irc->mynick, irc->myhost, irc->channel, flags, op ); op[0]='\0'; + nop=0; g_free(flags); } if(strlen(deop)+strlen(u->nick)+2 > 64) { @@ -275,6 +276,7 @@ char *set_eval_mode_buddies( set_t *set, char *value, char modeflag ) irc_write( irc, ":%s!%s@%s MODE %s -%s%s", irc->mynick, irc->mynick, irc->myhost, irc->channel, flags, deop ); deop[0]='\0'; + ndeop=0; g_free(flags); } -- cgit v1.2.3 From a161d33779bb56fabe6466f15a8ae98881f55520 Mon Sep 17 00:00:00 2001 From: Sven Moritz Hallberg Date: Thu, 14 Feb 2008 19:43:47 +0100 Subject: interpret ' --- lib/misc.c | 1 + 1 file changed, 1 insertion(+) diff --git a/lib/misc.c b/lib/misc.c index a8df2354..ccb04d4c 100644 --- a/lib/misc.c +++ b/lib/misc.c @@ -111,6 +111,7 @@ static const htmlentity_t ent[] = { "lt", "<" }, { "gt", ">" }, { "amp", "&" }, + { "apos", "'" }, { "quot", "\"" }, { "aacute", "á" }, { "eacute", "é" }, -- cgit v1.2.3 From ef93a2f828b62e74f62638e7fbbe63e47f92194f Mon Sep 17 00:00:00 2001 From: Sven Moritz Hallberg Date: Thu, 14 Feb 2008 19:48:34 +0100 Subject: chmod 0600 otr save files --- otr.c | 2 ++ 1 file changed, 2 insertions(+) diff --git a/otr.c b/otr.c index 757caca3..943a9c13 100644 --- a/otr.c +++ b/otr.c @@ -249,6 +249,7 @@ void otr_save(irc_t *irc) if(e) { log_message(LOGLVL_ERROR, "otr save: %s: %s", s, strerror(e)); } + chmod(s, 0600); } void otr_remove(const char *nick) @@ -1532,6 +1533,7 @@ gpointer otr_keygen_thread_func(gpointer data) g_static_rec_mutex_lock(&kg->irc->otr_mutex); kg->result = otrl_privkey_generate(kg->irc->otr_us, kg->keyfile, kg->handle, kg->protocol); + chmod(kg->keyfile, 0600); g_static_rec_mutex_unlock(&kg->irc->otr_mutex); /* OTR enabled again */ -- cgit v1.2.3 From f3597a161cd13e076db626fed3d833cac0d62d6c Mon Sep 17 00:00:00 2001 From: Sven Moritz Hallberg Date: Thu, 14 Feb 2008 22:01:21 +0100 Subject: revert keygen behaviour to old (lax) behavior --- otr.c | 32 +++++++++++++++++++------------- root_commands.c | 4 ++-- 2 files changed, 21 insertions(+), 15 deletions(-) diff --git a/otr.c b/otr.c index 943a9c13..e414bb39 100644 --- a/otr.c +++ b/otr.c @@ -164,9 +164,6 @@ Fingerprint *match_fingerprint(irc_t *irc, ConnContext *ctx, const char **args); /* find a private key by fingerprint prefix (given as any number of hex strings) */ OtrlPrivKey *match_privkey(irc_t *irc, const char **args); -/* to log out accounts during keygen */ -extern void cmd_account(irc_t *irc, char **cmd); - /*** routines declared in otr.h: ***/ @@ -300,7 +297,14 @@ char *otr_handle_message(struct im_connection *ic, const char *handle, const cha char *colormsg; if(!g_static_rec_mutex_trylock(&ic->irc->otr_mutex)) { - irc_usermsg(ic->irc, "otr keygen in progress - msg from %s dropped", + user_t *u = user_findhandle(ic, handle); + + /* fallback for non-otr clients */ + if(u && !u->encrypted) { + return g_strdup(msg); + } + + irc_usermsg(ic->irc, "encrypted msg from %s during keygen - dropped", peernick(ic->irc, handle, ic->acc->prpl->name)); return NULL; } @@ -349,7 +353,15 @@ int otr_send_message(struct im_connection *ic, const char *handle, const char *m ConnContext *ctx = NULL; if(!g_static_rec_mutex_trylock(&ic->irc->otr_mutex)) { - irc_usermsg(ic->irc, "otr keygen in progress - msg to %s not sent", + user_t *u = user_findhandle(ic, handle); + + /* Fallback for non-otr clients. + Yes, this better shouldn't send private stuff in the clear... */ + if(u && !u->encrypted) { + return ic->acc->prpl->buddy_msg(ic, (char *)handle, (char *)msg, flags); + } + + irc_usermsg(ic->irc, "encrypted message to %s during keygen - not sent", peernick(ic->irc, handle, ic->acc->prpl->name)); return 1; } @@ -1465,12 +1477,13 @@ void show_otr_context_info(irc_t *irc, ConnContext *ctx) void otr_keygen(irc_t *irc, const char *handle, const char *protocol) { - char *account_off[] = {"account", "off", NULL}; GError *err; GThread *thr; struct kgdata *kg; gint ev; + irc_usermsg(irc, "generating new private key for %s/%s...", handle, protocol); + kg = g_new0(struct kgdata, 1); if(!kg) { irc_usermsg(irc, "otr keygen failed: out of memory"); @@ -1508,13 +1521,6 @@ void otr_keygen(irc_t *irc, const char *handle, const char *protocol) return; } - /* tell the user what's happening, go comatose, and start the keygen */ - irc_usermsg(irc, "going comatose for otr key generation, this will take a moment"); - irc_usermsg(irc, "all accounts logging out, user commands disabled"); - cmd_account(irc, account_off); - irc_usermsg(irc, "generating new otr privkey for %s/%s...", - handle, protocol); - thr = g_thread_create(&otr_keygen_thread_func, kg, FALSE, &err); if(!thr) { irc_usermsg(irc, "otr keygen failed: %s", err->message); diff --git a/root_commands.c b/root_commands.c index 03b579a1..04c62fea 100644 --- a/root_commands.c +++ b/root_commands.c @@ -135,7 +135,7 @@ static void cmd_help( irc_t *irc, char **cmd ) } } -void cmd_account( irc_t *irc, char **cmd ); +static void cmd_account( irc_t *irc, char **cmd ); static void cmd_identify( irc_t *irc, char **cmd ) { @@ -212,7 +212,7 @@ static void cmd_drop( irc_t *irc, char **cmd ) } } -void cmd_account( irc_t *irc, char **cmd ) +static void cmd_account( irc_t *irc, char **cmd ) { account_t *a; -- cgit v1.2.3 From 6c91e6e59a1216100d851636d2824ce49ad7ce30 Mon Sep 17 00:00:00 2001 From: Sven Moritz Hallberg Date: Fri, 15 Feb 2008 01:45:21 +0100 Subject: otr_load error handling + stonedcoder copyright notice --- otr.c | 11 ++++++++--- otr.h | 2 ++ 2 files changed, 10 insertions(+), 3 deletions(-) diff --git a/otr.c b/otr.c index e414bb39..fcf653c5 100644 --- a/otr.c +++ b/otr.c @@ -6,7 +6,9 @@ /* OTR support (cf. http://www.cypherpunks.ca/otr/) + 2008, Sven Moritz Hallberg + (c) and funded by stonedcoder.org files used to store OTR data: /.otr_keys @@ -214,17 +216,20 @@ void otr_load(irc_t *irc) char s[512]; account_t *a; gcry_error_t e; + int eno; log_message(LOGLVL_DEBUG, "otr_load '%s'", irc->nick); g_snprintf(s, 511, "%s%s.otr_keys", global.conf->configdir, irc->nick); e = otrl_privkey_read(irc->otr_us, s); - if(e && e!=ENOENT) { + eno = gcry_error_code_to_errno(e); + if(e && eno!=ENOENT) { log_message(LOGLVL_ERROR, "otr load: %s: %s", s, strerror(e)); } g_snprintf(s, 511, "%s%s.otr_fprints", global.conf->configdir, irc->nick); e = otrl_privkey_read_fingerprints(irc->otr_us, s, NULL, NULL); - if(e && e!=ENOENT) { + eno = gcry_error_code_to_errno(e); + if(e && eno!=ENOENT) { log_message(LOGLVL_ERROR, "otr load: %s: %s", s, strerror(e)); } @@ -1471,7 +1476,7 @@ void show_otr_context_info(irc_t *irc, ConnContext *ctx) irc_usermsg(irc, " connection state: %d", ctx->msgstate); } - irc_usermsg(irc, " fingerprints: (bold=active)"); + irc_usermsg(irc, " fingerprints: (bold=active)"); show_fingerprints(irc, ctx); } diff --git a/otr.h b/otr.h index cd01aba2..805c0eea 100644 --- a/otr.h +++ b/otr.h @@ -6,7 +6,9 @@ /* OTR support (cf. http://www.cypherpunks.ca/otr/) + 2008, Sven Moritz Hallberg + (c) and funded by stonedcoder.org */ /* -- cgit v1.2.3 From 522a00f1b1163cedf15a86329c0097601eb7940b Mon Sep 17 00:00:00 2001 From: Sven Moritz Hallberg Date: Fri, 15 Feb 2008 10:27:26 +0100 Subject: remove thread-based keygen replace it with a process-based stub --- irc.c | 11 +++- irc.h | 3 +- otr.c | 190 ++++++++++---------------------------------------------- root_commands.c | 8 --- 4 files changed, 44 insertions(+), 168 deletions(-) diff --git a/irc.c b/irc.c index b7046e59..a2bcfc59 100644 --- a/irc.c +++ b/irc.c @@ -27,6 +27,8 @@ #include "bitlbee.h" #include "crypting.h" #include "ipc.h" +#include +#include static gboolean irc_userping( gpointer _irc, int fd, b_input_condition cond ); @@ -132,7 +134,8 @@ irc_t *irc_new( int fd ) conf_loaddefaults( irc ); irc->otr_us = otrl_userstate_create(); - g_static_rec_mutex_init(&irc->otr_mutex); + irc->otr_keygen = 0; + irc->otr_ntodo = 0; return( irc ); } @@ -285,7 +288,11 @@ void irc_free(irc_t * irc) } otrl_userstate_free(irc->otr_us); - g_static_rec_mutex_free(&irc->otr_mutex); + if(irc->otr_keygen) { + kill(irc->otr_keygen, SIGTERM); + waitpid(irc->otr_keygen, NULL, 0); + /* TODO: remove stale keygen tempfiles */ + } g_free(irc); diff --git a/irc.h b/irc.h index 8c993e13..8e9c95c5 100644 --- a/irc.h +++ b/irc.h @@ -98,7 +98,8 @@ typedef struct irc gint ping_source_id; OtrlUserState otr_us; - GStaticRecMutex otr_mutex; /* for locking otr during keygen */ + pid_t otr_keygen; + int otr_ntodo; } irc_t; #include "user.h" diff --git a/otr.c b/otr.c index fcf653c5..c4e8a20a 100644 --- a/otr.c +++ b/otr.c @@ -102,26 +102,12 @@ const command_t otr_commands[] = { /** misc. helpers/subroutines: **/ -/* start background thread to generate a (new) key for a given account */ +/* start background process to generate a (new) key for a given account */ void otr_keygen(irc_t *irc, const char *handle, const char *protocol); -/* keygen thread main func */ -gpointer otr_keygen_thread_func(gpointer data); - -/* mainloop handler for when keygen thread finishes */ +/* mainloop handler for when a keygen finishes */ gboolean keygen_finish_handler(gpointer data, gint fd, b_input_condition cond); -/* data to be passed to otr_keygen_thread_func */ -struct kgdata { - irc_t *irc; /* access to OTR userstate */ - char *keyfile; /* free me! */ - const char *handle; /* don't free! */ - const char *protocol; /* don't free! */ - GMutex *mutex; /* lock for the 'done' flag, free me! */ - int done; /* is the thread done? */ - gcry_error_t result; /* return value of otrl_privkey_generate */ -}; - /* some yes/no handlers */ void yes_keygen(gpointer w, void *data); void yes_forget_fingerprint(gpointer w, void *data); @@ -171,7 +157,6 @@ OtrlPrivKey *match_privkey(irc_t *irc, const char **args); void otr_init(void) { - if(!g_thread_supported()) g_thread_init(NULL); OTRL_INIT; /* fill global OtrlMessageAppOps */ @@ -195,42 +180,24 @@ void otr_init(void) global.otr_ops.account_name_free = NULL; } -/* Notice on the otr_mutex: - - The incoming/outgoing message handlers try to lock the otr_mutex. If they succeed, - this will prevent a concurrent keygen (possibly spawned by that very command) - from messing up the userstate. If the lock fails, that means there already is - a keygen in progress. Instead of blocking for an unknown time, they - will bail out gracefully, informing the user of this temporary "coma". - TODO: Hold back incoming/outgoing messages and process them when keygen completes? - - The other routines do not lock the otr_mutex themselves, it is done as a - catch-all in the root command handler. Rationale: - a) it's easy to code - b) it makes it obvious that no command can get its userstate corrupted - c) the "irc" struct is readily available there for feedback to the user - */ - void otr_load(irc_t *irc) { char s[512]; account_t *a; gcry_error_t e; - int eno; + gcry_error_t enoent = gcry_error_from_errno(ENOENT); log_message(LOGLVL_DEBUG, "otr_load '%s'", irc->nick); g_snprintf(s, 511, "%s%s.otr_keys", global.conf->configdir, irc->nick); e = otrl_privkey_read(irc->otr_us, s); - eno = gcry_error_code_to_errno(e); - if(e && eno!=ENOENT) { - log_message(LOGLVL_ERROR, "otr load: %s: %s", s, strerror(e)); + if(e && e!=enoent) { + irc_usermsg(irc, "otr load: %s: %s", s, gcry_strerror(e)); } g_snprintf(s, 511, "%s%s.otr_fprints", global.conf->configdir, irc->nick); e = otrl_privkey_read_fingerprints(irc->otr_us, s, NULL, NULL); - eno = gcry_error_code_to_errno(e); - if(e && eno!=ENOENT) { - log_message(LOGLVL_ERROR, "otr load: %s: %s", s, strerror(e)); + if(e && e!=enoent) { + irc_usermsg(irc, "otr load: %s: %s", s, gcry_strerror(e)); } /* check for otr keys on all accounts */ @@ -249,7 +216,7 @@ void otr_save(irc_t *irc) g_snprintf(s, 511, "%s%s.otr_fprints", global.conf->configdir, irc->nick); e = otrl_privkey_write_fingerprints(irc->otr_us, s); if(e) { - log_message(LOGLVL_ERROR, "otr save: %s: %s", s, strerror(e)); + irc_usermsg(irc, "otr save: %s: %s", s, gcry_strerror(e)); } chmod(s, 0600); } @@ -301,19 +268,6 @@ char *otr_handle_message(struct im_connection *ic, const char *handle, const cha OtrlTLV *tlvs = NULL; char *colormsg; - if(!g_static_rec_mutex_trylock(&ic->irc->otr_mutex)) { - user_t *u = user_findhandle(ic, handle); - - /* fallback for non-otr clients */ - if(u && !u->encrypted) { - return g_strdup(msg); - } - - irc_usermsg(ic->irc, "encrypted msg from %s during keygen - dropped", - peernick(ic->irc, handle, ic->acc->prpl->name)); - return NULL; - } - ignore_msg = otrl_message_receiving(ic->irc->otr_us, &global.otr_ops, ic, ic->acc->user, ic->acc->prpl->name, handle, msg, &newmsg, &tlvs, NULL, NULL); @@ -322,11 +276,9 @@ char *otr_handle_message(struct im_connection *ic, const char *handle, const cha if(ignore_msg) { /* this was an internal OTR protocol message */ - g_static_rec_mutex_unlock(&ic->irc->otr_mutex); return NULL; } else if(!newmsg) { /* this was a non-OTR message */ - g_static_rec_mutex_unlock(&ic->irc->otr_mutex); return g_strdup(msg); } else { /* OTR has processed this message */ @@ -346,7 +298,6 @@ char *otr_handle_message(struct im_connection *ic, const char *handle, const cha colormsg = g_strdup(newmsg); } otrl_message_free(newmsg); - g_static_rec_mutex_unlock(&ic->irc->otr_mutex); return colormsg; } } @@ -357,25 +308,10 @@ int otr_send_message(struct im_connection *ic, const char *handle, const char *m char *otrmsg = NULL; ConnContext *ctx = NULL; - if(!g_static_rec_mutex_trylock(&ic->irc->otr_mutex)) { - user_t *u = user_findhandle(ic, handle); - - /* Fallback for non-otr clients. - Yes, this better shouldn't send private stuff in the clear... */ - if(u && !u->encrypted) { - return ic->acc->prpl->buddy_msg(ic, (char *)handle, (char *)msg, flags); - } - - irc_usermsg(ic->irc, "encrypted message to %s during keygen - not sent", - peernick(ic->irc, handle, ic->acc->prpl->name)); - return 1; - } - st = otrl_message_sending(ic->irc->otr_us, &global.otr_ops, ic, ic->acc->user, ic->acc->prpl->name, handle, msg, NULL, &otrmsg, NULL, NULL); if(st) { - g_static_rec_mutex_unlock(&ic->irc->otr_mutex); return st; } @@ -386,7 +322,6 @@ int otr_send_message(struct im_connection *ic, const char *handle, const char *m if(otrmsg) { if(!ctx) { otrl_message_free(otrmsg); - g_static_rec_mutex_unlock(&ic->irc->otr_mutex); return 1; } st = otrl_message_fragment_and_send(&global.otr_ops, ic, ctx, @@ -398,7 +333,6 @@ int otr_send_message(struct im_connection *ic, const char *handle, const char *m st = ic->acc->prpl->buddy_msg( ic, (char *)handle, (char *)msg, flags ); } - g_static_rec_mutex_unlock(&ic->irc->otr_mutex); return st; } @@ -1482,101 +1416,43 @@ void show_otr_context_info(irc_t *irc, ConnContext *ctx) void otr_keygen(irc_t *irc, const char *handle, const char *protocol) { - GError *err; - GThread *thr; - struct kgdata *kg; - gint ev; irc_usermsg(irc, "generating new private key for %s/%s...", handle, protocol); - - kg = g_new0(struct kgdata, 1); - if(!kg) { - irc_usermsg(irc, "otr keygen failed: out of memory"); - return; - } + irc_usermsg(irc, "n/a: not implemented"); + return; - /* Assemble the job description to be passed to thread and handler */ - kg->irc = irc; - kg->keyfile = g_strdup_printf("%s%s.otr_keys", global.conf->configdir, kg->irc->nick); - if(!kg->keyfile) { - irc_usermsg(irc, "otr keygen failed: out of memory"); - g_free(kg); - return; - } - kg->handle = handle; - kg->protocol = protocol; - kg->mutex = g_mutex_new(); - if(!kg->mutex) { - irc_usermsg(irc, "otr keygen failed: couldn't create mutex"); - g_free(kg->keyfile); - g_free(kg); - return; - } - kg->done = 0; - - /* Poll for completion of the thread periodically. I would have preferred - to just wait on a pipe but this way it's portable to Windows. *sigh* - */ - ev = b_timeout_add(1000, &keygen_finish_handler, kg); - if(!ev) { - irc_usermsg(irc, "otr keygen failed: couldn't register timeout"); - g_free(kg->keyfile); - g_mutex_free(kg->mutex); - g_free(kg); - return; - } - - thr = g_thread_create(&otr_keygen_thread_func, kg, FALSE, &err); - if(!thr) { - irc_usermsg(irc, "otr keygen failed: %s", err->message); - g_free(kg->keyfile); - g_mutex_free(kg->mutex); - g_free(kg); - b_event_remove(ev); - } -} - -gpointer otr_keygen_thread_func(gpointer data) -{ - struct kgdata *kg = (struct kgdata *)data; + /* see if we already have a keygen child running. if not, start one and put a + handler on its output. + + b_input_add(fd, GAIM_INPUT_READ, keygen_finish_handler, NULL); - /* lock OTR subsystem and do the work */ - g_static_rec_mutex_lock(&kg->irc->otr_mutex); - kg->result = otrl_privkey_generate(kg->irc->otr_us, kg->keyfile, kg->handle, - kg->protocol); - chmod(kg->keyfile, 0600); - g_static_rec_mutex_unlock(&kg->irc->otr_mutex); - /* OTR enabled again */ + generate a fresh temp file name for our new key and save our current keys to it. + send the child filename and accountname/protocol for the new key + increment 'ntodo' */ - /* notify mainloop */ - g_mutex_lock(kg->mutex); - kg->done = 1; - g_mutex_unlock(kg->mutex); + /* in the child: + read filename, accountname, protocol from input, and start work: + + result = otrl_privkey_generate(kg->irc->otr_us, kg->keyfile, kg->handle, + kg->protocol); + + when done, send filename, accountname, and protocol to output. */ - return NULL; } gboolean keygen_finish_handler(gpointer data, gint fd, b_input_condition cond) { - struct kgdata *kg = (struct kgdata *)data; - int done; - - g_mutex_lock(kg->mutex); - done = kg->done; - g_mutex_unlock(kg->mutex); - if(kg->done) { - if(kg->result) { - irc_usermsg(kg->irc, "otr keygen: %s", strerror(kg->result)); - } else { + /* in the handler: + read filename, accountname, and protocol from child output + print a message to the user irc_usermsg(kg->irc, "otr keygen for %s/%s complete", kg->handle, kg->protocol); - } - g_free(kg->keyfile); - g_mutex_free(kg->mutex); - g_free(kg); - return FALSE; /* unregister timeout */ - } + load the file into userstate + call otr_save. + remove the tempfile. + decrement 'ntodo' + if 'ntodo' reaches zero, send SIGTERM to the child, waitpid for it, return FALSE */ - return TRUE; /* still working, continue checking */ + return TRUE; /* still working, keep watching */ } void yes_keygen(gpointer w, void *data) diff --git a/root_commands.c b/root_commands.c index 04c62fea..9b492048 100644 --- a/root_commands.c +++ b/root_commands.c @@ -85,27 +85,19 @@ void root_command( irc_t *irc, char *cmd[] ) if( !cmd[0] ) return; - if(!g_static_rec_mutex_trylock(&irc->otr_mutex)) { - irc_usermsg(irc, "keygen in progress, bitlbee comatose - please wait"); - return; - } - for( i = 0; commands[i].command; i++ ) if( g_strcasecmp( commands[i].command, cmd[0] ) == 0 ) { if( !cmd[commands[i].required_parameters] ) { irc_usermsg( irc, "Not enough parameters given (need %d)", commands[i].required_parameters ); - g_static_rec_mutex_unlock(&irc->otr_mutex); return; } commands[i].execute( irc, cmd ); - g_static_rec_mutex_unlock(&irc->otr_mutex); return; } irc_usermsg( irc, "Unknown command: %s. Please use \x02help commands\x02 to get a list of available commands.", cmd[0] ); - g_static_rec_mutex_unlock(&irc->otr_mutex); } static void cmd_help( irc_t *irc, char **cmd ) -- cgit v1.2.3 From 27db43361a3fdd3420b12aa5bf151dce4545273f Mon Sep 17 00:00:00 2001 From: Sven Moritz Hallberg Date: Fri, 15 Feb 2008 18:36:18 +0100 Subject: implement background keygen via child process --- irc.c | 2 + irc.h | 6 ++- otr.c | 173 ++++++++++++++++++++++++++++++++++++++++++++++++++++++++---------- 3 files changed, 153 insertions(+), 28 deletions(-) diff --git a/irc.c b/irc.c index a2bcfc59..469789e7 100644 --- a/irc.c +++ b/irc.c @@ -135,6 +135,8 @@ irc_t *irc_new( int fd ) irc->otr_us = otrl_userstate_create(); irc->otr_keygen = 0; + irc->otr_to = NULL; + irc->otr_from = NULL; irc->otr_ntodo = 0; return( irc ); diff --git a/irc.h b/irc.h index 8e9c95c5..c808c32d 100644 --- a/irc.h +++ b/irc.h @@ -98,8 +98,10 @@ typedef struct irc gint ping_source_id; OtrlUserState otr_us; - pid_t otr_keygen; - int otr_ntodo; + pid_t otr_keygen; /* pid of keygen slave */ + FILE *otr_to; /* pipe to keygen slave */ + FILE *otr_from; /* pipe from keygen slave */ + int otr_ntodo; /* number of keys left to generate */ } irc_t; #include "user.h" diff --git a/otr.c b/otr.c index c4e8a20a..26ce92de 100644 --- a/otr.c +++ b/otr.c @@ -42,6 +42,7 @@ #include "irc.h" #include "otr.h" #include +#include #include @@ -105,9 +106,18 @@ const command_t otr_commands[] = { /* start background process to generate a (new) key for a given account */ void otr_keygen(irc_t *irc, const char *handle, const char *protocol); +/* main function for the forked keygen slave */ +void keygen_child_main(OtrlUserState us, int infd, int outfd); + /* mainloop handler for when a keygen finishes */ gboolean keygen_finish_handler(gpointer data, gint fd, b_input_condition cond); +/* copy the contents of file a to file b, overwriting it if it exists */ +void copyfile(const char *a, const char *b); + +/* read one line of input from a stream, excluding trailing newline */ +void myfgets(char *s, int size, FILE *stream); + /* some yes/no handlers */ void yes_keygen(gpointer w, void *data); void yes_forget_fingerprint(gpointer w, void *data); @@ -1416,43 +1426,154 @@ void show_otr_context_info(irc_t *irc, ConnContext *ctx) void otr_keygen(irc_t *irc, const char *handle, const char *protocol) { - irc_usermsg(irc, "generating new private key for %s/%s...", handle, protocol); - irc_usermsg(irc, "n/a: not implemented"); - return; /* see if we already have a keygen child running. if not, start one and put a - handler on its output. - - b_input_add(fd, GAIM_INPUT_READ, keygen_finish_handler, NULL); - - generate a fresh temp file name for our new key and save our current keys to it. - send the child filename and accountname/protocol for the new key - increment 'ntodo' */ + handler on its output. */ + if(!irc->otr_keygen || waitpid(irc->otr_keygen, NULL, WNOHANG)) { + pid_t p; + int to[2], from[2]; + FILE *tof, *fromf; + + if(pipe(to) < 0 || pipe(from) < 0) { + irc_usermsg(irc, "otr keygen: couldn't create pipe: %s", strerror(errno)); + return; + } + + tof = fdopen(to[1], "w"); + fromf = fdopen(from[0], "r"); + if(!tof || !fromf) { + irc_usermsg(irc, "otr keygen: couldn't streamify pipe: %s", strerror(errno)); + return; + } + + p = fork(); + if(p<0) { + irc_usermsg(irc, "otr keygen: couldn't fork: %s", strerror(errno)); + return; + } + + if(!p) { + /* child process */ + signal(SIGTERM, exit); + keygen_child_main(irc->otr_us, to[0], from[1]); + exit(0); + } + + irc->otr_keygen = p; + irc->otr_to = tof; + irc->otr_from = fromf; + irc->otr_ntodo = 0; + b_input_add(from[0], GAIM_INPUT_READ, keygen_finish_handler, irc); + } - /* in the child: - read filename, accountname, protocol from input, and start work: + /* send accountname and protocol for the new key to our keygen slave */ + fprintf(irc->otr_to, "%s\n%s\n", handle, protocol); + fflush(irc->otr_to); + irc->otr_ntodo++; +} - result = otrl_privkey_generate(kg->irc->otr_us, kg->keyfile, kg->handle, - kg->protocol); +void keygen_child_main(OtrlUserState us, int infd, int outfd) +{ + FILE *input, *output; + char filename[128], accountname[512], protocol[512]; + gcry_error_t e; + int tempfd; + + input = fdopen(infd, "r"); + output = fdopen(outfd, "w"); + + while(!feof(input) && !ferror(input) && !feof(output) && !ferror(output)) { + myfgets(accountname, 512, input); + myfgets(protocol, 512, input); - when done, send filename, accountname, and protocol to output. */ + strncpy(filename, "/tmp/bitlbee-XXXXXX", 128); + tempfd = mkstemp(filename); + close(tempfd); + + e = otrl_privkey_generate(us, filename, accountname, protocol); + if(e) { + fprintf(output, "\n"); /* this means failure */ + fprintf(output, "otr keygen: %s\n", gcry_strerror(e)); + unlink(filename); + } else { + fprintf(output, "%s\n", filename); + fprintf(output, "otr keygen for %s/%s complete\n", accountname, protocol); + } + fflush(output); + } + fclose(input); + fclose(output); } gboolean keygen_finish_handler(gpointer data, gint fd, b_input_condition cond) { - /* in the handler: - read filename, accountname, and protocol from child output - print a message to the user - irc_usermsg(kg->irc, "otr keygen for %s/%s complete", kg->handle, kg->protocol); - load the file into userstate - call otr_save. - remove the tempfile. - decrement 'ntodo' - if 'ntodo' reaches zero, send SIGTERM to the child, waitpid for it, return FALSE */ - - return TRUE; /* still working, keep watching */ + irc_t *irc = (irc_t *)data; + char filename[512], msg[512]; + + log_message(LOGLVL_DEBUG, "keygen_finish_handler cond=%d", cond); + + myfgets(filename, 512, irc->otr_from); + myfgets(msg, 512, irc->otr_from); + + log_message(LOGLVL_DEBUG, "filename='%s'", filename); + log_message(LOGLVL_DEBUG, "msg='%s'", msg); + + irc_usermsg(irc, "%s", msg); + if(filename[0]) { + char *kf = g_strdup_printf("%s%s.otr_keys", global.conf->configdir, irc->nick); + char *tmp = g_strdup_printf("%s.new", kf); + copyfile(filename, tmp); + unlink(filename); + rename(tmp,kf); + otrl_privkey_read(irc->otr_us, kf); + g_free(kf); + g_free(tmp); + } + + irc->otr_ntodo--; + if(irc->otr_ntodo < 1) { + /* okay, we think the slave is idle now, so kill him */ + log_message(LOGLVL_DEBUG, "all keys done. die, slave!"); + fclose(irc->otr_from); + fclose(irc->otr_to); + kill(irc->otr_keygen, SIGTERM); + waitpid(irc->otr_keygen, NULL, 0); + irc->otr_keygen = 0; + return FALSE; /* unregister ourselves */ + } else { + return TRUE; /* slave still working, keep watching */ + } +} + +void copyfile(const char *a, const char *b) +{ + int fda, fdb; + int n; + char buf[1024]; + + log_message(LOGLVL_DEBUG, "copyfile '%s' '%s'", a, b); + + fda = open(a, O_RDONLY); + fdb = open(b, O_WRONLY | O_CREAT | O_TRUNC, 0600); + + while((n=read(fda, buf, 1024)) > 0) + write(fdb, buf, n); + + close(fda); + close(fdb); +} + +void myfgets(char *s, int size, FILE *stream) +{ + if(!fgets(s, size, stream)) { + s[0] = '\0'; + } else { + int n = strlen(s); + if(n>0 && s[n-1] == '\n') + s[n-1] = '\0'; + } } void yes_keygen(gpointer w, void *data) -- cgit v1.2.3 From dc9797f7ad4177dc72373ce71d375257fb0271a1 Mon Sep 17 00:00:00 2001 From: Sven Moritz Hallberg Date: Sat, 16 Feb 2008 14:24:44 +0100 Subject: keep track of which keys are queued for generation --- irc.c | 13 +--- irc.h | 6 +- otr.c | 223 +++++++++++++++++++++++++++++++++++++++++++++++++----------------- otr.h | 34 ++++++++-- 4 files changed, 198 insertions(+), 78 deletions(-) diff --git a/irc.c b/irc.c index 469789e7..d73f5b4a 100644 --- a/irc.c +++ b/irc.c @@ -133,11 +133,7 @@ irc_t *irc_new( int fd ) conf_loaddefaults( irc ); - irc->otr_us = otrl_userstate_create(); - irc->otr_keygen = 0; - irc->otr_to = NULL; - irc->otr_from = NULL; - irc->otr_ntodo = 0; + irc->otr = otr_new(); return( irc ); } @@ -289,12 +285,7 @@ void irc_free(irc_t * irc) } } - otrl_userstate_free(irc->otr_us); - if(irc->otr_keygen) { - kill(irc->otr_keygen, SIGTERM); - waitpid(irc->otr_keygen, NULL, 0); - /* TODO: remove stale keygen tempfiles */ - } + otr_free(irc->otr); g_free(irc); diff --git a/irc.h b/irc.h index c808c32d..e9f47082 100644 --- a/irc.h +++ b/irc.h @@ -97,11 +97,7 @@ typedef struct irc gint w_watch_source_id; gint ping_source_id; - OtrlUserState otr_us; - pid_t otr_keygen; /* pid of keygen slave */ - FILE *otr_to; /* pipe to keygen slave */ - FILE *otr_from; /* pipe from keygen slave */ - int otr_ntodo; /* number of keys left to generate */ + otr_t *otr; /* OTR state and book keeping */ } irc_t; #include "user.h" diff --git a/otr.c b/otr.c index 26ce92de..9535e8db 100644 --- a/otr.c +++ b/otr.c @@ -44,6 +44,7 @@ #include #include #include +#include /** OTR interface routines for the OtrlMessageAppOps struct: **/ @@ -103,6 +104,9 @@ const command_t otr_commands[] = { /** misc. helpers/subroutines: **/ +/* check whether we are already generating a key for a given account */ +int keygen_in_progress(irc_t *irc, const char *handle, const char *protocol); + /* start background process to generate a (new) key for a given account */ void otr_keygen(irc_t *irc, const char *handle, const char *protocol); @@ -190,6 +194,35 @@ void otr_init(void) global.otr_ops.account_name_free = NULL; } +otr_t *otr_new(void) +{ + otr_t *otr = g_new0(otr_t, 1); + + otr->us = otrl_userstate_create(); + + return otr; +} + +void otr_free(otr_t *otr) +{ + otrl_userstate_free(otr->us); + if(otr->keygen) { + kill(otr->keygen, SIGTERM); + waitpid(otr->keygen, NULL, 0); + /* TODO: remove stale keygen tempfiles */ + } + if(otr->to) + fclose(otr->to); + if(otr->from) + fclose(otr->from); + while(otr->todo) { + kg_t *p = otr->todo; + otr->todo = p->next; + g_free(p); + } + g_free(otr); +} + void otr_load(irc_t *irc) { char s[512]; @@ -200,12 +233,12 @@ void otr_load(irc_t *irc) log_message(LOGLVL_DEBUG, "otr_load '%s'", irc->nick); g_snprintf(s, 511, "%s%s.otr_keys", global.conf->configdir, irc->nick); - e = otrl_privkey_read(irc->otr_us, s); + e = otrl_privkey_read(irc->otr->us, s); if(e && e!=enoent) { irc_usermsg(irc, "otr load: %s: %s", s, gcry_strerror(e)); } g_snprintf(s, 511, "%s%s.otr_fprints", global.conf->configdir, irc->nick); - e = otrl_privkey_read_fingerprints(irc->otr_us, s, NULL, NULL); + e = otrl_privkey_read_fingerprints(irc->otr->us, s, NULL, NULL); if(e && e!=enoent) { irc_usermsg(irc, "otr load: %s: %s", s, gcry_strerror(e)); } @@ -224,7 +257,7 @@ void otr_save(irc_t *irc) log_message(LOGLVL_DEBUG, "otr_save '%s'", irc->nick); g_snprintf(s, 511, "%s%s.otr_fprints", global.conf->configdir, irc->nick); - e = otrl_privkey_write_fingerprints(irc->otr_us, s); + e = otrl_privkey_write_fingerprints(irc->otr->us, s); if(e) { irc_usermsg(irc, "otr save: %s: %s", s, gcry_strerror(e)); } @@ -262,10 +295,11 @@ void otr_check_for_key(account_t *a) irc_t *irc = a->irc; OtrlPrivKey *k; - k = otrl_privkey_find(irc->otr_us, a->user, a->prpl->name); + k = otrl_privkey_find(irc->otr->us, a->user, a->prpl->name); if(k) { - irc_usermsg(irc, "otr: %s/%s ready", - a->user, a->prpl->name); + irc_usermsg(irc, "otr: %s/%s ready", a->user, a->prpl->name); + } if(keygen_in_progress(irc, a->user, a->prpl->name)) { + irc_usermsg(irc, "otr: keygen for %s/%s in progress", a->user, a->prpl->name); } else { otr_keygen(irc, a->user, a->prpl->name); } @@ -278,7 +312,7 @@ char *otr_handle_message(struct im_connection *ic, const char *handle, const cha OtrlTLV *tlvs = NULL; char *colormsg; - ignore_msg = otrl_message_receiving(ic->irc->otr_us, &global.otr_ops, ic, + ignore_msg = otrl_message_receiving(ic->irc->otr->us, &global.otr_ops, ic, ic->acc->user, ic->acc->prpl->name, handle, msg, &newmsg, &tlvs, NULL, NULL); @@ -292,7 +326,7 @@ char *otr_handle_message(struct im_connection *ic, const char *handle, const cha return g_strdup(msg); } else { /* OTR has processed this message */ - ConnContext *context = otrl_context_find(ic->irc->otr_us, handle, + ConnContext *context = otrl_context_find(ic->irc->otr->us, handle, ic->acc->user, ic->acc->prpl->name, 0, NULL, NULL, NULL); if(context && context->msgstate == OTRL_MSGSTATE_ENCRYPTED && set_getbool(&ic->irc->set, "color_encrypted")) { @@ -318,14 +352,14 @@ int otr_send_message(struct im_connection *ic, const char *handle, const char *m char *otrmsg = NULL; ConnContext *ctx = NULL; - st = otrl_message_sending(ic->irc->otr_us, &global.otr_ops, ic, + st = otrl_message_sending(ic->irc->otr->us, &global.otr_ops, ic, ic->acc->user, ic->acc->prpl->name, handle, msg, NULL, &otrmsg, NULL, NULL); if(st) { return st; } - ctx = otrl_context_find(ic->irc->otr_us, + ctx = otrl_context_find(ic->irc->otr->us, handle, ic->acc->user, ic->acc->prpl->name, 1, NULL, NULL, NULL); @@ -383,6 +417,14 @@ OtrlPolicy op_policy(void *opdata, ConnContext *context) { struct im_connection *ic = check_imc(opdata, context->accountname, context->protocol); const char *p; + + log_message(LOGLVL_DEBUG, "op_policy '%s' '%s'", context->accountname, context->protocol); + + /* policy override during keygen: if we're missing the key for context but are currently + generating it, then that's as much as we can do. => temporarily return NEVER. */ + if(keygen_in_progress(ic->irc, context->accountname, context->protocol) && + !otrl_privkey_find(ic->irc->otr->us, context->accountname, context->protocol)) + return OTRL_POLICY_NEVER; p = set_getstr(&ic->irc->set, "otr_policy"); if(!strcmp(p, "never")) @@ -401,13 +443,11 @@ void op_create_privkey(void *opdata, const char *accountname, const char *protocol) { struct im_connection *ic = check_imc(opdata, accountname, protocol); - char *s; log_message(LOGLVL_DEBUG, "op_create_privkey '%s' '%s'", accountname, protocol); - s = g_strdup_printf("oops, no otr privkey for %s - generate one now?", - accountname); - query_add(ic->irc, ic, s, yes_keygen, NULL, ic->acc); + /* will fail silently if keygen already in progress */ + otr_keygen(ic->irc, accountname, protocol); } int op_is_logged_in(void *opdata, const char *accountname, @@ -595,13 +635,13 @@ void cmd_otr_disconnect(irc_t *irc, char **args) return; } - otrl_message_disconnect(irc->otr_us, &global.otr_ops, + otrl_message_disconnect(irc->otr->us, &global.otr_ops, u->ic, u->ic->acc->user, u->ic->acc->prpl->name, u->handle); /* for some reason, libotr (3.1.0) doesn't do this itself: */ if(u->encrypted) { ConnContext *ctx; - ctx = otrl_context_find(irc->otr_us, u->handle, u->ic->acc->user, + ctx = otrl_context_find(irc->otr->us, u->handle, u->ic->acc->user, u->ic->acc->prpl->name, 0, NULL, NULL, NULL); if(ctx) op_gone_insecure(u->ic, ctx); @@ -642,7 +682,7 @@ void cmd_otr_smp(irc_t *irc, char **args) return; } - ctx = otrl_context_find(irc->otr_us, u->handle, + ctx = otrl_context_find(irc->otr->us, u->handle, u->ic->acc->user, u->ic->acc->prpl->name, 1, NULL, NULL, NULL); if(!ctx) { /* huh? out of memory or what? */ @@ -653,7 +693,7 @@ void cmd_otr_smp(irc_t *irc, char **args) log_message(LOGLVL_INFO, "SMP already in phase %d, sending abort before reinitiating", ctx->smstate->nextExpected+1); - otrl_message_abort_smp(irc->otr_us, &global.otr_ops, u->ic, ctx); + otrl_message_abort_smp(irc->otr->us, &global.otr_ops, u->ic, ctx); otrl_sm_state_free(ctx->smstate); } @@ -661,14 +701,14 @@ void cmd_otr_smp(irc_t *irc, char **args) is completed or aborted! */ if(ctx->smstate->secret == NULL) { irc_usermsg(irc, "smp: initiating with %s...", u->nick); - otrl_message_initiate_smp(irc->otr_us, &global.otr_ops, + otrl_message_initiate_smp(irc->otr->us, &global.otr_ops, u->ic, ctx, (unsigned char *)args[2], strlen(args[2])); /* smp is now in EXPECT2 */ } else { /* if we're still in EXPECT1 but smstate is initialized, we must have received the SMP1, so let's issue a response */ irc_usermsg(irc, "smp: responding to %s...", u->nick); - otrl_message_respond_smp(irc->otr_us, &global.otr_ops, + otrl_message_respond_smp(irc->otr->us, &global.otr_ops, u->ic, ctx, (unsigned char *)args[2], strlen(args[2])); /* smp is now in EXPECT3 */ } @@ -688,7 +728,7 @@ void cmd_otr_trust(irc_t *irc, char **args) return; } - ctx = otrl_context_find(irc->otr_us, u->handle, + ctx = otrl_context_find(irc->otr->us, u->handle, u->ic->acc->user, u->ic->acc->prpl->name, 0, NULL, NULL, NULL); if(!ctx) { irc_usermsg(irc, "%s: no otr context with user", args[1]); @@ -752,7 +792,7 @@ void cmd_otr_info(irc_t *irc, char **args) if(protocol && myhandle) { *(myhandle++) = '\0'; handle = arg; - ctx = otrl_context_find(irc->otr_us, handle, myhandle, protocol, 0, NULL, NULL, NULL); + ctx = otrl_context_find(irc->otr->us, handle, myhandle, protocol, 0, NULL, NULL, NULL); if(!ctx) { irc_usermsg(irc, "no such context"); g_free(arg); @@ -765,7 +805,7 @@ void cmd_otr_info(irc_t *irc, char **args) g_free(arg); return; } - ctx = otrl_context_find(irc->otr_us, u->handle, u->ic->acc->user, + ctx = otrl_context_find(irc->otr->us, u->handle, u->ic->acc->user, u->ic->acc->prpl->name, 0, NULL, NULL, NULL); if(!ctx) { irc_usermsg(irc, "no otr context with %s", args[1]); @@ -802,7 +842,12 @@ void cmd_otr_keygen(irc_t *irc, char **args) return; } - if(otrl_privkey_find(irc->otr_us, a->user, a->prpl->name)) { + if(keygen_in_progress(irc, a->user, a->prpl->name)) { + irc_usermsg(irc, "keygen for account %d already in progress", n); + return; + } + + if(otrl_privkey_find(irc->otr->us, a->user, a->prpl->name)) { char *s = g_strdup_printf("account %d already has a key, replace it?", n); query_add(irc, NULL, s, yes_keygen, NULL, a); } else { @@ -870,7 +915,7 @@ void cmd_otr_forget(irc_t *irc, char **args) return; } - ctx = otrl_context_find(irc->otr_us, u->handle, u->ic->acc->user, + ctx = otrl_context_find(irc->otr->us, u->handle, u->ic->acc->user, u->ic->acc->prpl->name, 0, NULL, NULL, NULL); if(!ctx) { irc_usermsg(irc, "no otr context with %s", args[2]); @@ -906,7 +951,7 @@ void cmd_otr_forget(irc_t *irc, char **args) return; } - ctx = otrl_context_find(irc->otr_us, u->handle, u->ic->acc->user, + ctx = otrl_context_find(irc->otr->us, u->handle, u->ic->acc->user, u->ic->acc->prpl->name, 0, NULL, NULL, NULL); if(!ctx) { irc_usermsg(irc, "no otr context with %s", args[2]); @@ -957,7 +1002,7 @@ void cmd_otr_forget(irc_t *irc, char **args) void otr_handle_smp(struct im_connection *ic, const char *handle, OtrlTLV *tlvs) { irc_t *irc = ic->irc; - OtrlUserState us = irc->otr_us; + OtrlUserState us = irc->otr->us; OtrlMessageAppOps *ops = &global.otr_ops; OtrlTLV *tlv = NULL; ConnContext *context; @@ -1306,8 +1351,8 @@ OtrlPrivKey *match_privkey(irc_t *irc, const char **args) /* find first key which matches the given prefix */ n = strlen(prefix); - for(k=irc->otr_us->privkey_root; k; k=k->next) { - p = otrl_privkey_fingerprint(irc->otr_us, human, k->accountname, k->protocol); + for(k=irc->otr->us->privkey_root; k; k=k->next) { + p = otrl_privkey_fingerprint(irc->otr->us, human, k->accountname, k->protocol); if(!p) /* gah! :-P */ continue; if(!strncmp(prefix, human, n)) @@ -1320,7 +1365,7 @@ OtrlPrivKey *match_privkey(irc_t *irc, const char **args) /* make sure the match, if any, is unique */ for(k2=k->next; k2; k2=k2->next) { - p = otrl_privkey_fingerprint(irc->otr_us, human, k2->accountname, k2->protocol); + p = otrl_privkey_fingerprint(irc->otr->us, human, k2->accountname, k2->protocol); if(!p) /* gah! :-P */ continue; if(!strncmp(prefix, human, n)) @@ -1342,7 +1387,7 @@ void show_general_otr_info(irc_t *irc) /* list all privkeys */ irc_usermsg(irc, "\x1fprivate keys:\x1f"); - for(key=irc->otr_us->privkey_root; key; key=key->next) { + for(key=irc->otr->us->privkey_root; key; key=key->next) { const char *hash; switch(key->pubkey_type) { @@ -1357,7 +1402,7 @@ void show_general_otr_info(irc_t *irc) /* No, it doesn't make much sense to search for the privkey again by account/protocol, but libotr currently doesn't provide a direct routine for hashing a given 'OtrlPrivKey'... */ - hash = otrl_privkey_fingerprint(irc->otr_us, human, key->accountname, key->protocol); + hash = otrl_privkey_fingerprint(irc->otr->us, human, key->accountname, key->protocol); if(hash) /* should always succeed */ irc_usermsg(irc, " %s", human); } @@ -1365,7 +1410,7 @@ void show_general_otr_info(irc_t *irc) /* list all contexts */ irc_usermsg(irc, "%s", ""); irc_usermsg(irc, "\x1f" "connection contexts:\x1f (bold=currently encrypted)"); - for(ctx=irc->otr_us->context_root; ctx; ctx=ctx->next) {\ + for(ctx=irc->otr->us->context_root; ctx; ctx=ctx->next) {\ user_t *u; char *userstring; @@ -1424,13 +1469,41 @@ void show_otr_context_info(irc_t *irc, ConnContext *ctx) show_fingerprints(irc, ctx); } +int keygen_in_progress(irc_t *irc, const char *handle, const char *protocol) +{ + kg_t *kg; + + log_message(LOGLVL_DEBUG, "keygen_in_progress '%s' '%s'", handle, protocol); + + if(!irc->otr->sent_accountname || !irc->otr->sent_protocol) + return 0; + + /* are we currently working on this key? */ + if(!strcmp(handle, irc->otr->sent_accountname) && + !strcmp(protocol, irc->otr->sent_protocol)) + return 1; + + /* do we have it queued for later? */ + for(kg=irc->otr->todo; kg; kg=kg->next) { + if(!strcmp(handle, kg->accountname) && + !strcmp(protocol, kg->protocol)) + return 1; + } + + return 0; +} + void otr_keygen(irc_t *irc, const char *handle, const char *protocol) { + /* do nothing if a key for the requested account is already being generated */ + if(keygen_in_progress(irc, handle, protocol)) + return; + irc_usermsg(irc, "generating new private key for %s/%s...", handle, protocol); /* see if we already have a keygen child running. if not, start one and put a handler on its output. */ - if(!irc->otr_keygen || waitpid(irc->otr_keygen, NULL, WNOHANG)) { + if(!irc->otr->keygen || waitpid(irc->otr->keygen, NULL, WNOHANG)) { pid_t p; int to[2], from[2]; FILE *tof, *fromf; @@ -1456,21 +1529,37 @@ void otr_keygen(irc_t *irc, const char *handle, const char *protocol) if(!p) { /* child process */ signal(SIGTERM, exit); - keygen_child_main(irc->otr_us, to[0], from[1]); + keygen_child_main(irc->otr->us, to[0], from[1]); exit(0); } - irc->otr_keygen = p; - irc->otr_to = tof; - irc->otr_from = fromf; - irc->otr_ntodo = 0; + irc->otr->keygen = p; + irc->otr->to = tof; + irc->otr->from = fromf; + irc->otr->sent_accountname = NULL; + irc->otr->sent_protocol = NULL; + irc->otr->todo = NULL; b_input_add(from[0], GAIM_INPUT_READ, keygen_finish_handler, irc); } - /* send accountname and protocol for the new key to our keygen slave */ - fprintf(irc->otr_to, "%s\n%s\n", handle, protocol); - fflush(irc->otr_to); - irc->otr_ntodo++; + /* is the keygen slave currently working? */ + if(irc->otr->sent_accountname) { + /* enqueue our job for later transmission */ + log_message(LOGLVL_DEBUG, "enqueueing keygen for %s/%s", handle, protocol); + kg_t **kg = &irc->otr->todo; + while(*kg) + kg=&((*kg)->next); + *kg = g_new0(kg_t, 1); + (*kg)->accountname = handle; + (*kg)->protocol = protocol; + } else { + /* send our job over and remember it */ + log_message(LOGLVL_DEBUG, "slave: generate for %s/%s!", handle, protocol); + fprintf(irc->otr->to, "%s\n%s\n", handle, protocol); + fflush(irc->otr->to); + irc->otr->sent_accountname = handle; + irc->otr->sent_protocol = protocol; + } } void keygen_child_main(OtrlUserState us, int infd, int outfd) @@ -1514,8 +1603,8 @@ gboolean keygen_finish_handler(gpointer data, gint fd, b_input_condition cond) log_message(LOGLVL_DEBUG, "keygen_finish_handler cond=%d", cond); - myfgets(filename, 512, irc->otr_from); - myfgets(msg, 512, irc->otr_from); + myfgets(filename, 512, irc->otr->from); + myfgets(msg, 512, irc->otr->from); log_message(LOGLVL_DEBUG, "filename='%s'", filename); log_message(LOGLVL_DEBUG, "msg='%s'", msg); @@ -1527,23 +1616,36 @@ gboolean keygen_finish_handler(gpointer data, gint fd, b_input_condition cond) copyfile(filename, tmp); unlink(filename); rename(tmp,kf); - otrl_privkey_read(irc->otr_us, kf); + otrl_privkey_read(irc->otr->us, kf); g_free(kf); g_free(tmp); } - - irc->otr_ntodo--; - if(irc->otr_ntodo < 1) { - /* okay, we think the slave is idle now, so kill him */ + + /* forget this job */ + irc->otr->sent_accountname = NULL; + irc->otr->sent_protocol = NULL; + + /* see if there are any more in the queue */ + if(irc->otr->todo) { + kg_t *p = irc->otr->todo; + /* send the next one over */ + log_message(LOGLVL_DEBUG, "slave: keygen for %s/%s!", p->accountname, p->protocol); + fprintf(irc->otr->to, "%s\n%s\n", p->accountname, p->protocol); + fflush(irc->otr->to); + irc->otr->sent_accountname = p->accountname; + irc->otr->sent_protocol = p->protocol; + irc->otr->todo = p->next; + g_free(p); + return TRUE; /* keep watching */ + } else { + /* okay, the slave is idle now, so kill him */ log_message(LOGLVL_DEBUG, "all keys done. die, slave!"); - fclose(irc->otr_from); - fclose(irc->otr_to); - kill(irc->otr_keygen, SIGTERM); - waitpid(irc->otr_keygen, NULL, 0); - irc->otr_keygen = 0; + fclose(irc->otr->from); + fclose(irc->otr->to); + kill(irc->otr->keygen, SIGTERM); + waitpid(irc->otr->keygen, NULL, 0); + irc->otr->keygen = 0; return FALSE; /* unregister ourselves */ - } else { - return TRUE; /* slave still working, keep watching */ } } @@ -1580,7 +1682,12 @@ void yes_keygen(gpointer w, void *data) { account_t *acc = (account_t *)data; - otr_keygen(acc->irc, acc->user, acc->prpl->name); + if(keygen_in_progress(acc->irc, acc->user, acc->prpl->name)) { + irc_usermsg(acc->irc, "keygen for %s/%s already in progress", + acc->user, acc->prpl->name); + } else { + otr_keygen(acc->irc, acc->user, acc->prpl->name); + } } diff --git a/otr.h b/otr.h index 805c0eea..36e3b922 100644 --- a/otr.h +++ b/otr.h @@ -48,9 +48,36 @@ void cmd_otr(struct irc *, char **args); #include #include +/* representing a keygen job */ +typedef struct kg { + const char *accountname; + const char *protocol; + + struct kg *next; +} kg_t; + +/* struct to encapsulate our book keeping stuff */ +typedef struct otr { + OtrlUserState us; + pid_t keygen; /* pid of keygen slave (0 if none) */ + FILE *to; /* pipe to keygen slave */ + FILE *from; /* pipe from keygen slave */ + + /* active keygen job (NULL if none) */ + const char *sent_accountname; + const char *sent_protocol; + + /* keygen jobs waiting to be sent to slave */ + kg_t *todo; +} otr_t; + /* called from main() */ void otr_init(void); +/* called from irc_new()/irc_free() */ +otr_t *otr_new(); +void otr_free(otr_t *otr); + /* called by storage_* functions */ void otr_load(struct irc *irc); void otr_save(struct irc *irc); @@ -70,13 +97,12 @@ int otr_send_message(struct im_connection *ic, const char *handle, const char *m #else -typedef void *OtrlUserState; +typedef void otr_t; typedef void *OtrlMessageAppOps; -#define otrl_userstate_create() (NULL) -#define otrl_userstate_free(us) {} - #define otr_init() {} +#define otr_new() (NULL) +#define otr_free(otr) {} #define otr_load(irc) {} #define otr_save(irc) {} #define otr_remove(nick) {} -- cgit v1.2.3 From 3064ea42452e7e069bce9fc132ceb8ae4d7d11b4 Mon Sep 17 00:00:00 2001 From: Sven Moritz Hallberg Date: Sat, 16 Feb 2008 16:20:58 +0100 Subject: rework keygen messages and add some notices --- irc.c | 3 +++ otr.c | 38 +++++++++++++++++++++++++++++++------- otr.h | 2 +- root_commands.c | 4 +++- 4 files changed, 38 insertions(+), 9 deletions(-) diff --git a/irc.c b/irc.c index d73f5b4a..7ef653a1 100644 --- a/irc.c +++ b/irc.c @@ -787,6 +787,9 @@ void irc_login( irc_t *irc ) irc_spawn( irc, u ); irc_usermsg( irc, "Welcome to the BitlBee gateway!\n\nIf you've never used BitlBee before, please do read the help information using the \x02help\x02 command. Lots of FAQs are answered there." ); + #ifdef WITH_OTR + irc_usermsg( irc, "\nOTR users please note: Private key files are owned by the user BitlBee is running as." ); + #endif if( global.conf->runmode == RUNMODE_FORKDAEMON || global.conf->runmode == RUNMODE_DAEMON ) ipc_to_master_str( "CLIENT %s %s :%s\r\n", irc->host, irc->nick, irc->realname ); diff --git a/otr.c b/otr.c index 9535e8db..15f4225a 100644 --- a/otr.c +++ b/otr.c @@ -229,6 +229,7 @@ void otr_load(irc_t *irc) account_t *a; gcry_error_t e; gcry_error_t enoent = gcry_error_from_errno(ENOENT); + int kg=0; log_message(LOGLVL_DEBUG, "otr_load '%s'", irc->nick); @@ -245,7 +246,16 @@ void otr_load(irc_t *irc) /* check for otr keys on all accounts */ for(a=irc->accounts; a; a=a->next) { - otr_check_for_key(a); + kg = otr_check_for_key(a) || kg; + } + if(kg) { + irc_usermsg(irc, "Notice: " + "The accounts above do not have OTR encryption keys associated with them, yet. " + "These keys are now being generated in the background. " + "You will be notified as they are completed. " + "It is not necessary to wait; " + "BitlBee can be used normally during key generation. " + "You may safely ignore this message if you don't know what OTR is. ;)"); } } @@ -290,7 +300,7 @@ void otr_rename(const char *onick, const char *nnick) rename(s,t); } -void otr_check_for_key(account_t *a) +int otr_check_for_key(account_t *a) { irc_t *irc = a->irc; OtrlPrivKey *k; @@ -298,10 +308,14 @@ void otr_check_for_key(account_t *a) k = otrl_privkey_find(irc->otr->us, a->user, a->prpl->name); if(k) { irc_usermsg(irc, "otr: %s/%s ready", a->user, a->prpl->name); + return 0; } if(keygen_in_progress(irc, a->user, a->prpl->name)) { - irc_usermsg(irc, "otr: keygen for %s/%s in progress", a->user, a->prpl->name); + irc_usermsg(irc, "otr: keygen for %s/%s already in progress", a->user, a->prpl->name); + return 0; } else { + irc_usermsg(irc, "otr: starting background keygen for %s/%s", a->user, a->prpl->name); otr_keygen(irc, a->user, a->prpl->name); + return 1; } } @@ -1244,7 +1258,7 @@ void show_fingerprints(irc_t *irc, ConnContext *ctx) } } if(count==0) - irc_usermsg(irc, " no fingerprints"); + irc_usermsg(irc, " (none)"); } Fingerprint *match_fingerprint(irc_t *irc, ConnContext *ctx, const char **args) @@ -1384,8 +1398,9 @@ void show_general_otr_info(irc_t *irc) ConnContext *ctx; OtrlPrivKey *key; char human[45]; + kg_t *kg; - /* list all privkeys */ + /* list all privkeys (including ones being generated) */ irc_usermsg(irc, "\x1fprivate keys:\x1f"); for(key=irc->otr->us->privkey_root; key; key=key->next) { const char *hash; @@ -1406,6 +1421,12 @@ void show_general_otr_info(irc_t *irc) if(hash) /* should always succeed */ irc_usermsg(irc, " %s", human); } + for(kg=irc->otr->todo; kg; kg=kg->next) { + irc_usermsg(irc, " %s/%s - DSA", kg->accountname, kg->protocol); + irc_usermsg(irc, " (being generated)"); + } + if(key == irc->otr->us->privkey_root && kg == irc->otr->todo) + irc_usermsg(irc, " (none)"); /* list all contexts */ irc_usermsg(irc, "%s", ""); @@ -1430,6 +1451,8 @@ void show_general_otr_info(irc_t *irc) g_free(userstring); } + if(ctx == irc->otr->us->context_root) + irc_usermsg(irc, " (none)"); } void show_otr_context_info(irc_t *irc, ConnContext *ctx) @@ -1498,8 +1521,6 @@ void otr_keygen(irc_t *irc, const char *handle, const char *protocol) /* do nothing if a key for the requested account is already being generated */ if(keygen_in_progress(irc, handle, protocol)) return; - - irc_usermsg(irc, "generating new private key for %s/%s...", handle, protocol); /* see if we already have a keygen child running. if not, start one and put a handler on its output. */ @@ -1686,6 +1707,9 @@ void yes_keygen(gpointer w, void *data) irc_usermsg(acc->irc, "keygen for %s/%s already in progress", acc->user, acc->prpl->name); } else { + irc_usermsg(acc->irc, "starting background keygen for %s/%s", + acc->user, acc->prpl->name); + irc_usermsg(acc->irc, "you will be notified when it completes"); otr_keygen(acc->irc, acc->user, acc->prpl->name); } } diff --git a/otr.h b/otr.h index 36e3b922..e5dddbb1 100644 --- a/otr.h +++ b/otr.h @@ -85,7 +85,7 @@ void otr_remove(const char *nick); void otr_rename(const char *onick, const char *nnick); /* called from account_add() */ -void otr_check_for_key(struct account *a); +int otr_check_for_key(struct account *a); /* called from imcb_buddy_msg() */ char *otr_handle_message(struct im_connection *ic, const char *handle, diff --git a/root_commands.c b/root_commands.c index 9b492048..d47a8b1d 100644 --- a/root_commands.c +++ b/root_commands.c @@ -242,7 +242,9 @@ static void cmd_account( irc_t *irc, char **cmd ) irc_usermsg( irc, "Account successfully added" ); - otr_check_for_key(a); + if(otr_check_for_key(a)) { + irc_usermsg(irc, "otr: you will be notified when it completes"); + } } else if( g_strcasecmp( cmd[1], "del" ) == 0 ) { -- cgit v1.2.3 From 1221ef093f35d620123cab82aecf4bb7bcc2e86a Mon Sep 17 00:00:00 2001 From: Sven Moritz Hallberg Date: Sat, 16 Feb 2008 16:28:08 +0100 Subject: show keys being generated in 'otr info' --- otr.c | 11 +++++++++-- 1 file changed, 9 insertions(+), 2 deletions(-) diff --git a/otr.c b/otr.c index 15f4225a..460dcda0 100644 --- a/otr.c +++ b/otr.c @@ -1421,11 +1421,18 @@ void show_general_otr_info(irc_t *irc) if(hash) /* should always succeed */ irc_usermsg(irc, " %s", human); } + if(irc->otr->sent_accountname) { + irc_usermsg(irc, " %s/%s - DSA", irc->otr->sent_accountname, + irc->otr->sent_protocol); + irc_usermsg(irc, " (being generated)"); + } for(kg=irc->otr->todo; kg; kg=kg->next) { irc_usermsg(irc, " %s/%s - DSA", kg->accountname, kg->protocol); - irc_usermsg(irc, " (being generated)"); + irc_usermsg(irc, " (queued)"); } - if(key == irc->otr->us->privkey_root && kg == irc->otr->todo) + if(key == irc->otr->us->privkey_root && + !irc->otr->sent_accountname && + kg == irc->otr->todo) irc_usermsg(irc, " (none)"); /* list all contexts */ -- cgit v1.2.3 From 903a2fcc60f82f52fe05c79250e6875dc48f23f0 Mon Sep 17 00:00:00 2001 From: Sven Moritz Hallberg Date: Sat, 16 Feb 2008 17:24:38 +0100 Subject: remove (broken) 'otr forget key' command again --- doc/user-guide/commands.xml | 15 +-------------- otr.c | 31 ------------------------------- 2 files changed, 1 insertion(+), 45 deletions(-) diff --git a/doc/user-guide/commands.xml b/doc/user-guide/commands.xml index 8c2a30ca..2d6b866d 100644 --- a/doc/user-guide/commands.xml +++ b/doc/user-guide/commands.xml @@ -345,7 +345,7 @@ - Forgets some part of our OTR userstate. Available things: fingerprint, context, and key. See help otr forget <thing> for more information. + Forgets some part of our OTR userstate. Available things: fingerprint and context. See help otr forget <thing> for more information. @@ -376,19 +376,6 @@ - - otr forget key <fingerprint> - - - - - Forgets an OTR private key matching the specified fingerprint. It is allowed to specify only a (unique) prefix of the fingerprint. - - - - - - diff --git a/otr.c b/otr.c index 460dcda0..78fc688f 100644 --- a/otr.c +++ b/otr.c @@ -126,7 +126,6 @@ void myfgets(char *s, int size, FILE *stream); void yes_keygen(gpointer w, void *data); void yes_forget_fingerprint(gpointer w, void *data); void yes_forget_context(gpointer w, void *data); -void yes_forget_key(gpointer w, void *data); /* helper to make sure accountname and protocol match the incoming "opdata" */ struct im_connection *check_imc(void *opdata, const char *accountname, @@ -898,15 +897,6 @@ void yes_forget_context(gpointer w, void *data) otrl_context_forget(ctx); } -void yes_forget_key(gpointer w, void *data) -{ - OtrlPrivKey *key = (OtrlPrivKey *)data; - - /* FIXME: For some reason which /completely eludes me/, this call keeps - barfing on the gcry_sexp_release inside (invalid pointer free). */ - otrl_privkey_forget(key); -} - void cmd_otr_forget(irc_t *irc, char **args) { if(!strcmp(args[1], "fingerprint")) @@ -981,27 +971,6 @@ void cmd_otr_forget(irc_t *irc, char **args) query_add(irc, NULL, s, yes_forget_context, NULL, ctx); } - else if(!strcmp(args[1], "key")) - { - OtrlPrivKey *key; - char *s; - - key = match_privkey(irc, ((const char **)args)+2); - if(!key) { - /* match_privkey does error messages */ - return; - } - - /* TODO: Find out why 'otr forget key' barfs (cf. yes_forget_key) */ - irc_usermsg(irc, "otr %s %s: not implemented, please edit \x02%s%s.otr_keys\x02 manually :-/", - args[0], args[1], global.conf->configdir, irc->nick); - return; - - s = g_strdup_printf("about to forget the private key for %s/%s, are you sure?", - key->accountname, key->protocol); - query_add(irc, NULL, s, yes_forget_key, NULL, key); - } - else { irc_usermsg(irc, "otr %s: unknown subcommand \"%s\", see \x02help otr forget\x02", -- cgit v1.2.3 From 896195002cc903ec4b1ef7e1468f73c1dc08df9e Mon Sep 17 00:00:00 2001 From: Sven Moritz Hallberg Date: Sat, 16 Feb 2008 17:25:24 +0100 Subject: read root's welcome message from a file (like tho MOTD) --- Makefile | 2 ++ bitlbee.conf | 7 +++++++ conf.c | 6 ++++++ conf.h | 1 + irc.c | 28 ++++++++++++++++++++++++---- welcome.txt | 5 +++++ 6 files changed, 45 insertions(+), 4 deletions(-) create mode 100644 welcome.txt diff --git a/Makefile b/Makefile index c0d7aad9..10c671d9 100644 --- a/Makefile +++ b/Makefile @@ -84,10 +84,12 @@ uninstall-dev: install-etc: mkdir -p $(DESTDIR)$(ETCDIR) install -m 0644 motd.txt $(DESTDIR)$(ETCDIR)/motd.txt + install -m 0644 welcome.txt $(DESTDIR)$(ETCDIR)/welcome.txt install -m 0644 bitlbee.conf $(DESTDIR)$(ETCDIR)/bitlbee.conf uninstall-etc: rm -f $(DESTDIR)$(ETCDIR)/motd.txt + rm -f $(DESTDIR)$(ETCDIR)/welcome.txt rm -f $(DESTDIR)$(ETCDIR)/bitlbee.conf -rmdir $(DESTDIR)$(ETCDIR) diff --git a/bitlbee.conf b/bitlbee.conf index d9f878c8..d2d8b559 100644 --- a/bitlbee.conf +++ b/bitlbee.conf @@ -72,6 +72,13 @@ ## # MotdFile = /etc/bitlbee/motd.txt +## WelcomeFile +## +## Specify an alternative file for the welcome message displayed when joining the +## control channel. Default value depends on the --etcdir argument to configure. +## +# WelcomeFile = /etc/bitlbee/welcome.txt + ## ConfigDir ## ## Specify an alternative directory to store all the per-user configuration diff --git a/conf.c b/conf.c index 94c673cc..f87eb983 100644 --- a/conf.c +++ b/conf.c @@ -60,6 +60,7 @@ conf_t *conf_load( int argc, char *argv[] ) conf->plugindir = g_strdup( PLUGINDIR ); conf->pidfile = g_strdup( PIDFILE ); conf->motdfile = g_strdup( ETCDIR "/motd.txt" ); + conf->welcomefile = g_strdup( ETCDIR "/welcome.txt" ); conf->ping_interval = 180; conf->ping_timeout = 300; conf->user = NULL; @@ -240,6 +241,11 @@ static int conf_loadini( conf_t *conf, char *file ) g_free( conf->motdfile ); conf->motdfile = g_strdup( ini->value ); } + else if( g_strcasecmp( ini->key, "welcomefile" ) == 0 ) + { + g_free( conf->welcomefile ); + conf->welcomefile = g_strdup( ini->value ); + } else if( g_strcasecmp( ini->key, "account_storage" ) == 0 ) { g_free( conf->primary_storage ); diff --git a/conf.h b/conf.h index d21ec577..75c5ddb4 100644 --- a/conf.h +++ b/conf.h @@ -44,6 +44,7 @@ typedef struct conf char *plugindir; char *pidfile; char *motdfile; + char *welcomefile; char *primary_storage; char **migrate_storage; int ping_interval; diff --git a/irc.c b/irc.c index 7ef653a1..3ea0d755 100644 --- a/irc.c +++ b/irc.c @@ -31,6 +31,7 @@ #include static gboolean irc_userping( gpointer _irc, int fd, b_input_condition cond ); +static void irc_welcome( irc_t *irc ); GSList *irc_connection_list = NULL; @@ -786,10 +787,7 @@ void irc_login( irc_t *irc ) u->online = 1; irc_spawn( irc, u ); - irc_usermsg( irc, "Welcome to the BitlBee gateway!\n\nIf you've never used BitlBee before, please do read the help information using the \x02help\x02 command. Lots of FAQs are answered there." ); - #ifdef WITH_OTR - irc_usermsg( irc, "\nOTR users please note: Private key files are owned by the user BitlBee is running as." ); - #endif + irc_welcome( irc ); if( global.conf->runmode == RUNMODE_FORKDAEMON || global.conf->runmode == RUNMODE_DAEMON ) ipc_to_master_str( "CLIENT %s %s :%s\r\n", irc->host, irc->nick, irc->realname ); @@ -797,6 +795,28 @@ void irc_login( irc_t *irc ) irc->status |= USTATUS_LOGGED_IN; } +static void irc_welcome( irc_t *irc ) +{ + FILE *f; + + f = fopen( global.conf->welcomefile, "r" ); + if( !f ) + { + irc_usermsg( irc, "Welcome to the BitlBee gateway!\n\nIf you've never used BitlBee before, please do read the help information using the \x02help\x02 command. Lots of FAQs are answered there.\n\nOTR users please note: Private key files are owned by the user BitlBee is running as." ); + } + else + { + char linebuf[380]; + + while( fgets( linebuf, 380, f ) ) + { + irc_usermsg( irc, linebuf ); + } + + fclose( f ); + } +} + void irc_motd( irc_t *irc ) { int fd; diff --git a/welcome.txt b/welcome.txt new file mode 100644 index 00000000..3216efc0 --- /dev/null +++ b/welcome.txt @@ -0,0 +1,5 @@ +Welcome to the BitlBee gateway! + +If you've never used BitlBee before, please do read the help information using the help command. Lots of FAQs are answered there. + +OTR users please note: Private key files are owned by the user BitlBee is running as. -- cgit v1.2.3 From fd9fa52e0014459079444bd7bfff7a40eef4e27a Mon Sep 17 00:00:00 2001 From: Sven Moritz Hallberg Date: Sat, 16 Feb 2008 18:17:29 +0100 Subject: indent fingerprints in 'otr info ' --- otr.c | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/otr.c b/otr.c index 78fc688f..d30c8c93 100644 --- a/otr.c +++ b/otr.c @@ -1221,13 +1221,13 @@ void show_fingerprints(irc_t *irc, ConnContext *ctx) trust=fp->trust; } if(fp == ctx->active_fingerprint) { - irc_usermsg(irc, " \x02%s (%s)\x02", human, trust); + irc_usermsg(irc, " \x02%s (%s)\x02", human, trust); } else { - irc_usermsg(irc, " %s (%s)", human, trust); + irc_usermsg(irc, " %s (%s)", human, trust); } } if(count==0) - irc_usermsg(irc, " (none)"); + irc_usermsg(irc, " (none)"); } Fingerprint *match_fingerprint(irc_t *irc, ConnContext *ctx, const char **args) -- cgit v1.2.3 From ba5add72f824504a21eb780cae638c3ea2166ba0 Mon Sep 17 00:00:00 2001 From: Sven Moritz Hallberg Date: Sun, 17 Feb 2008 02:39:39 +0100 Subject: explicitly initialize ssl in order to avoid gnutls and libotr fighting over the global state of libgcrypt --- lib/ssl_bogus.c | 4 ++++ lib/ssl_client.h | 3 +++ lib/ssl_gnutls.c | 11 ++++++++--- lib/ssl_nss.c | 12 +++++++++--- lib/ssl_openssl.c | 9 +++++++-- otr.c | 23 ++++++++++++++++------- otr.h | 8 ++++---- unix.c | 7 +++++++ 8 files changed, 58 insertions(+), 19 deletions(-) diff --git a/lib/ssl_bogus.c b/lib/ssl_bogus.c index 5bae3496..391e634a 100644 --- a/lib/ssl_bogus.c +++ b/lib/ssl_bogus.c @@ -27,6 +27,10 @@ int ssl_errno; +void ssl_init( void ) +{ +} + void *ssl_connect( char *host, int port, ssl_input_function func, gpointer data ) { return( NULL ); diff --git a/lib/ssl_client.h b/lib/ssl_client.h index dcbf9a01..44fd658c 100644 --- a/lib/ssl_client.h +++ b/lib/ssl_client.h @@ -46,6 +46,9 @@ extern int ssl_errno; typedef gboolean (*ssl_input_function)(gpointer, void*, b_input_condition); +/* Perform any global initialization the SSL library might need. */ +G_MODULE_EXPORT void ssl_init( void ); + /* Connect to host:port, call the given function when the connection is ready to be used for SSL traffic. This is all done asynchronously, no blocking I/O! (Except for the DNS lookups, for now...) */ diff --git a/lib/ssl_gnutls.c b/lib/ssl_gnutls.c index b964ab49..ae6f46a4 100644 --- a/lib/ssl_gnutls.c +++ b/lib/ssl_gnutls.c @@ -60,6 +60,13 @@ static gboolean ssl_starttls_real( gpointer data, gint source, b_input_condition static gboolean ssl_handshake( gpointer data, gint source, b_input_condition cond ); +void ssl_init( void ) +{ + gnutls_global_init(); + initialized = TRUE; + atexit( gnutls_global_deinit ); +} + void *ssl_connect( char *host, int port, ssl_input_function func, gpointer data ) { struct scd *conn = g_new0( struct scd, 1 ); @@ -121,9 +128,7 @@ static gboolean ssl_connected( gpointer data, gint source, b_input_condition con if( !initialized ) { - gnutls_global_init(); - initialized = TRUE; - atexit( gnutls_global_deinit ); + ssl_init(); } gnutls_certificate_allocate_credentials( &conn->xcred ); diff --git a/lib/ssl_nss.c b/lib/ssl_nss.c index 218b3a80..16560e63 100644 --- a/lib/ssl_nss.c +++ b/lib/ssl_nss.c @@ -90,6 +90,14 @@ static SECStatus nss_bad_cert (void *arg, PRFileDesc *socket) } +void ssl_init( void ) +{ + PR_Init( PR_SYSTEM_THREAD, PR_PRIORITY_NORMAL, 1); + NSS_NoDB_Init(NULL); + NSS_SetDomesticPolicy(); + initialized = TRUE; +} + void *ssl_connect( char *host, int port, ssl_input_function func, gpointer data ) { struct scd *conn = g_new0( struct scd, 1 ); @@ -106,9 +114,7 @@ void *ssl_connect( char *host, int port, ssl_input_function func, gpointer data if( !initialized ) { - PR_Init( PR_SYSTEM_THREAD, PR_PRIORITY_NORMAL, 1); - NSS_NoDB_Init(NULL); - NSS_SetDomesticPolicy(); + ssl_init(); } diff --git a/lib/ssl_openssl.c b/lib/ssl_openssl.c index b1ba1db9..e54b21ee 100644 --- a/lib/ssl_openssl.c +++ b/lib/ssl_openssl.c @@ -56,6 +56,12 @@ static gboolean ssl_starttls_real( gpointer data, gint source, b_input_condition static gboolean ssl_handshake( gpointer data, gint source, b_input_condition cond ); +void ssl_init( void ); +{ + initialized = TRUE; + SSLeay_add_ssl_algorithms(); +} + void *ssl_connect( char *host, int port, ssl_input_function func, gpointer data ) { struct scd *conn = g_new0( struct scd, 1 ); @@ -114,8 +120,7 @@ static gboolean ssl_connected( gpointer data, gint source, b_input_condition con if( !initialized ) { - initialized = TRUE; - SSLeay_add_ssl_algorithms(); + ssl_init(); } meth = TLSv1_client_method(); diff --git a/otr.c b/otr.c index d30c8c93..418da177 100644 --- a/otr.c +++ b/otr.c @@ -111,7 +111,7 @@ int keygen_in_progress(irc_t *irc, const char *handle, const char *protocol); void otr_keygen(irc_t *irc, const char *handle, const char *protocol); /* main function for the forked keygen slave */ -void keygen_child_main(OtrlUserState us, int infd, int outfd); +void keygen_child_main(const char *nick, int infd, int outfd); /* mainloop handler for when a keygen finishes */ gboolean keygen_finish_handler(gpointer data, gint fd, b_input_condition cond); @@ -1526,7 +1526,7 @@ void otr_keygen(irc_t *irc, const char *handle, const char *protocol) if(!p) { /* child process */ signal(SIGTERM, exit); - keygen_child_main(irc->otr->us, to[0], from[1]); + keygen_child_main(irc->nick, to[0], from[1]); exit(0); } @@ -1547,25 +1547,32 @@ void otr_keygen(irc_t *irc, const char *handle, const char *protocol) while(*kg) kg=&((*kg)->next); *kg = g_new0(kg_t, 1); - (*kg)->accountname = handle; - (*kg)->protocol = protocol; + (*kg)->accountname = g_strdup(handle); + (*kg)->protocol = g_strdup(protocol); } else { /* send our job over and remember it */ log_message(LOGLVL_DEBUG, "slave: generate for %s/%s!", handle, protocol); fprintf(irc->otr->to, "%s\n%s\n", handle, protocol); fflush(irc->otr->to); - irc->otr->sent_accountname = handle; - irc->otr->sent_protocol = protocol; + irc->otr->sent_accountname = g_strdup(handle); + irc->otr->sent_protocol = g_strdup(protocol); } } -void keygen_child_main(OtrlUserState us, int infd, int outfd) +void keygen_child_main(const char *nick, int infd, int outfd) { + OtrlUserState us; + char *kf; FILE *input, *output; char filename[128], accountname[512], protocol[512]; gcry_error_t e; int tempfd; + us = otrl_userstate_create(); + kf = g_strdup_printf("%s%s.otr_keys", global.conf->configdir, nick); + otrl_privkey_read(us, kf); + g_free(kf); + input = fdopen(infd, "r"); output = fdopen(outfd, "w"); @@ -1619,6 +1626,8 @@ gboolean keygen_finish_handler(gpointer data, gint fd, b_input_condition cond) } /* forget this job */ + g_free(irc->otr->sent_accountname); + g_free(irc->otr->sent_protocol); irc->otr->sent_accountname = NULL; irc->otr->sent_protocol = NULL; diff --git a/otr.h b/otr.h index e5dddbb1..ca45c1e9 100644 --- a/otr.h +++ b/otr.h @@ -50,8 +50,8 @@ void cmd_otr(struct irc *, char **args); /* representing a keygen job */ typedef struct kg { - const char *accountname; - const char *protocol; + char *accountname; + char *protocol; struct kg *next; } kg_t; @@ -64,8 +64,8 @@ typedef struct otr { FILE *from; /* pipe from keygen slave */ /* active keygen job (NULL if none) */ - const char *sent_accountname; - const char *sent_protocol; + char *sent_accountname; + char *sent_protocol; /* keygen jobs waiting to be sent to slave */ kg_t *todo; diff --git a/unix.c b/unix.c index 07cb709f..cbd5c15d 100644 --- a/unix.c +++ b/unix.c @@ -30,6 +30,7 @@ #include "protocols/nogaim.h" #include "help.h" #include "ipc.h" +#include "lib/ssl_client.h" #include #include #include @@ -54,6 +55,12 @@ int main( int argc, char *argv[], char **envp ) b_main_init(); nogaim_init(); + /* Ugly Note: libotr and gnutls both use libgcrypt. libgcrypt + has a process-global config state whose initialization happpens + twice if libotr and gnutls are used together. libotr installs custom + memory management functions for libgcrypt while our gnutls module + uses the defaults. Therefore we initialize OTR after SSL. *sigh* */ + ssl_init(); otr_init(); srand( time( NULL ) ^ getpid() ); -- cgit v1.2.3 From 82e8fe8f36b0c0c53389358dca184f6d12184933 Mon Sep 17 00:00:00 2001 From: Sven Moritz Hallberg Date: Sun, 17 Feb 2008 02:50:23 +0100 Subject: free query strings after query_add --- otr.c | 3 +++ 1 file changed, 3 insertions(+) diff --git a/otr.c b/otr.c index 418da177..4d465e71 100644 --- a/otr.c +++ b/otr.c @@ -863,6 +863,7 @@ void cmd_otr_keygen(irc_t *irc, char **args) if(otrl_privkey_find(irc->otr->us, a->user, a->prpl->name)) { char *s = g_strdup_printf("account %d already has a key, replace it?", n); query_add(irc, NULL, s, yes_keygen, NULL, a); + g_free(s); } else { otr_keygen(irc, a->user, a->prpl->name); } @@ -940,6 +941,7 @@ void cmd_otr_forget(irc_t *irc, char **args) otrl_privkey_hash_to_human(human, fp->fingerprint); s = g_strdup_printf("about to forget fingerprint %s, are you sure?", human); query_add(irc, NULL, s, yes_forget_fingerprint, NULL, fp); + g_free(s); } else if(!strcmp(args[1], "context")) @@ -969,6 +971,7 @@ void cmd_otr_forget(irc_t *irc, char **args) s = g_strdup_printf("about to forget otr data about %s, are you sure?", args[2]); query_add(irc, NULL, s, yes_forget_context, NULL, ctx); + g_free(s); } else -- cgit v1.2.3 From d0faf626e98cf8a332afac5ac7d61c80dd8d3064 Mon Sep 17 00:00:00 2001 From: Sven Moritz Hallberg Date: Sun, 17 Feb 2008 02:58:41 +0100 Subject: put 'otr forget key' back in, which now works --- doc/user-guide/commands.xml | 15 ++++++++++++++- otr.c | 25 +++++++++++++++++++++++++ 2 files changed, 39 insertions(+), 1 deletion(-) diff --git a/doc/user-guide/commands.xml b/doc/user-guide/commands.xml index 2d6b866d..8c2a30ca 100644 --- a/doc/user-guide/commands.xml +++ b/doc/user-guide/commands.xml @@ -345,7 +345,7 @@ - Forgets some part of our OTR userstate. Available things: fingerprint and context. See help otr forget <thing> for more information. + Forgets some part of our OTR userstate. Available things: fingerprint, context, and key. See help otr forget <thing> for more information. @@ -376,6 +376,19 @@ + + otr forget key <fingerprint> + + + + + Forgets an OTR private key matching the specified fingerprint. It is allowed to specify only a (unique) prefix of the fingerprint. + + + + + + diff --git a/otr.c b/otr.c index 4d465e71..d69dffaf 100644 --- a/otr.c +++ b/otr.c @@ -126,6 +126,7 @@ void myfgets(char *s, int size, FILE *stream); void yes_keygen(gpointer w, void *data); void yes_forget_fingerprint(gpointer w, void *data); void yes_forget_context(gpointer w, void *data); +void yes_forget_key(gpointer w, void *data); /* helper to make sure accountname and protocol match the incoming "opdata" */ struct im_connection *check_imc(void *opdata, const char *accountname, @@ -898,6 +899,13 @@ void yes_forget_context(gpointer w, void *data) otrl_context_forget(ctx); } +void yes_forget_key(gpointer w, void *data) +{ + OtrlPrivKey *key = (OtrlPrivKey *)data; + + otrl_privkey_forget(key); +} + void cmd_otr_forget(irc_t *irc, char **args) { if(!strcmp(args[1], "fingerprint")) @@ -974,6 +982,23 @@ void cmd_otr_forget(irc_t *irc, char **args) g_free(s); } + else if(!strcmp(args[1], "key")) + { + OtrlPrivKey *key; + char *s; + + key = match_privkey(irc, ((const char **)args)+2); + if(!key) { + /* match_privkey does error messages */ + return; + } + + s = g_strdup_printf("about to forget the private key for %s/%s, are you sure?", + key->accountname, key->protocol); + query_add(irc, NULL, s, yes_forget_key, NULL, key); + g_free(s); + } + else { irc_usermsg(irc, "otr %s: unknown subcommand \"%s\", see \x02help otr forget\x02", -- cgit v1.2.3 From 37bff517e244def9f87a711a0154b182c89f07b3 Mon Sep 17 00:00:00 2001 From: Sven Moritz Hallberg Date: Sun, 17 Feb 2008 03:05:23 +0100 Subject: add TODO note about erasing forgotten keys --- otr.c | 3 +++ 1 file changed, 3 insertions(+) diff --git a/otr.c b/otr.c index d69dffaf..e675dc25 100644 --- a/otr.c +++ b/otr.c @@ -904,6 +904,9 @@ void yes_forget_key(gpointer w, void *data) OtrlPrivKey *key = (OtrlPrivKey *)data; otrl_privkey_forget(key); + /* Hm, libotr doesn't seem to offer a function for explicitly /writing/ + keyfiles. So the key will be back on the next load... */ + /* TODO: Actually erase forgotten keys from storage? */ } void cmd_otr_forget(irc_t *irc, char **args) -- cgit v1.2.3 From d858d2196a11910382d879527d117c8a181177fc Mon Sep 17 00:00:00 2001 From: Sven Moritz Hallberg Date: Sun, 17 Feb 2008 03:12:42 +0100 Subject: remove debugging output --- bitlbee.c | 3 --- otr.c | 50 -------------------------------------------------- 2 files changed, 53 deletions(-) diff --git a/bitlbee.c b/bitlbee.c index 52acafa4..59a417f0 100644 --- a/bitlbee.c +++ b/bitlbee.c @@ -142,9 +142,6 @@ int bitlbee_inetd_init() log_link( LOGLVL_ERROR, LOGOUTPUT_IRC ); log_link( LOGLVL_WARNING, LOGOUTPUT_IRC ); - /* TODO: Remove debugging log_link's */ - log_link( LOGLVL_INFO, LOGOUTPUT_IRC ); - log_link( LOGLVL_DEBUG, LOGOUTPUT_IRC ); return( 0 ); } diff --git a/otr.c b/otr.c index e675dc25..8d6f9839 100644 --- a/otr.c +++ b/otr.c @@ -231,8 +231,6 @@ void otr_load(irc_t *irc) gcry_error_t enoent = gcry_error_from_errno(ENOENT); int kg=0; - log_message(LOGLVL_DEBUG, "otr_load '%s'", irc->nick); - g_snprintf(s, 511, "%s%s.otr_keys", global.conf->configdir, irc->nick); e = otrl_privkey_read(irc->otr->us, s); if(e && e!=enoent) { @@ -264,8 +262,6 @@ void otr_save(irc_t *irc) char s[512]; gcry_error_t e; - log_message(LOGLVL_DEBUG, "otr_save '%s'", irc->nick); - g_snprintf(s, 511, "%s%s.otr_fprints", global.conf->configdir, irc->nick); e = otrl_privkey_write_fingerprints(irc->otr->us, s); if(e) { @@ -278,8 +274,6 @@ void otr_remove(const char *nick) { char s[512]; - log_message(LOGLVL_DEBUG, "otr_remove '%s'", nick); - g_snprintf(s, 511, "%s%s.otr_keys", global.conf->configdir, nick); unlink(s); g_snprintf(s, 511, "%s%s.otr_fprints", global.conf->configdir, nick); @@ -290,8 +284,6 @@ void otr_rename(const char *onick, const char *nnick) { char s[512], t[512]; - log_message(LOGLVL_DEBUG, "otr_rename '%s' -> '%s'", onick, nnick); - g_snprintf(s, 511, "%s%s.otr_keys", global.conf->configdir, onick); g_snprintf(t, 511, "%s%s.otr_keys", global.conf->configdir, nnick); rename(s,t); @@ -432,8 +424,6 @@ OtrlPolicy op_policy(void *opdata, ConnContext *context) struct im_connection *ic = check_imc(opdata, context->accountname, context->protocol); const char *p; - log_message(LOGLVL_DEBUG, "op_policy '%s' '%s'", context->accountname, context->protocol); - /* policy override during keygen: if we're missing the key for context but are currently generating it, then that's as much as we can do. => temporarily return NEVER. */ if(keygen_in_progress(ic->irc, context->accountname, context->protocol) && @@ -458,8 +448,6 @@ void op_create_privkey(void *opdata, const char *accountname, { struct im_connection *ic = check_imc(opdata, accountname, protocol); - log_message(LOGLVL_DEBUG, "op_create_privkey '%s' '%s'", accountname, protocol); - /* will fail silently if keygen already in progress */ otr_keygen(ic->irc, accountname, protocol); } @@ -470,8 +458,6 @@ int op_is_logged_in(void *opdata, const char *accountname, struct im_connection *ic = check_imc(opdata, accountname, protocol); user_t *u; - log_message(LOGLVL_DEBUG, "op_is_logged_in '%s' '%s' '%s'", accountname, protocol, recipient); - /* lookup the user_t for the given recipient */ u = user_findhandle(ic, recipient); if(u) { @@ -489,8 +475,6 @@ void op_inject_message(void *opdata, const char *accountname, { struct im_connection *ic = check_imc(opdata, accountname, protocol); - log_message(LOGLVL_DEBUG, "op_inject_message '%s' '%s' '%s' '%s'", accountname, protocol, recipient, message); - if (strcmp(accountname, recipient) == 0) { /* huh? injecting messages to myself? */ irc_usermsg(ic->irc, "note to self: %s", message); @@ -508,8 +492,6 @@ int op_display_otr_message(void *opdata, const char *accountname, struct im_connection *ic = check_imc(opdata, accountname, protocol); char *msg = g_strdup(message); - log_message(LOGLVL_DEBUG, "op_display_otr_message '%s' '%s' '%s' '%s'", accountname, protocol, username, message); - strip_html(msg); irc_usermsg(ic->irc, "otr: %s", msg); @@ -525,8 +507,6 @@ void op_new_fingerprint(void *opdata, OtrlUserState us, char hunam[45]; /* anybody looking? ;-) */ otrl_privkey_hash_to_human(hunam, fingerprint); - log_message(LOGLVL_DEBUG, "op_new_fingerprint '%s' '%s' '%s' '%s'", accountname, protocol, username, hunam); - irc_usermsg(ic->irc, "new fingerprint for %s: %s", peernick(ic->irc, username, protocol), hunam); } @@ -535,8 +515,6 @@ void op_write_fingerprints(void *opdata) { struct im_connection *ic = (struct im_connection *)opdata; - log_message(LOGLVL_DEBUG, "op_write_fingerprints"); - otr_save(ic->irc); } @@ -547,8 +525,6 @@ void op_gone_secure(void *opdata, ConnContext *context) user_t *u; const char *trust; - log_message(LOGLVL_DEBUG, "op_gone_secure '%s' '%s' '%s'", context->accountname, context->protocol, context->username); - u = peeruser(ic->irc, context->username, context->protocol); if(!u) { log_message(LOGLVL_ERROR, @@ -572,8 +548,6 @@ void op_gone_insecure(void *opdata, ConnContext *context) check_imc(opdata, context->accountname, context->protocol); user_t *u; - log_message(LOGLVL_DEBUG, "op_gone_insecure '%s' '%s' '%s'", context->accountname, context->protocol, context->username); - u = peeruser(ic->irc, context->username, context->protocol); if(!u) { log_message(LOGLVL_ERROR, @@ -592,9 +566,6 @@ void op_still_secure(void *opdata, ConnContext *context, int is_reply) check_imc(opdata, context->accountname, context->protocol); user_t *u; - log_message(LOGLVL_DEBUG, "op_still_secure '%s' '%s' '%s' is_reply=%d", - context->accountname, context->protocol, context->username, is_reply); - u = peeruser(ic->irc, context->username, context->protocol); if(!u) { log_message(LOGLVL_ERROR, @@ -631,8 +602,6 @@ const char *op_account_name(void *opdata, const char *account, const char *proto { struct im_connection *ic = (struct im_connection *)opdata; - log_message(LOGLVL_DEBUG, "op_account_name '%s' '%s'", account, protocol); - return peernick(ic->irc, account, protocol); } @@ -1130,8 +1099,6 @@ user_t *peeruser(irc_t *irc, const char *handle, const char *protocol) { user_t *u; - log_message(LOGLVL_DEBUG, "peeruser '%s' '%s'", handle, protocol); - for(u=irc->users; u; u=u->next) { struct prpl *prpl; if(!u->ic || !u->handle) @@ -1294,8 +1261,6 @@ Fingerprint *match_fingerprint(irc_t *irc, ConnContext *ctx, const char **args) } } *p = '\0'; - log_message(LOGLVL_DEBUG, "match_fingerprint '%s'", prefix); - log_message(LOGLVL_DEBUG, "n=%d strlen(prefix)=%d", n, strlen(prefix)); /* find first fingerprint with the given prefix */ n = strlen(prefix); @@ -1360,8 +1325,6 @@ OtrlPrivKey *match_privkey(irc_t *irc, const char **args) } } *p = '\0'; - log_message(LOGLVL_DEBUG, "match_privkey '%s'", prefix); - log_message(LOGLVL_DEBUG, "n=%d strlen(prefix)=%d", n, strlen(prefix)); /* find first key which matches the given prefix */ n = strlen(prefix); @@ -1503,8 +1466,6 @@ int keygen_in_progress(irc_t *irc, const char *handle, const char *protocol) { kg_t *kg; - log_message(LOGLVL_DEBUG, "keygen_in_progress '%s' '%s'", handle, protocol); - if(!irc->otr->sent_accountname || !irc->otr->sent_protocol) return 0; @@ -1573,7 +1534,6 @@ void otr_keygen(irc_t *irc, const char *handle, const char *protocol) /* is the keygen slave currently working? */ if(irc->otr->sent_accountname) { /* enqueue our job for later transmission */ - log_message(LOGLVL_DEBUG, "enqueueing keygen for %s/%s", handle, protocol); kg_t **kg = &irc->otr->todo; while(*kg) kg=&((*kg)->next); @@ -1582,7 +1542,6 @@ void otr_keygen(irc_t *irc, const char *handle, const char *protocol) (*kg)->protocol = g_strdup(protocol); } else { /* send our job over and remember it */ - log_message(LOGLVL_DEBUG, "slave: generate for %s/%s!", handle, protocol); fprintf(irc->otr->to, "%s\n%s\n", handle, protocol); fflush(irc->otr->to); irc->otr->sent_accountname = g_strdup(handle); @@ -1636,14 +1595,9 @@ gboolean keygen_finish_handler(gpointer data, gint fd, b_input_condition cond) irc_t *irc = (irc_t *)data; char filename[512], msg[512]; - log_message(LOGLVL_DEBUG, "keygen_finish_handler cond=%d", cond); - myfgets(filename, 512, irc->otr->from); myfgets(msg, 512, irc->otr->from); - log_message(LOGLVL_DEBUG, "filename='%s'", filename); - log_message(LOGLVL_DEBUG, "msg='%s'", msg); - irc_usermsg(irc, "%s", msg); if(filename[0]) { char *kf = g_strdup_printf("%s%s.otr_keys", global.conf->configdir, irc->nick); @@ -1666,7 +1620,6 @@ gboolean keygen_finish_handler(gpointer data, gint fd, b_input_condition cond) if(irc->otr->todo) { kg_t *p = irc->otr->todo; /* send the next one over */ - log_message(LOGLVL_DEBUG, "slave: keygen for %s/%s!", p->accountname, p->protocol); fprintf(irc->otr->to, "%s\n%s\n", p->accountname, p->protocol); fflush(irc->otr->to); irc->otr->sent_accountname = p->accountname; @@ -1676,7 +1629,6 @@ gboolean keygen_finish_handler(gpointer data, gint fd, b_input_condition cond) return TRUE; /* keep watching */ } else { /* okay, the slave is idle now, so kill him */ - log_message(LOGLVL_DEBUG, "all keys done. die, slave!"); fclose(irc->otr->from); fclose(irc->otr->to); kill(irc->otr->keygen, SIGTERM); @@ -1692,8 +1644,6 @@ void copyfile(const char *a, const char *b) int n; char buf[1024]; - log_message(LOGLVL_DEBUG, "copyfile '%s' '%s'", a, b); - fda = open(a, O_RDONLY); fdb = open(b, O_WRONLY | O_CREAT | O_TRUNC, 0600); -- cgit v1.2.3 From 59ab2afb144aaad7618fde69e81df40bc7241904 Mon Sep 17 00:00:00 2001 From: Sven Moritz Hallberg Date: Sun, 17 Feb 2008 03:21:11 +0100 Subject: fix compilation without otr support --- otr.h | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/otr.h b/otr.h index ca45c1e9..7c1962a8 100644 --- a/otr.h +++ b/otr.h @@ -107,7 +107,7 @@ typedef void *OtrlMessageAppOps; #define otr_save(irc) {} #define otr_remove(nick) {} #define otr_rename(onick,nnick) {} -#define otr_check_for_key(acc) {} +#define otr_check_for_key(acc) (0) #define otr_handle_message(ic,handle,msg) (g_strdup(msg)) #define otr_send_message(ic,h,m,f) (ic->acc->prpl->buddy_msg(ic,h,m,f)) -- cgit v1.2.3 From 9e64011b1758bc9dbeddb517eff5eccbb0733674 Mon Sep 17 00:00:00 2001 From: Sven Moritz Hallberg Date: Sun, 17 Feb 2008 03:28:05 +0100 Subject: fix an uninitialized value --- otr.c | 1 + 1 file changed, 1 insertion(+) diff --git a/otr.c b/otr.c index 8d6f9839..dd3fc13e 100644 --- a/otr.c +++ b/otr.c @@ -768,6 +768,7 @@ void cmd_otr_info(irc_t *irc, char **args) /* interpret arg as 'user/protocol/account' if possible */ protocol = strchr(arg, '/'); + myhandle = NULL; if(protocol) { *(protocol++) = '\0'; myhandle = strchr(protocol, '/'); -- cgit v1.2.3 From 12cc58b34a98ae010cf1718e994b63da35ddd7c2 Mon Sep 17 00:00:00 2001 From: Sven Moritz Hallberg Date: Sun, 17 Feb 2008 11:52:28 +0100 Subject: don't unnecessarily duplicate otr userstate in keygen slave --- otr.c | 13 +++---------- 1 file changed, 3 insertions(+), 10 deletions(-) diff --git a/otr.c b/otr.c index dd3fc13e..3e953854 100644 --- a/otr.c +++ b/otr.c @@ -111,7 +111,7 @@ int keygen_in_progress(irc_t *irc, const char *handle, const char *protocol); void otr_keygen(irc_t *irc, const char *handle, const char *protocol); /* main function for the forked keygen slave */ -void keygen_child_main(const char *nick, int infd, int outfd); +void keygen_child_main(OtrlUserState us, int infd, int outfd); /* mainloop handler for when a keygen finishes */ gboolean keygen_finish_handler(gpointer data, gint fd, b_input_condition cond); @@ -1519,7 +1519,7 @@ void otr_keygen(irc_t *irc, const char *handle, const char *protocol) if(!p) { /* child process */ signal(SIGTERM, exit); - keygen_child_main(irc->nick, to[0], from[1]); + keygen_child_main(irc->otr->us, to[0], from[1]); exit(0); } @@ -1550,20 +1550,13 @@ void otr_keygen(irc_t *irc, const char *handle, const char *protocol) } } -void keygen_child_main(const char *nick, int infd, int outfd) +void keygen_child_main(OtrlUserState us, int infd, int outfd) { - OtrlUserState us; - char *kf; FILE *input, *output; char filename[128], accountname[512], protocol[512]; gcry_error_t e; int tempfd; - us = otrl_userstate_create(); - kf = g_strdup_printf("%s%s.otr_keys", global.conf->configdir, nick); - otrl_privkey_read(us, kf); - g_free(kf); - input = fdopen(infd, "r"); output = fdopen(outfd, "w"); -- cgit v1.2.3 From 4d8fbe809475aeed7fd073a328b53414642194b0 Mon Sep 17 00:00:00 2001 From: Sven Moritz Hallberg Date: Sun, 17 Feb 2008 12:15:49 +0100 Subject: remove gthread dependency --- configure | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/configure b/configure index c0a9bdb4..b0058931 100755 --- a/configure +++ b/configure @@ -175,8 +175,8 @@ fi if $PKG_CONFIG --version > /dev/null 2>/dev/null && $PKG_CONFIG glib-2.0; then if $PKG_CONFIG glib-2.0 --atleast-version=$GLIB_MIN_VERSION; then cat<>Makefile.settings -EFLAGS+=`$PKG_CONFIG --libs glib-2.0 gmodule-2.0 gthread-2.0` -CFLAGS+=`$PKG_CONFIG --cflags glib-2.0 gmodule-2.0 gthread-2.0` +EFLAGS+=`$PKG_CONFIG --libs glib-2.0 gmodule-2.0` +CFLAGS+=`$PKG_CONFIG --cflags glib-2.0 gmodule-2.0` EOF else echo -- cgit v1.2.3 From fcfd9c56a81a993d86f18c792b1584ec5c5dca99 Mon Sep 17 00:00:00 2001 From: Sven Moritz Hallberg Date: Sun, 17 Feb 2008 14:24:48 +0100 Subject: tabidy some indentation --- otr.c | 10 +++++----- 1 file changed, 5 insertions(+), 5 deletions(-) diff --git a/otr.c b/otr.c index 3e953854..bd76e2ea 100644 --- a/otr.c +++ b/otr.c @@ -249,11 +249,11 @@ void otr_load(irc_t *irc) if(kg) { irc_usermsg(irc, "Notice: " "The accounts above do not have OTR encryption keys associated with them, yet. " - "These keys are now being generated in the background. " - "You will be notified as they are completed. " - "It is not necessary to wait; " - "BitlBee can be used normally during key generation. " - "You may safely ignore this message if you don't know what OTR is. ;)"); + "These keys are now being generated in the background. " + "You will be notified as they are completed. " + "It is not necessary to wait; " + "BitlBee can be used normally during key generation. " + "You may safely ignore this message if you don't know what OTR is. ;)"); } } -- cgit v1.2.3 From 5933da72f1f04810a1b789f9e1e1c1d7e360f30f Mon Sep 17 00:00:00 2001 From: Sven Moritz Hallberg Date: Mon, 18 Feb 2008 10:36:23 +0100 Subject: fix "purple message" bug ;) --- otr.c | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) diff --git a/otr.c b/otr.c index bd76e2ea..3fdd61b1 100644 --- a/otr.c +++ b/otr.c @@ -337,13 +337,13 @@ char *otr_handle_message(struct im_connection *ic, const char *handle, const cha if(context && context->msgstate == OTRL_MSGSTATE_ENCRYPTED && set_getbool(&ic->irc->set, "color_encrypted")) { /* color according to f'print trust */ - char color; + int color; const char *trust = context->active_fingerprint->trust; if(trust && trust[0] != '\0') - color='3'; /* green */ + color=3; /* green */ else - color='5'; /* red */ - colormsg = g_strdup_printf("\x03%c%s\x0F", color, newmsg); + color=5; /* red */ + colormsg = g_strdup_printf("\x03%.2d%s\x0F", color, newmsg); } else { colormsg = g_strdup(newmsg); } -- cgit v1.2.3 From b14890de5407689555b1fcc293455fb77d306afd Mon Sep 17 00:00:00 2001 From: Sven Moritz Hallberg Date: Mon, 18 Feb 2008 10:42:25 +0100 Subject: fix "purple background" bug *sigh* :P --- otr.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/otr.c b/otr.c index 3fdd61b1..6451568c 100644 --- a/otr.c +++ b/otr.c @@ -343,7 +343,7 @@ char *otr_handle_message(struct im_connection *ic, const char *handle, const cha color=3; /* green */ else color=5; /* red */ - colormsg = g_strdup_printf("\x03%.2d%s\x0F", color, newmsg); + colormsg = g_strdup_printf("\x03%.2d,00%s\x0F", color, newmsg); } else { colormsg = g_strdup(newmsg); } -- cgit v1.2.3 From 0cf867b8fbeb33e1841136a23297337f937da790 Mon Sep 17 00:00:00 2001 From: Sven Moritz Hallberg Date: Sat, 22 Mar 2008 15:02:11 +0100 Subject: fix a syntax error in ssl_openssl.c --- lib/ssl_openssl.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/lib/ssl_openssl.c b/lib/ssl_openssl.c index e54b21ee..0ec9865f 100644 --- a/lib/ssl_openssl.c +++ b/lib/ssl_openssl.c @@ -56,7 +56,7 @@ static gboolean ssl_starttls_real( gpointer data, gint source, b_input_condition static gboolean ssl_handshake( gpointer data, gint source, b_input_condition cond ); -void ssl_init( void ); +void ssl_init( void ) { initialized = TRUE; SSLeay_add_ssl_algorithms(); -- cgit v1.2.3 From eb0714d7be4f57b92231fcc1b0798c7fe644c000 Mon Sep 17 00:00:00 2001 From: Sven Moritz Hallberg Date: Sat, 22 Mar 2008 15:19:33 +0100 Subject: silence a warning --- otr.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/otr.c b/otr.c index 6451568c..18e25807 100644 --- a/otr.c +++ b/otr.c @@ -763,7 +763,7 @@ void cmd_otr_info(irc_t *irc, char **args) show_general_otr_info(irc); } else { char *arg = g_strdup(args[1]); - char *myhandle, *handle, *protocol; + char *myhandle, *handle=NULL, *protocol; ConnContext *ctx; /* interpret arg as 'user/protocol/account' if possible */ -- cgit v1.2.3 From 9730d7250bb9e938ca00b72efdd8e8b3c03b2753 Mon Sep 17 00:00:00 2001 From: Sven Moritz Hallberg Date: Wed, 16 Jul 2008 23:45:12 +0200 Subject: minor bugfixes --- lib/ssl_openssl.c | 2 +- otr.c | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/lib/ssl_openssl.c b/lib/ssl_openssl.c index e54b21ee..0ec9865f 100644 --- a/lib/ssl_openssl.c +++ b/lib/ssl_openssl.c @@ -56,7 +56,7 @@ static gboolean ssl_starttls_real( gpointer data, gint source, b_input_condition static gboolean ssl_handshake( gpointer data, gint source, b_input_condition cond ); -void ssl_init( void ); +void ssl_init( void ) { initialized = TRUE; SSLeay_add_ssl_algorithms(); diff --git a/otr.c b/otr.c index 6451568c..18e25807 100644 --- a/otr.c +++ b/otr.c @@ -763,7 +763,7 @@ void cmd_otr_info(irc_t *irc, char **args) show_general_otr_info(irc); } else { char *arg = g_strdup(args[1]); - char *myhandle, *handle, *protocol; + char *myhandle, *handle=NULL, *protocol; ConnContext *ctx; /* interpret arg as 'user/protocol/account' if possible */ -- cgit v1.2.3 From 823de9d44f262ea2364ac8ec6a1e18e0f7dab658 Mon Sep 17 00:00:00 2001 From: Sven Moritz Hallberg Date: Thu, 12 Mar 2009 20:10:06 +0100 Subject: commit updates by ashish shukla --- Makefile | 2 +- account.c | 123 ++++++++++++++++++++++++++++++++++------ account.h | 9 ++- bitlbee.h | 5 +- conf.c | 7 +++ configure | 2 +- doc/CHANGES | 21 +++++++ doc/user-guide/commands.xml | 10 +++- ipc.c | 35 +++--------- ipc.h | 3 +- irc.c | 79 ++++++++++++++------------ irc_commands.c | 1 - lib/ssl_bogus.c | 5 ++ protocols/jabber/jabber.c | 2 +- protocols/jabber/jabber_util.c | 4 +- protocols/msn/msn.c | 61 +------------------- protocols/msn/msn.h | 6 ++ protocols/msn/ns.c | 23 +------- protocols/msn/sb.c | 42 ++++++++++++++ protocols/nogaim.c | 10 +++- protocols/yahoo/libyahoo2.c | 2 + protocols/yahoo/yahoo.c | 3 - protocols/yahoo/yahoo_httplib.c | 2 + root_commands.c | 91 +++++++++++++++++++---------- set.c | 55 ++++++++++++------ set.h | 10 +++- sock.h | 8 ++- storage.c | 37 +++++++++--- storage.h | 8 +-- storage_text.c | 21 +------ storage_xml.c | 26 ++++----- unix.c | 16 +++--- 32 files changed, 445 insertions(+), 284 deletions(-) diff --git a/Makefile b/Makefile index 6c8e289f..9a495283 100644 --- a/Makefile +++ b/Makefile @@ -106,7 +106,7 @@ tar: fakeroot debian/rules clean || make distclean x=$$(basename $$(pwd)); \ cd ..; \ - tar czf $$x.tar.gz --exclude=debian --exclude=.bzr $$x + tar czf $$x.tar.gz --exclude=debian --exclude=.bzr* $$x $(subdirs): @$(MAKE) -C $@ $(MAKECMDGOALS) diff --git a/account.c b/account.c index 2c6e1069..f3e15d7e 100644 --- a/account.c +++ b/account.c @@ -54,7 +54,7 @@ account_t *account_add( irc_t *irc, struct prpl *prpl, char *user, char *pass ) s = set_add( &a->set, "auto_reconnect", "true", set_eval_bool, a ); s = set_add( &a->set, "password", NULL, set_eval_account, a ); - s->flags |= ACC_SET_NOSAVE; + s->flags |= ACC_SET_NOSAVE | SET_NULL_OK; s = set_add( &a->set, "username", NULL, set_eval_account, a ); s->flags |= ACC_SET_NOSAVE | ACC_SET_OFFLINE_ONLY; @@ -76,9 +76,23 @@ char *set_eval_account( set_t *set, char *value ) /* Double-check: We refuse to edit on-line accounts. */ if( set->flags & ACC_SET_OFFLINE_ONLY && acc->ic ) - return NULL; + return SET_INVALID; - if( strcmp( set->key, "username" ) == 0 ) + if( strcmp( set->key, "server" ) == 0 ) + { + g_free( acc->server ); + if( value && *value ) + { + acc->server = g_strdup( value ); + return value; + } + else + { + acc->server = g_strdup( set->def ); + return g_strdup( set->def ); + } + } + else if( strcmp( set->key, "username" ) == 0 ) { g_free( acc->user ); acc->user = g_strdup( value ); @@ -86,34 +100,29 @@ char *set_eval_account( set_t *set, char *value ) } else if( strcmp( set->key, "password" ) == 0 ) { - g_free( acc->pass ); - acc->pass = g_strdup( value ); - return NULL; /* password shouldn't be visible in plaintext! */ - } - else if( strcmp( set->key, "server" ) == 0 ) - { - g_free( acc->server ); - if( *value ) + if( value ) { - acc->server = g_strdup( value ); - return value; + g_free( acc->pass ); + acc->pass = g_strdup( value ); + return NULL; /* password shouldn't be visible in plaintext! */ } else { - acc->server = NULL; - return g_strdup( set->def ); + /* NULL can (should) be stored in the set_t + variable, but is otherwise not correct. */ + return SET_INVALID; } } else if( strcmp( set->key, "auto_connect" ) == 0 ) { if( !is_bool( value ) ) - return NULL; + return SET_INVALID; acc->auto_connect = bool2int( value ); return value; } - return NULL; + return SET_INVALID; } account_t *account_get( irc_t *irc, char *id ) @@ -233,3 +242,83 @@ void account_off( irc_t *irc, account_t *a ) cancel_auto_reconnect( a ); } } + +struct account_reconnect_delay +{ + int start; + char op; + int step; + int max; +}; + +int account_reconnect_delay_parse( char *value, struct account_reconnect_delay *p ) +{ + memset( p, 0, sizeof( *p ) ); + /* A whole day seems like a sane "maximum maximum". */ + p->max = 86400; + + /* Format: /[0-9]+([*+][0-9]+(<[0-9+]))/ */ + while( *value && isdigit( *value ) ) + p->start = p->start * 10 + *value++ - '0'; + + /* Sure, call me evil for implementing my own fscanf here, but it's + dead simple and I immediately know where to continue parsing. */ + + if( *value == 0 ) + /* If the string ends now, the delay is constant. */ + return 1; + else if( *value != '+' && *value != '*' ) + /* Otherwise allow either a + or a * */ + return 0; + + p->op = *value++; + + /* + or * the delay by this number every time. */ + while( *value && isdigit( *value ) ) + p->step = p->step * 10 + *value++ - '0'; + + if( *value == 0 ) + /* Use the default maximum (one day). */ + return 1; + else if( *value != '<' ) + return 0; + + p->max = 0; + value ++; + while( *value && isdigit( *value ) ) + p->max = p->max * 10 + *value++ - '0'; + + return p->max > 0; +} + +char *set_eval_account_reconnect_delay( set_t *set, char *value ) +{ + struct account_reconnect_delay p; + + return account_reconnect_delay_parse( value, &p ) ? value : SET_INVALID; +} + +int account_reconnect_delay( account_t *a ) +{ + char *setting = set_getstr( &a->irc->set, "auto_reconnect_delay" ); + struct account_reconnect_delay p; + + if( account_reconnect_delay_parse( setting, &p ) ) + { + if( a->auto_reconnect_delay == 0 ) + a->auto_reconnect_delay = p.start; + else if( p.op == '+' ) + a->auto_reconnect_delay += p.step; + else if( p.op == '*' ) + a->auto_reconnect_delay *= p.step; + + if( a->auto_reconnect_delay > p.max ) + a->auto_reconnect_delay = p.max; + } + else + { + a->auto_reconnect_delay = 0; + } + + return a->auto_reconnect_delay; +} diff --git a/account.h b/account.h index a81ca928..cf22482c 100644 --- a/account.h +++ b/account.h @@ -34,6 +34,7 @@ typedef struct account char *server; int auto_connect; + int auto_reconnect_delay; int reconnect; set_t *set; @@ -51,9 +52,11 @@ void account_on( irc_t *irc, account_t *a ); void account_off( irc_t *irc, account_t *a ); char *set_eval_account( set_t *set, char *value ); +char *set_eval_account_reconnect_delay( set_t *set, char *value ); +int account_reconnect_delay( account_t *a ); -#define ACC_SET_NOSAVE 1 -#define ACC_SET_OFFLINE_ONLY 2 -#define ACC_SET_ONLINE_ONLY 4 +#define ACC_SET_NOSAVE 0x01 +#define ACC_SET_OFFLINE_ONLY 0x02 +#define ACC_SET_ONLINE_ONLY 0x04 #endif diff --git a/bitlbee.h b/bitlbee.h index f11d275d..5543b501 100644 --- a/bitlbee.h +++ b/bitlbee.h @@ -31,8 +31,11 @@ /* Depend on Windows 2000 for now since we need getaddrinfo() */ #define _WIN32_WINNT 0x0501 +/* Depend on Windows 2000 for now since we need getaddrinfo() */ +#define _WIN32_WINNT 0x0501 + #define PACKAGE "BitlBee" -#define BITLBEE_VERSION "1.2.1" +#define BITLBEE_VERSION "1.2.3" #define VERSION BITLBEE_VERSION #define MAX_STRING 511 diff --git a/conf.c b/conf.c index bd7d5b6a..d930b26d 100644 --- a/conf.c +++ b/conf.c @@ -146,6 +146,13 @@ conf_t *conf_load( int argc, char *argv[] ) " -h Show this help page.\n" ); return NULL; } + else if( opt == 'R' ) + { + /* Backward compatibility; older BitlBees passed this + info using a command-line flag. Allow people to + upgrade from such a version for now. */ + setenv( "_BITLBEE_RESTART_STATE", optarg, 0 ); + } else if( opt == 'u' ) { g_free( conf->user ); diff --git a/configure b/configure index bb22c4cc..2287b2ad 100755 --- a/configure +++ b/configure @@ -71,7 +71,7 @@ Option Description Default --strip=0/1 Disable/enable binary stripping $strip --gcov=0/1 Disable/enable test coverage reporting $gcov --plugins=0/1 Disable/enable plugins support $plugins ---otr=0/1/auto Disable/enable OTR encryption support $otr +--otr=0/1 Disable/enable OTR encryption support $otr --events=... Event handler (glib, libevent) $events --ssl=... SSL library to use (gnutls, nss, openssl, bogus, auto) diff --git a/doc/CHANGES b/doc/CHANGES index ac1f1f02..84dbe162 100644 --- a/doc/CHANGES +++ b/doc/CHANGES @@ -3,6 +3,27 @@ found in the bzr commit logs, for example you can try: http://bugs.bitlbee.org/bitlbee/timeline?daysback=90&changeset=on +Version 1.2.3: +- Fixed one more flaw similar to the previous hijacking bug, caused by incon- + sistent handling of the USTATUS_IDENTIFIED state. All code touching these + variables was reviewed and should be correct now. + +Finished 7 Sep 2008 + +Version 1.2.2: +- Security bugfix: It was possible to hijack accounts (without gaining access + to the old account, it's simply an overwrite) +- Some more stability improvements. +- Fixed bug where people with non-lowercase nicks couldn't drop their account. +- Easier upgrades of non-forking daemon mode servers (using the DEAF + command). +- Can be cross-compiled for Win32 now! (No support for SSL yet though, which + makes it less useful for now.) +- Exponential backoff on auto-reconnect. +- Changing passwords gives less confusing feedback ("password is empty") now. + +Finished 26 Aug 2008 + Version 1.2.1: - Fixed proxy support. - Fixed stalling issues while connecting to Jabber when using the OpenSSL diff --git a/doc/user-guide/commands.xml b/doc/user-guide/commands.xml index f0653232..3a7bd5a8 100644 --- a/doc/user-guide/commands.xml +++ b/doc/user-guide/commands.xml @@ -469,12 +469,16 @@ - - 300 + + 5*3<900 - Tell BitlBee after how many seconds it should attempt to bring an IM-connection back up after a crash. It's not a good idea to set this value very low, it will cause too much useless traffic when an IM-server is down for a few hours. + Tell BitlBee after how many seconds it should attempt to bring a broken IM-connection back up. + + + + This can be one integer, for a constant delay. One can also set it to something like "10*10", which means wait for ten seconds on the first reconnect, multiply it by ten on every failure. Once successfully connected, this delay is re-set to the initial value. With < you can give a maximum delay. diff --git a/ipc.c b/ipc.c index d6b850f1..f853a18f 100644 --- a/ipc.c +++ b/ipc.c @@ -32,6 +32,7 @@ #endif GSList *child_list = NULL; +static char *statefile = NULL; static void ipc_master_cmd_client( irc_t *data, char **cmd ) { @@ -61,25 +62,6 @@ static void ipc_master_cmd_die( irc_t *data, char **cmd ) bitlbee_shutdown( NULL, -1, 0 ); } -static void ipc_master_cmd_deaf( irc_t *data, char **cmd ) -{ - if( global.conf->runmode == RUNMODE_DAEMON ) - { - b_event_remove( global.listen_watch_source_id ); - close( global.listen_socket ); - - global.listen_socket = global.listen_watch_source_id = -1; - - ipc_to_children_str( "OPERMSG :Closed listening socket, waiting " - "for all users to disconnect." ); - } - else - { - ipc_to_children_str( "OPERMSG :The DEAF command only works in " - "normal daemon mode. Try DIE instead." ); - } -} - void ipc_master_cmd_rehash( irc_t *data, char **cmd ) { runmode_t oldmode; @@ -115,7 +97,6 @@ static const command_t ipc_master_commands[] = { { "client", 3, ipc_master_cmd_client, 0 }, { "hello", 0, ipc_master_cmd_client, 0 }, { "die", 0, ipc_master_cmd_die, 0 }, - { "deaf", 0, ipc_master_cmd_deaf, 0 }, { "wallops", 1, NULL, IPC_CMD_TO_CHILDREN }, { "wall", 1, NULL, IPC_CMD_TO_CHILDREN }, { "opermsg", 1, NULL, IPC_CMD_TO_CHILDREN }, @@ -459,7 +440,6 @@ void ipc_child_disable() global.listen_socket = -1; } -#ifndef _WIN32 char *ipc_master_save_state() { char *fn = g_strdup( "/tmp/bee-restart.XXXXXX" ); @@ -500,6 +480,11 @@ char *ipc_master_save_state() } } +void ipc_master_set_statefile( char *fn ) +{ + statefile = g_strdup( fn ); +} + static gboolean new_ipc_client( gpointer data, gint serversock, b_input_condition cond ) { @@ -520,6 +505,7 @@ static gboolean new_ipc_client( gpointer data, gint serversock, b_input_conditio return TRUE; } +#ifndef _WIN32 int ipc_master_listen_socket() { struct sockaddr_un un_addr; @@ -556,14 +542,10 @@ int ipc_master_listen_socket() return 1; } #else -int ipc_master_listen_socket() -{ /* FIXME: Open named pipe \\.\BITLBEE */ - return 0; -} #endif -int ipc_master_load_state( char *statefile ) +int ipc_master_load_state() { struct bitlbee_child *child; FILE *fp; @@ -571,7 +553,6 @@ int ipc_master_load_state( char *statefile ) if( statefile == NULL ) return 0; - fp = fopen( statefile, "r" ); unlink( statefile ); /* Why do it later? :-) */ if( fp == NULL ) diff --git a/ipc.h b/ipc.h index 0e71c520..f3d24614 100644 --- a/ipc.h +++ b/ipc.h @@ -57,7 +57,8 @@ void ipc_to_children_str( char *format, ... ) G_GNUC_PRINTF( 1, 2 ); void ipc_master_cmd_rehash( irc_t *data, char **cmd ); char *ipc_master_save_state(); -int ipc_master_load_state( char *statefile ); +void ipc_master_set_statefile( char *fn ); +int ipc_master_load_state(); int ipc_master_listen_socket(); extern GSList *child_list; diff --git a/irc.c b/irc.c index d0b184fc..f32ae21a 100644 --- a/irc.c +++ b/irc.c @@ -32,17 +32,23 @@ #include static gboolean irc_userping( gpointer _irc, int fd, b_input_condition cond ); -static void irc_welcome( irc_t *irc ); +static void irc_welcome( irc_t* irc ); GSList *irc_connection_list = NULL; -static char *passchange( set_t *set, char *value ) +static char *set_eval_password( set_t *set, char *value ) { irc_t *irc = set->data; - irc_setpass( irc, value ); - irc_usermsg( irc, "Password successfully changed" ); - return NULL; + if( irc->status & USTATUS_IDENTIFIED && value ) + { + irc_setpass( irc, value ); + return NULL; + } + else + { + return SET_INVALID; + } } static char *set_eval_charset( set_t *set, char *value ) @@ -79,6 +85,7 @@ irc_t *irc_new( int fd ) irc_t *irc; struct sockaddr_storage sock; socklen_t socklen = sizeof( sock ); + set_t *s; irc = g_new0( irc_t, 1 ); @@ -138,38 +145,38 @@ irc_t *irc_new( int fd ) irc_connection_list = g_slist_append( irc_connection_list, irc ); - set_add( &irc->set, "auto_connect", "true", set_eval_bool, irc ); - set_add( &irc->set, "auto_reconnect", "false", set_eval_bool, irc ); - set_add( &irc->set, "auto_reconnect_delay", "300", set_eval_int, irc ); - set_add( &irc->set, "buddy_sendbuffer", "false", set_eval_bool, irc ); - set_add( &irc->set, "buddy_sendbuffer_delay", "200", set_eval_int, irc ); - set_add( &irc->set, "charset", "utf-8", set_eval_charset, irc ); - set_add( &irc->set, "color_encrypted", "true", set_eval_bool, irc ); - set_add( &irc->set, "debug", "false", set_eval_bool, irc ); - set_add( &irc->set, "default_target", "root", NULL, irc ); - set_add( &irc->set, "display_namechanges", "false", set_eval_bool, irc ); - set_add( &irc->set, "handle_unknown", "root", NULL, irc ); - set_add( &irc->set, "halfop_buddies", "encrypted", set_eval_halfop_buddies, irc ); - set_add( &irc->set, "lcnicks", "true", set_eval_bool, irc ); - set_add( &irc->set, "op_buddies", "trusted", set_eval_op_buddies, irc ); - set_add( &irc->set, "op_root", "true", set_eval_op_root, irc ); - set_add( &irc->set, "op_user", "true", set_eval_op_user, irc ); - set_add( &irc->set, "otr_policy", "opportunistic", set_eval_otr_policy, irc ); - set_add( &irc->set, "password", NULL, passchange, irc ); - set_add( &irc->set, "private", "true", set_eval_bool, irc ); - set_add( &irc->set, "query_order", "lifo", NULL, irc ); - set_add( &irc->set, "root_nick", irc->mynick, set_eval_root_nick, irc ); - set_add( &irc->set, "save_on_quit", "true", set_eval_bool, irc ); - set_add( &irc->set, "simulate_netsplit", "true", set_eval_bool, irc ); - set_add( &irc->set, "strip_html", "true", NULL, irc ); - set_add( &irc->set, "to_char", ": ", set_eval_to_char, irc ); - set_add( &irc->set, "typing_notice", "false", set_eval_bool, irc ); - set_add( &irc->set, "voice_buddies", "notaway", set_eval_voice_buddies, irc ); + s = set_add( &irc->set, "auto_connect", "true", set_eval_bool, irc ); + s = set_add( &irc->set, "auto_reconnect", "false", set_eval_bool, irc ); + s = set_add( &irc->set, "auto_reconnect_delay", "5*3<900", set_eval_account_reconnect_delay, irc ); + s = set_add( &irc->set, "buddy_sendbuffer", "false", set_eval_bool, irc ); + s = set_add( &irc->set, "buddy_sendbuffer_delay", "200", set_eval_int, irc ); + s = set_add( &irc->set, "charset", "utf-8", set_eval_charset, irc ); + s = set_add( &irc->set, "color_encrypted", "true", set_eval_bool, irc ); + s = set_add( &irc->set, "debug", "false", set_eval_bool, irc ); + s = set_add( &irc->set, "default_target", "root", NULL, irc ); + s = set_add( &irc->set, "display_namechanges", "false", set_eval_bool, irc ); + s = set_add( &irc->set, "handle_unknown", "root", NULL, irc ); + s = set_add( &irc->set, "halfop_buddies", "encrypted", set_eval_halfop_buddies, irc ); + s = set_add( &irc->set, "lcnicks", "true", set_eval_bool, irc ); + s = set_add( &irc->set, "op_buddies", "trusted", set_eval_op_buddies, irc ); + s = set_add( &irc->set, "op_root", "true", set_eval_op_root, irc ); + s = set_add( &irc->set, "otr_policy", "oppurtunistic", set_eval_otr_policy, irc ); + s = set_add( &irc->set, "password", NULL, set_eval_password, irc ); + s->flags |= SET_NULL_OK; + s = set_add( &irc->set, "private", "true", set_eval_bool, irc ); + s = set_add( &irc->set, "query_order", "lifo", NULL, irc ); + s = set_add( &irc->set, "root_nick", irc->mynick, set_eval_root_nick, irc ); + s = set_add( &irc->set, "save_on_quit", "true", set_eval_bool, irc ); + s = set_add( &irc->set, "simulate_netsplit", "true", set_eval_bool, irc ); + s = set_add( &irc->set, "strip_html", "true", NULL, irc ); + s = set_add( &irc->set, "to_char", ": ", set_eval_to_char, irc ); + s = set_add( &irc->set, "typing_notice", "false", set_eval_bool, irc ); + s = set_add( &irc->set, "voice_buddies", "notaway", set_eval_voice_buddies, irc); conf_loaddefaults( irc ); irc->otr = otr_new(); - + /* Evaluator sets the iconv/oconv structures. */ set_eval_charset( set_find( &irc->set, "charset" ), set_getstr( &irc->set, "charset" ) ); @@ -240,7 +247,7 @@ void irc_free( irc_t * irc ) log_message( LOGLVL_INFO, "Destroying connection with fd %d", irc->fd ); if( irc->status & USTATUS_IDENTIFIED && set_getbool( &irc->set, "save_on_quit" ) ) - if( storage_save( irc, TRUE ) != STORAGE_OK ) + if( storage_save( irc, NULL, TRUE ) != STORAGE_OK ) irc_usermsg( irc, "Error while saving settings!" ); irc_connection_list = g_slist_remove( irc_connection_list, irc ); @@ -321,11 +328,11 @@ void irc_free( irc_t * irc ) g_free( irc->channel ); g_free( irc->last_target ); - + otr_free(irc->otr); g_free( irc ); - + if( global.conf->runmode == RUNMODE_INETD || global.conf->runmode == RUNMODE_FORKDAEMON || ( global.conf->runmode == RUNMODE_DAEMON && diff --git a/irc_commands.c b/irc_commands.c index fb2bc7cf..6a47007a 100644 --- a/irc_commands.c +++ b/irc_commands.c @@ -625,7 +625,6 @@ static const command_t irc_commands[] = { { "version", 0, irc_cmd_version, IRC_CMD_LOGGED_IN }, { "completions", 0, irc_cmd_completions, IRC_CMD_LOGGED_IN }, { "die", 0, NULL, IRC_CMD_OPER_ONLY | IRC_CMD_TO_MASTER }, - { "deaf", 0, NULL, IRC_CMD_OPER_ONLY | IRC_CMD_TO_MASTER }, { "wallops", 1, NULL, IRC_CMD_OPER_ONLY | IRC_CMD_TO_MASTER }, { "wall", 1, NULL, IRC_CMD_OPER_ONLY | IRC_CMD_TO_MASTER }, { "rehash", 0, irc_cmd_rehash, IRC_CMD_OPER_ONLY }, diff --git a/lib/ssl_bogus.c b/lib/ssl_bogus.c index 6eea18c7..22226592 100644 --- a/lib/ssl_bogus.c +++ b/lib/ssl_bogus.c @@ -69,3 +69,8 @@ int ssl_pending( void *conn ) { return 0; } + +int ssl_pending( void *conn ) +{ + return 0; +} diff --git a/protocols/jabber/jabber.c b/protocols/jabber/jabber.c index 5fb2c4fc..518c9506 100644 --- a/protocols/jabber/jabber.c +++ b/protocols/jabber/jabber.c @@ -69,7 +69,7 @@ static void jabber_init( account_t *acc ) s = set_add( &acc->set, "resource_select", "priority", NULL, acc ); s = set_add( &acc->set, "server", NULL, set_eval_account, acc ); - s->flags |= ACC_SET_NOSAVE | ACC_SET_OFFLINE_ONLY; + s->flags |= ACC_SET_NOSAVE | ACC_SET_OFFLINE_ONLY | SET_NULL_OK; s = set_add( &acc->set, "ssl", "false", set_eval_bool, acc ); s->flags |= ACC_SET_OFFLINE_ONLY; diff --git a/protocols/jabber/jabber_util.c b/protocols/jabber/jabber_util.c index 1bee5009..19a73b6a 100644 --- a/protocols/jabber/jabber_util.c +++ b/protocols/jabber/jabber_util.c @@ -36,10 +36,10 @@ char *set_eval_priority( set_t *set, char *value ) { /* Priority is a signed 8-bit integer, according to RFC 3921. */ if( i < -128 || i > 127 ) - return NULL; + return SET_INVALID; } else - return NULL; + return SET_INVALID; /* Only run this stuff if the account is online ATM, and if the setting seems to be acceptable. */ diff --git a/protocols/msn/msn.c b/protocols/msn/msn.c index 335ae894..05eae541 100644 --- a/protocols/msn/msn.c +++ b/protocols/msn/msn.c @@ -112,7 +112,6 @@ static void msn_logout( struct im_connection *ic ) static int msn_buddy_msg( struct im_connection *ic, char *who, char *message, int away ) { struct msn_switchboard *sb; - struct msn_data *md = ic->proto_data; if( ( sb = msn_sb_by_handle( ic, who ) ) ) { @@ -121,47 +120,13 @@ static int msn_buddy_msg( struct im_connection *ic, char *who, char *message, in else { struct msn_message *m; - char buf[1024]; /* Create a message. We have to arrange a usable switchboard, and send the message later. */ m = g_new0( struct msn_message, 1 ); m->who = g_strdup( who ); m->text = g_strdup( message ); - /* FIXME: *CHECK* the reliability of using spare sb's! */ - if( ( sb = msn_sb_spare( ic ) ) ) - { - debug( "Trying to use a spare switchboard to message %s", who ); - - sb->who = g_strdup( who ); - g_snprintf( buf, sizeof( buf ), "CAL %d %s\r\n", ++sb->trId, who ); - if( msn_sb_write( sb, buf, strlen( buf ) ) ) - { - /* He/She should join the switchboard soon, let's queue the message. */ - sb->msgq = g_slist_append( sb->msgq, m ); - return( 1 ); - } - } - - debug( "Creating a new switchboard to message %s", who ); - - /* If we reach this line, there was no spare switchboard, so let's make one. */ - g_snprintf( buf, sizeof( buf ), "XFR %d SB\r\n", ++md->trId ); - if( !msn_write( ic, buf, strlen( buf ) ) ) - { - g_free( m->who ); - g_free( m->text ); - g_free( m ); - - return( 0 ); - } - - /* And queue the message to md. We'll pick it up when the switchboard comes up. */ - md->msgq = g_slist_append( md->msgq, m ); - - /* FIXME: If the switchboard creation fails, the message will not be sent. */ - - return( 1 ); + return msn_sb_write_msg( ic, m ); } return( 0 ); @@ -251,8 +216,6 @@ static void msn_chat_leave( struct groupchat *c ) static struct groupchat *msn_chat_with( struct im_connection *ic, char *who ) { struct msn_switchboard *sb; - struct msn_data *md = ic->proto_data; - char buf[1024]; if( ( sb = msn_sb_by_handle( ic, who ) ) ) { @@ -263,31 +226,13 @@ static struct groupchat *msn_chat_with( struct im_connection *ic, char *who ) { struct msn_message *m; - if( ( sb = msn_sb_spare( ic ) ) ) - { - debug( "Trying to reuse an existing switchboard as a groupchat with %s", who ); - g_snprintf( buf, sizeof( buf ), "CAL %d %s\r\n", ++sb->trId, who ); - if( msn_sb_write( sb, buf, strlen( buf ) ) ) - return msn_sb_to_chat( sb ); - } - - /* If the stuff above failed for some reason: */ - debug( "Creating a new switchboard to groupchat with %s", who ); - - /* Request a new switchboard. */ - g_snprintf( buf, sizeof( buf ), "XFR %d SB\r\n", ++md->trId ); - if( !msn_write( ic, buf, strlen( buf ) ) ) - return( 0 ); - /* Create a magic message. This is quite hackish, but who cares? :-P */ m = g_new0( struct msn_message, 1 ); m->who = g_strdup( who ); m->text = g_strdup( GROUPCHAT_SWITCHBOARD_MESSAGE ); - /* Queue the magic message and cross your fingers. */ - md->msgq = g_slist_append( md->msgq, m ); - - /* FIXME: Can I try to return something here already? */ + msn_sb_write_msg( ic, m ); + return NULL; } diff --git a/protocols/msn/msn.h b/protocols/msn/msn.h index 63759303..7c849acf 100644 --- a/protocols/msn/msn.h +++ b/protocols/msn/msn.h @@ -23,6 +23,9 @@ Suite 330, Boston, MA 02111-1307 USA */ +#ifndef _MSN_H +#define _MSN_H + /* Some hackish magicstrings to make special-purpose messages/switchboards. */ #define TYPING_NOTIFICATION_MESSAGE "\r\r\rBEWARE, ME R TYPINK MESSAGE!!!!\r\r\r" @@ -175,3 +178,6 @@ int msn_sb_sendmessage( struct msn_switchboard *sb, char *text ); struct groupchat *msn_sb_to_chat( struct msn_switchboard *sb ); void msn_sb_destroy( struct msn_switchboard *sb ); gboolean msn_sb_connected( gpointer data, gint source, b_input_condition cond ); +int msn_sb_write_msg( struct im_connection *ic, struct msn_message *m ); + +#endif //_MSN_H diff --git a/protocols/msn/ns.c b/protocols/msn/ns.c index fe48f96d..ffaa90a7 100644 --- a/protocols/msn/ns.c +++ b/protocols/msn/ns.c @@ -277,25 +277,11 @@ static int msn_ns_command( gpointer data, char **cmd, int num_parts ) { if( num_parts == 5 ) { - int i, groupcount; - - groupcount = atoi( cmd[4] ); - if( groupcount > 0 ) - { - /* valgrind says this is leaking memory, I'm guessing - that this happens during server redirects. */ - if( md->grouplist ) - { - for( i = 0; i < md->groupcount; i ++ ) - g_free( md->grouplist[i] ); - g_free( md->grouplist ); - } - - md->groupcount = groupcount; + md->buddycount = atoi( cmd[3] ); + md->groupcount = atoi( cmd[4] ); + if( md->groupcount > 0 ) md->grouplist = g_new0( char *, md->groupcount ); - } - md->buddycount = atoi( cmd[3] ); if( !*cmd[3] || md->buddycount == 0 ) msn_logged_in( ic ); } @@ -678,9 +664,6 @@ static int msn_ns_message( gpointer data, char *msg, int msglen, char **cmd, int { imcb_log( ic, "INBOX contains %s new messages, plus %s messages in other folders.", inbox, folders ); } - - g_free( inbox ); - g_free( folders ); } else if( g_strncasecmp( ct, "text/x-msmsgsemailnotification", 30 ) == 0 ) { diff --git a/protocols/msn/sb.c b/protocols/msn/sb.c index 18c41ef5..e9526234 100644 --- a/protocols/msn/sb.c +++ b/protocols/msn/sb.c @@ -47,6 +47,48 @@ int msn_sb_write( struct msn_switchboard *sb, char *s, int len ) return( 1 ); } +int msn_sb_write_msg( struct im_connection *ic, struct msn_message *m ) +{ + struct msn_data *md = ic->proto_data; + struct msn_switchboard *sb; + char buf[1024]; + + /* FIXME: *CHECK* the reliability of using spare sb's! */ + if( ( sb = msn_sb_spare( ic ) ) ) + { + debug( "Trying to use a spare switchboard to message %s", m->who ); + + sb->who = g_strdup( m->who ); + g_snprintf( buf, sizeof( buf ), "CAL %d %s\r\n", ++sb->trId, m->who ); + if( msn_sb_write( sb, buf, strlen( buf ) ) ) + { + /* He/She should join the switchboard soon, let's queue the message. */ + sb->msgq = g_slist_append( sb->msgq, m ); + return( 1 ); + } + } + + debug( "Creating a new switchboard to message %s", m->who ); + + /* If we reach this line, there was no spare switchboard, so let's make one. */ + g_snprintf( buf, sizeof( buf ), "XFR %d SB\r\n", ++md->trId ); + if( !msn_write( ic, buf, strlen( buf ) ) ) + { + g_free( m->who ); + g_free( m->text ); + g_free( m ); + + return( 0 ); + } + + /* And queue the message to md. We'll pick it up when the switchboard comes up. */ + md->msgq = g_slist_append( md->msgq, m ); + + /* FIXME: If the switchboard creation fails, the message will not be sent. */ + + return( 1 ); +} + struct msn_switchboard *msn_sb_create( struct im_connection *ic, char *host, int port, char *key, int session ) { struct msn_data *md = ic->proto_data; diff --git a/protocols/nogaim.c b/protocols/nogaim.c index 95d950ca..e6a89dfd 100644 --- a/protocols/nogaim.c +++ b/protocols/nogaim.c @@ -266,6 +266,10 @@ void imcb_connected( struct im_connection *ic ) /* Also necessary when we're not away, at least for some of the protocols. */ imc_set_away( ic, u->away ); + + /* Apparently we're connected successfully, so reset the + exponential backoff timer. */ + ic->acc->auto_reconnect_delay = 0; } gboolean auto_reconnect( gpointer data, gint fd, b_input_condition cond ) @@ -289,6 +293,7 @@ void imc_logout( struct im_connection *ic, int allow_reconnect ) irc_t *irc = ic->irc; user_t *t, *u; account_t *a; + int delay; /* Nested calls might happen sometimes, this is probably the best place to catch them. */ @@ -328,10 +333,9 @@ void imc_logout( struct im_connection *ic, int allow_reconnect ) /* Uhm... This is very sick. */ } else if( allow_reconnect && set_getbool( &irc->set, "auto_reconnect" ) && - set_getbool( &a->set, "auto_reconnect" ) ) + set_getbool( &a->set, "auto_reconnect" ) && + ( delay = account_reconnect_delay( a ) ) > 0 ) { - int delay = set_getint( &irc->set, "auto_reconnect_delay" ); - imcb_log( ic, "Reconnecting in %d seconds..", delay ); a->reconnect = b_timeout_add( delay * 1000, auto_reconnect, a ); } diff --git a/protocols/yahoo/libyahoo2.c b/protocols/yahoo/libyahoo2.c index a61955c4..897ba27b 100644 --- a/protocols/yahoo/libyahoo2.c +++ b/protocols/yahoo/libyahoo2.c @@ -68,6 +68,8 @@ char *strchr (), *strrchr (); #ifdef __MINGW32__ # include +# define write(a,b,c) send(a,b,c,0) +# define read(a,b,c) recv(a,b,c,0) #endif #include diff --git a/protocols/yahoo/yahoo.c b/protocols/yahoo/yahoo.c index 0db6e27a..8feb6639 100644 --- a/protocols/yahoo/yahoo.c +++ b/protocols/yahoo/yahoo.c @@ -665,9 +665,6 @@ void ext_yahoo_error( int id, const char *err, int fatal, int num ) struct im_connection *ic = byahoo_get_ic_by_id( id ); imcb_error( ic, "%s", err ); - - if( fatal ) - imc_logout( ic, TRUE ); } /* TODO: Clear up the mess of inp and d structures */ diff --git a/protocols/yahoo/yahoo_httplib.c b/protocols/yahoo/yahoo_httplib.c index 1b084992..dbbe2a84 100644 --- a/protocols/yahoo/yahoo_httplib.c +++ b/protocols/yahoo/yahoo_httplib.c @@ -50,6 +50,8 @@ char *strchr (), *strrchr (); #include "yahoo_debug.h" #ifdef __MINGW32__ # include +# define write(a,b,c) send(a,b,c,0) +# define read(a,b,c) recv(a,b,c,0) # define snprintf _snprintf #endif diff --git a/root_commands.c b/root_commands.c index 2f00eca8..ffa163bb 100644 --- a/root_commands.c +++ b/root_commands.c @@ -131,7 +131,7 @@ static void cmd_account( irc_t *irc, char **cmd ); static void cmd_identify( irc_t *irc, char **cmd ) { - storage_status_t status = storage_load( irc->nick, cmd[1], irc ); + storage_status_t status = storage_load( irc, cmd[1] ); char *account_on[] = { "account", "on", NULL }; switch (status) { @@ -143,6 +143,8 @@ static void cmd_identify( irc_t *irc, char **cmd ) break; case STORAGE_OK: irc_usermsg( irc, "Password accepted, settings and accounts loaded" ); + irc_setpass( irc, cmd[1] ); + irc->status |= USTATUS_IDENTIFIED; irc_umode_set( irc, "+R", 1 ); if( set_getbool( &irc->set, "auto_connect" ) ) cmd_account( irc, account_on ); @@ -162,14 +164,14 @@ static void cmd_register( irc_t *irc, char **cmd ) return; } - irc_setpass( irc, cmd[1] ); - switch( storage_save( irc, FALSE )) { + switch( storage_save( irc, cmd[1], FALSE ) ) { case STORAGE_ALREADY_EXISTS: irc_usermsg( irc, "Nick is already registered" ); break; case STORAGE_OK: irc_usermsg( irc, "Account successfully created" ); + irc_setpass( irc, cmd[1] ); irc->status |= USTATUS_IDENTIFIED; irc_umode_set( irc, "+R", 1 ); break; @@ -238,6 +240,16 @@ void cmd_account_del_no( void *data ) g_free( data ); } +static void cmd_showset( irc_t *irc, set_t **head, char *key ) +{ + char *val; + + if( ( val = set_getstr( head, key ) ) ) + irc_usermsg( irc, "%s = `%s'", key, val ); + else + irc_usermsg( irc, "%s is empty", key ); +} + static void cmd_account( irc_t *irc, char **cmd ) { account_t *a; @@ -449,6 +461,7 @@ static void cmd_account( irc_t *irc, char **cmd ) if( cmd[3] && set_name ) { set_t *s = set_find( &a->set, set_name ); + int st; if( a->ic && s && s->flags & ACC_SET_OFFLINE_ONLY ) { @@ -464,27 +477,32 @@ static void cmd_account( irc_t *irc, char **cmd ) } if( g_strncasecmp( cmd[2], "-del", 4 ) == 0 ) - set_reset( &a->set, set_name ); + st = set_reset( &a->set, set_name ); + else + st = set_setstr( &a->set, set_name, cmd[3] ); + + if( set_getstr( &a->set, set_name ) == NULL ) + { + if( st ) + irc_usermsg( irc, "Setting changed successfully" ); + else + irc_usermsg( irc, "Failed to change setting" ); + } else - set_setstr( &a->set, set_name, cmd[3] ); + { + cmd_showset( irc, &a->set, set_name ); + } } - if( set_name ) /* else 'forgotten' on purpose.. Must show new value after changing */ + else if( set_name ) { - char *s = set_getstr( &a->set, set_name ); - if( s ) - irc_usermsg( irc, "%s = `%s'", set_name, s ); - else - irc_usermsg( irc, "%s is empty", set_name ); + cmd_showset( irc, &a->set, set_name ); } else { set_t *s = a->set; while( s ) { - if( s->value || s->def ) - irc_usermsg( irc, "%s = `%s'", s->key, s->value ? s->value : s->def ); - else - irc_usermsg( irc, "%s is empty", s->key ); + cmd_showset( irc, &s, s->key ); s = s->next; } } @@ -614,6 +632,8 @@ static void cmd_rename( irc_t *irc, char **cmd ) g_free( irc->mynick ); irc->mynick = g_strdup( cmd[2] ); + /* If we're called internally (user did "set root_nick"), + let's not go O(INF). :-) */ if( strcmp( cmd[0], "set_rename" ) != 0 ) set_setstr( &irc->set, "root_nick", cmd[2] ); } @@ -637,7 +657,7 @@ char *set_eval_root_nick( set_t *set, char *new_nick ) cmd_rename( irc, cmd ); } - return strcmp( irc->mynick, new_nick ) == 0 ? new_nick : NULL; + return strcmp( irc->mynick, new_nick ) == 0 ? new_nick : SET_INVALID; } static void cmd_remove( irc_t *irc, char **cmd ) @@ -825,23 +845,37 @@ static void cmd_set( irc_t *irc, char **cmd ) if( cmd[1] && cmd[2] ) { + int st; + if( g_strncasecmp( cmd[1], "-del", 4 ) == 0 ) { - set_reset( &irc->set, cmd[2] ); + st = set_reset( &irc->set, cmd[2] ); set_name = cmd[2]; } else { - set_setstr( &irc->set, cmd[1], cmd[2] ); + st = set_setstr( &irc->set, cmd[1], cmd[2] ); + } + + /* Normally we just show the variable's new/unchanged + value as feedback to the user, but this has always + caused confusion when changing the password. Give + other feedback instead: */ + if( set_getstr( &irc->set, set_name ) == NULL ) + { + if( st ) + irc_usermsg( irc, "Setting changed successfully" ); + else + irc_usermsg( irc, "Failed to change setting" ); + } + else + { + cmd_showset( irc, &irc->set, set_name ); } } - if( set_name ) /* else 'forgotten' on purpose.. Must show new value after changing */ + else if( set_name ) { - char *s = set_getstr( &irc->set, set_name ); - if( s ) - irc_usermsg( irc, "%s = `%s'", set_name, s ); - else - irc_usermsg( irc, "%s is empty", set_name ); + cmd_showset( irc, &irc->set, set_name ); if( strchr( set_name, '/' ) ) irc_usermsg( irc, "Warning: / found in setting name, you're probably looking for the `account set' command." ); @@ -851,10 +885,7 @@ static void cmd_set( irc_t *irc, char **cmd ) set_t *s = irc->set; while( s ) { - if( s->value || s->def ) - irc_usermsg( irc, "%s = `%s'", s->key, s->value ? s->value : s->def ); - else - irc_usermsg( irc, "%s is empty", s->key ); + cmd_showset( irc, &s, s->key ); s = s->next; } } @@ -862,7 +893,9 @@ static void cmd_set( irc_t *irc, char **cmd ) static void cmd_save( irc_t *irc, char **cmd ) { - if( storage_save( irc, TRUE ) == STORAGE_OK ) + if( ( irc->status & USTATUS_IDENTIFIED ) == 0 ) + irc_usermsg( irc, "Please create an account first" ); + else if( storage_save( irc, NULL, TRUE ) == STORAGE_OK ) irc_usermsg( irc, "Configuration saved" ); else irc_usermsg( irc, "Configuration could not be saved!" ); diff --git a/set.c b/set.c index b18d7cc8..764ca8b7 100644 --- a/set.c +++ b/set.c @@ -25,6 +25,9 @@ #define BITLBEE_CORE #include "bitlbee.h" +/* Used to use NULL for this, but NULL is actually a "valid" value. */ +char *SET_INVALID = "nee"; + set_t *set_add( set_t **head, char *key, char *def, set_eval eval, void *data ) { set_t *s = set_find( head, key ); @@ -113,9 +116,20 @@ int set_setstr( set_t **head, char *key, char *value ) char *nv = value; if( !s ) + /* + Used to do this, but it never really made sense. s = set_add( head, key, NULL, NULL, NULL ); + */ + return 0; - if( s->eval && !( nv = s->eval( s, value ) ) ) + if( value == NULL && ( s->flags & SET_NULL_OK ) == 0 ) + return 0; + + /* Call the evaluator. For invalid values, evaluators should now + return SET_INVALID, but previously this was NULL. Try to handle + that too if NULL is not an allowed value for this setting. */ + if( s->eval && ( ( nv = s->eval( s, value ) ) == SET_INVALID || + ( ( s->flags & SET_NULL_OK ) == 0 && nv == NULL ) ) ) return 0; if( s->value ) @@ -167,13 +181,15 @@ void set_del( set_t **head, char *key ) } } -void set_reset( set_t **head, char *key ) +int set_reset( set_t **head, char *key ) { set_t *s; s = set_find( head, key ); if( s ) - set_setstr( head, key, s->def ); + return set_setstr( head, key, s->def ); + + return 0; } char *set_eval_int( set_t *set, char *value ) @@ -186,14 +202,14 @@ char *set_eval_int( set_t *set, char *value ) for( ; *s; s ++ ) if( !isdigit( *s ) ) - return NULL; + return SET_INVALID; return value; } char *set_eval_bool( set_t *set, char *value ) { - return is_bool( value ) ? value : NULL; + return is_bool( value ) ? value : SET_INVALID; } char *set_eval_to_char( set_t *set, char *value ) @@ -208,25 +224,27 @@ char *set_eval_to_char( set_t *set, char *value ) return s; } -char *set_eval_op_root( set_t *set, char *value ) +char* set_eval_op_root( set_t *set, char* value ) { irc_t *irc = set->data; - char *ret = set_eval_bool(set, value); + char* ret = set_eval_bool(set, value); int b = bool2int(ret); - + irc_write( irc, ":%s!%s@%s MODE %s %s %s", irc->mynick, irc->mynick, irc->myhost, - irc->channel, b?"+o":"-o", irc->mynick ); + irc->channel, b?"+o":"-o", irc->mynick); + return ret; } -char *set_eval_op_user( set_t *set, char *value ) +char* set_eval_op_user( set_t *set, char* value ) { irc_t *irc = set->data; - char *ret = set_eval_bool(set, value); + char* ret = set_eval_bool(set, value); int b = bool2int(ret); - + irc_write( irc, ":%s!%s@%s MODE %s %s %s", irc->mynick, irc->mynick, irc->myhost, - irc->channel, b?"+o":"-o", irc->nick ); + irc->channel, b?"+o":"-o", irc->nick); + return ret; } @@ -249,7 +267,7 @@ char *set_eval_mode_buddies( set_t *set, char *value, char modeflag ) mode=3; else return NULL; - + /* sorry for calling them op/deop - too lazy for search+replace :P */ op[0]='\0'; deop[0]='\0'; @@ -334,10 +352,10 @@ char *set_eval_mode_buddies( set_t *set, char *value, char modeflag ) if(*deop) { char *flags = g_strnfill(ndeop, modeflag); irc_write( irc, ":%s!%s@%s MODE %s -%s%s", irc->mynick, irc->mynick, irc->myhost, - irc->channel, flags, deop ); - g_free(flags); - } - + irc->channel, flags, deop ); + g_free(flags); + } + return value; } @@ -369,3 +387,4 @@ char *set_eval_otr_policy( set_t *set, char *value ) return value; return NULL; } + diff --git a/set.h b/set.h index 988429ff..05f879c0 100644 --- a/set.h +++ b/set.h @@ -43,6 +43,10 @@ struct set; typedef char *(*set_eval) ( struct set *set, char *value ); +extern char *SET_INVALID; + +#define SET_NULL_OK 0x0100 + typedef struct set { void *data; /* Here you can save a pointer to the @@ -60,8 +64,8 @@ typedef struct set int flags; /* See account.h, for example. set.c doesn't use this (yet?). */ - /* Eval: Returns NULL if the value is incorrect or exactly the - passed value variable. When returning a corrected value, + /* Eval: Returns SET_INVALID if the value is incorrect or exactly + the passed value variable. When returning a corrected value, set_setstr() should be able to free() the returned string! */ set_eval eval; struct set *next; @@ -87,7 +91,7 @@ G_MODULE_EXPORT int set_getbool( set_t **head, char *key ); int set_setstr( set_t **head, char *key, char *value ); int set_setint( set_t **head, char *key, int value ); void set_del( set_t **head, char *key ); -void set_reset( set_t **head, char *key ); +int set_reset( set_t **head, char *key ); /* Two very useful generic evaluators. */ char *set_eval_int( set_t *set, char *value ); diff --git a/sock.h b/sock.h index 848dc466..c3c0428e 100644 --- a/sock.h +++ b/sock.h @@ -15,11 +15,17 @@ #endif #else # include -# include +# ifndef _MSC_VER +# include +# endif # if !defined(BITLBEE_CORE) && defined(_MSC_VER) # pragma comment(lib,"bitlbee.lib") # endif # include +# define read(a,b,c) recv(a,b,c,0) +# define write(a,b,c) send(a,b,c,0) +# define umask _umask +# define mode_t int # define sock_make_nonblocking(fd) { int non_block = 1; ioctlsocket(fd, FIONBIO, &non_block); } # define sock_make_blocking(fd) { int non_block = 0; ioctlsocket(fd, FIONBIO, &non_block); } # define sockerr_again() (WSAGetLastError() == WSAEINTR || WSAGetLastError() == WSAEINPROGRESS || WSAGetLastError() == WSAEWOULDBLOCK) diff --git a/storage.c b/storage.c index 6a62af83..0ff2d34d 100644 --- a/storage.c +++ b/storage.c @@ -103,36 +103,52 @@ storage_status_t storage_check_pass (const char *nick, const char *password) return STORAGE_NO_SUCH_USER; } -storage_status_t storage_load (const char *nick, const char *password, irc_t * irc) +storage_status_t storage_load (irc_t * irc, const char *password) { GList *gl; + if (irc && irc->status & USTATUS_IDENTIFIED) + return STORAGE_OTHER_ERROR; + /* Loop until we don't get NO_SUCH_USER */ for (gl = global.storage; gl; gl = gl->next) { storage_t *st = gl->data; storage_status_t status; - status = st->load(nick, password, irc); + status = st->load(irc, password); if (status == STORAGE_OK) { - irc_setpass(irc, password); - otr_load(irc); /* load our OTR userstate */ + otr_load(irc); return status; } - if (status != STORAGE_NO_SUCH_USER) { + if (status != STORAGE_NO_SUCH_USER) return status; - } } return STORAGE_NO_SUCH_USER; } -storage_status_t storage_save (irc_t *irc, int overwrite) +storage_status_t storage_save (irc_t *irc, char *password, int overwrite) { storage_status_t st; + if (password != NULL) { + /* Should only use this in the "register" command. */ + if (irc->password || overwrite) + return STORAGE_OTHER_ERROR; + + irc_setpass(irc, password); + } else if ((irc->status & USTATUS_IDENTIFIED) == 0) { + return STORAGE_NO_SUCH_USER; + } + otr_save(irc); st = ((storage_t *)global.storage->data)->save(irc, overwrite); + + if (password != NULL) { + irc_setpass(irc, NULL); + } + return st; } @@ -149,8 +165,7 @@ storage_status_t storage_remove (const char *nick, const char *password) storage_status_t status; status = st->remove(nick, password); - if (status != STORAGE_NO_SUCH_USER && - status != STORAGE_OK) + if (status != STORAGE_NO_SUCH_USER && status != STORAGE_OK) ret = status; } if (ret == STORAGE_OK) { @@ -160,6 +175,9 @@ storage_status_t storage_remove (const char *nick, const char *password) return ret; } +#if 0 +Not using this yet. Test thoroughly before adding UI hooks to this function. + storage_status_t storage_rename (const char *onick, const char *nnick, const char *password) { storage_status_t status; @@ -201,3 +219,4 @@ storage_status_t storage_rename (const char *onick, const char *nnick, const cha return STORAGE_OK; } +#endif diff --git a/storage.h b/storage.h index d114dec4..f2e9afce 100644 --- a/storage.h +++ b/storage.h @@ -44,7 +44,7 @@ typedef struct { storage_status_t (*check_pass) (const char *nick, const char *password); - storage_status_t (*load) (const char *nick, const char *password, irc_t * irc); + storage_status_t (*load) (irc_t *irc, const char *password); storage_status_t (*save) (irc_t *irc, int overwrite); storage_status_t (*remove) (const char *nick, const char *password); @@ -54,11 +54,11 @@ typedef struct { storage_status_t storage_check_pass (const char *nick, const char *password); -storage_status_t storage_load (const char *nick, const char *password, irc_t * irc); -storage_status_t storage_save (irc_t *irc, int overwrite); +storage_status_t storage_load (irc_t * irc, const char *password); +storage_status_t storage_save (irc_t *irc, char *password, int overwrite); storage_status_t storage_remove (const char *nick, const char *password); -storage_status_t storage_rename (const char *onick, const char *nnick, const char *password); +/* storage_status_t storage_rename (const char *onick, const char *nnick, const char *password); */ void register_storage_backend(storage_t *); G_GNUC_MALLOC GList *storage_init(const char *primary, char **migrate); diff --git a/storage_text.c b/storage_text.c index 78f7e3bd..003bde44 100644 --- a/storage_text.c +++ b/storage_text.c @@ -26,14 +26,6 @@ #define BITLBEE_CORE #include "bitlbee.h" #include "crypting.h" -#ifdef _WIN32 -# define umask _umask -# define mode_t int -#endif - -#ifndef F_OK -#define F_OK 0 -#endif static void text_init (void) { @@ -43,7 +35,7 @@ static void text_init (void) it's read only! */ } -static storage_status_t text_load ( const char *my_nick, const char* password, irc_t *irc ) +static storage_status_t text_load( irc_t *irc, const char* password ) { char s[512]; char *line; @@ -53,10 +45,7 @@ static storage_status_t text_load ( const char *my_nick, const char* password, i user_t *ru = user_find( irc, ROOT_NICK ); account_t *acc, *acc_lookup[9]; - if( irc->status & USTATUS_IDENTIFIED ) - return( 1 ); - - g_snprintf( s, 511, "%s%s%s", global.conf->configdir, my_nick, ".accounts" ); + g_snprintf( s, 511, "%s%s%s", global.conf->configdir, irc->nick, ".accounts" ); fp = fopen( s, "r" ); if( !fp ) return STORAGE_NO_SUCH_USER; @@ -68,10 +57,6 @@ static storage_status_t text_load ( const char *my_nick, const char* password, i return STORAGE_INVALID_PASSWORD; } - /* Do this now. If the user runs with AuthMode = Registered, the - account command will not work otherwise. */ - irc->status |= USTATUS_IDENTIFIED; - while( fscanf( fp, "%511[^\n]s", s ) > 0 ) { fgetc( fp ); @@ -100,7 +85,7 @@ static storage_status_t text_load ( const char *my_nick, const char* password, i acc_lookup[8] = acc; } - g_snprintf( s, 511, "%s%s%s", global.conf->configdir, my_nick, ".nicks" ); + g_snprintf( s, 511, "%s%s%s", global.conf->configdir, irc->nick, ".nicks" ); fp = fopen( s, "r" ); if( !fp ) return STORAGE_NO_SUCH_USER; while( fscanf( fp, "%s %d %s", s, &proto, nick ) > 0 ) diff --git a/storage_xml.c b/storage_xml.c index 240206f1..09e2f328 100644 --- a/storage_xml.c +++ b/storage_xml.c @@ -28,7 +28,14 @@ #include "base64.h" #include "arc.h" #include "md5.h" + +#if GLIB_CHECK_VERSION(2,8,0) #include +#else +/* GLib < 2.8.0 doesn't have g_access, so just use the system access(). */ +#include +#define g_access access +#endif #if !GLIB_CHECK_VERSION(2,8,0) /* GLib < 2.8.0 doesn't have g_access, so just use the system access(). */ @@ -255,7 +262,7 @@ static void xml_init( void ) log_message( LOGLVL_WARNING, "Permission problem: Can't read/write from/to `%s'.", global.conf->configdir ); } -static storage_status_t xml_load_real( const char *my_nick, const char *password, irc_t *irc, xml_pass_st action ) +static storage_status_t xml_load_real( irc_t *irc, const char *my_nick, const char *password, xml_pass_st action ) { GMarkupParseContext *ctx; struct xml_parsedata *xd; @@ -263,9 +270,6 @@ static storage_status_t xml_load_real( const char *my_nick, const char *password GError *gerr = NULL; int fd, st; - if( irc && irc->status & USTATUS_IDENTIFIED ) - return( 1 ); - xd = g_new0( struct xml_parsedata, 1 ); xd->irc = irc; xd->given_nick = g_strdup( my_nick ); @@ -317,21 +321,19 @@ static storage_status_t xml_load_real( const char *my_nick, const char *password if( action == XML_PASS_CHECK_ONLY ) return STORAGE_OK; - irc->status |= USTATUS_IDENTIFIED; - return STORAGE_OK; } -static storage_status_t xml_load( const char *my_nick, const char *password, irc_t *irc ) +static storage_status_t xml_load( irc_t *irc, const char *password ) { - return xml_load_real( my_nick, password, irc, XML_PASS_UNKNOWN ); + return xml_load_real( irc, irc->nick, password, XML_PASS_UNKNOWN ); } static storage_status_t xml_check_pass( const char *my_nick, const char *password ) { /* This is a little bit risky because we have to pass NULL for the irc_t argument. This *should* be fine, if I didn't miss anything... */ - return xml_load_real( my_nick, password, NULL, XML_PASS_CHECK_ONLY ); + return xml_load_real( NULL, my_nick, password, XML_PASS_CHECK_ONLY ); } static int xml_printf( int fd, int indent, char *fmt, ... ) @@ -367,12 +369,6 @@ static storage_status_t xml_save( irc_t *irc, int overwrite ) md5_byte_t pass_md5[21]; md5_state_t md5_state; - if( irc->password == NULL ) - { - irc_usermsg( irc, "Please register yourself if you want to save your settings." ); - return STORAGE_OTHER_ERROR; - } - path2 = g_strdup( irc->nick ); nick_lc( path2 ); g_snprintf( path, sizeof( path ) - 2, "%s%s%s", global.conf->configdir, path2, ".xml" ); diff --git a/unix.c b/unix.c index 6fc85dbb..8a828eec 100644 --- a/unix.c +++ b/unix.c @@ -55,13 +55,13 @@ int main( int argc, char *argv[] ) b_main_init(); nogaim_init(); - /* Ugly Note: libotr and gnutls both use libgcrypt. libgcrypt - has a process-global config state whose initialization happpens - twice if libotr and gnutls are used together. libotr installs custom - memory management functions for libgcrypt while our gnutls module - uses the defaults. Therefore we initialize OTR after SSL. *sigh* */ - ssl_init(); - otr_init(); + /* Ugly Note: libotr and gnutls both use libgcrypt. libgcrypt + has a process-global config state whose initialization happpens + twice if libotr and gnutls are used together. libotr installs custom + memory management functions for libgcrypt while our gnutls module + uses the defaults. Therefore we initialize OTR after SSL. *sigh* */ + ssl_init(); + otr_init(); srand( time( NULL ) ^ getpid() ); global.helpfile = g_strdup( HELP_FILE ); @@ -222,5 +222,3 @@ double gettime() gettimeofday( time, 0 ); return( (double) time->tv_sec + (double) time->tv_usec / 1000000 ); } - - -- cgit v1.2.3 From fc34fb5d0b717d90edfc7ed78f11166eb23c536e Mon Sep 17 00:00:00 2001 From: Sven Moritz Hallberg Date: Fri, 13 Mar 2009 12:31:47 +0100 Subject: dont specify a background when coloring encrypted messages --- otr.c | 9 ++++++++- 1 file changed, 8 insertions(+), 1 deletion(-) diff --git a/otr.c b/otr.c index 8d12361b..d69b9a87 100644 --- a/otr.c +++ b/otr.c @@ -349,7 +349,14 @@ char *otr_handle_message(struct im_connection *ic, const char *handle, const cha color=3; /* green */ else color=5; /* red */ - colormsg = g_strdup_printf("\x03%.2d,00%s\x0F", color, newmsg); + + if(newmsg[0] == ',') { + /* could be a problem with the color code */ + /* insert a space between color spec and message */ + colormsg = g_strdup_printf("\x03%.2d %s\x0F", color, newmsg); + } else { + colormsg = g_strdup_printf("\x03%.2d%s\x0F", color, newmsg); + } } else { colormsg = g_strdup(newmsg); } -- cgit v1.2.3 From a6b2f13e38e75e7bca00a1e6c1963783f244c0b2 Mon Sep 17 00:00:00 2001 From: Sven Moritz Hallberg Date: Thu, 3 Jun 2010 18:18:46 +0200 Subject: fix a double free --- protocols/nogaim.c | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/protocols/nogaim.c b/protocols/nogaim.c index f0c87c20..ebb00418 100644 --- a/protocols/nogaim.c +++ b/protocols/nogaim.c @@ -786,8 +786,8 @@ void imcb_buddy_msg( struct im_connection *ic, const char *handle, char *msg, ui ( ts = format_timestamp( irc, sent_at ) ) ) { char *new = g_strconcat( ts, msg, NULL ); - g_free( ts ); - ts = msg = new; + g_free( msg ); + msg = new; } wrapped = word_wrap( msg, 425 ); -- cgit v1.2.3 From 1dd34701541fb8142402f593dc256ff9b54121bb Mon Sep 17 00:00:00 2001 From: Sven Moritz Hallberg Date: Thu, 3 Jun 2010 23:47:53 +0200 Subject: add an option to disable otr on twitter (and other unsuitable protocols) --- otr.c | 15 +++++++++++++++ protocols/nogaim.h | 9 +++++---- protocols/twitter/twitter.c | 1 + 3 files changed, 21 insertions(+), 4 deletions(-) diff --git a/otr.c b/otr.c index d69b9a87..c6ba32a4 100644 --- a/otr.c +++ b/otr.c @@ -303,6 +303,11 @@ int otr_check_for_key(account_t *a) irc_t *irc = a->irc; OtrlPrivKey *k; + /* don't do OTR on certain (not classic IM) protocols, e.g. twitter */ + if(a->prpl->options & OPT_NOOTR) { + return 0; + } + k = otrl_privkey_find(irc->otr->us, a->user, a->prpl->name); if(k) { irc_usermsg(irc, "otr: %s/%s ready", a->user, a->prpl->name); @@ -324,6 +329,11 @@ char *otr_handle_message(struct im_connection *ic, const char *handle, const cha OtrlTLV *tlvs = NULL; char *colormsg; + /* don't do OTR on certain (not classic IM) protocols, e.g. twitter */ + if(ic->acc->prpl->options & OPT_NOOTR) { + return (g_strdup(msg)); + } + ignore_msg = otrl_message_receiving(ic->irc->otr->us, &global.otr_ops, ic, ic->acc->user, ic->acc->prpl->name, handle, msg, &newmsg, &tlvs, NULL, NULL); @@ -370,6 +380,11 @@ int otr_send_message(struct im_connection *ic, const char *handle, const char *m int st; char *otrmsg = NULL; ConnContext *ctx = NULL; + + /* don't do OTR on certain (not classic IM) protocols, e.g. twitter */ + if(ic->acc->prpl->options & OPT_NOOTR) { + return (ic->acc->prpl->buddy_msg(ic, handle, msg, flags)); + } st = otrl_message_sending(ic->irc->otr->us, &global.otr_ops, ic, ic->acc->user, ic->acc->prpl->name, handle, diff --git a/protocols/nogaim.h b/protocols/nogaim.h index d3f5847f..62d800c6 100644 --- a/protocols/nogaim.h +++ b/protocols/nogaim.h @@ -58,6 +58,7 @@ #define OPT_LOCALBUDDY 0x00000020 /* For nicks local to one groupchat */ #define OPT_TYPING 0x00000100 /* Some pieces of code make assumptions */ #define OPT_THINKING 0x00000200 /* about these values... Stupid me! */ +#define OPT_NOOTR 0x00001000 /* protocol not suitable for OTR */ /* ok. now the fun begins. first we create a connection structure */ struct im_connection @@ -131,10 +132,10 @@ struct prpl { /* You should set this to the name of your protocol. * - The user sees this name ie. when imcb_log() is used. */ const char *name; - /* Maximum Message Size of this protocol. - * - Introduced for OTR, in order to fragment large protocol messages. - * - 0 means "unlimited". */ - unsigned int mms; + /* Maximum Message Size of this protocol. + * - Introduced for OTR, in order to fragment large protocol messages. + * - 0 means "unlimited". */ + unsigned int mms; /* Added this one to be able to add per-account settings, don't think * it should be used for anything else. You are supposed to use the diff --git a/protocols/twitter/twitter.c b/protocols/twitter/twitter.c index 98e85641..afa2fb32 100644 --- a/protocols/twitter/twitter.c +++ b/protocols/twitter/twitter.c @@ -335,6 +335,7 @@ void twitter_initmodule() { struct prpl *ret = g_new0(struct prpl, 1); + ret->options = OPT_NOOTR; ret->name = "twitter"; ret->login = twitter_login; ret->init = twitter_init; -- cgit v1.2.3