diff -Nru bitlbee-3.4.1/bitlbee.c bitlbee-3.4.2/bitlbee.c --- bitlbee-3.4.1/bitlbee.c 2015-06-16 22:30:40.000000000 +0000 +++ bitlbee-3.4.2/bitlbee.c 2016-03-19 23:37:33.000000000 +0000 @@ -310,6 +310,9 @@ close(global.listen_socket); b_event_remove(global.listen_watch_source_id); + /* Make a new pipe for the shutdown signal handler */ + sighandler_shutdown_setup(); + /* Make the connection. */ irc = irc_new(new_socket); diff -Nru bitlbee-3.4.1/bitlbee.h bitlbee-3.4.2/bitlbee.h --- bitlbee-3.4.1/bitlbee.h 2015-06-16 22:30:40.000000000 +0000 +++ bitlbee-3.4.2/bitlbee.h 2016-03-19 23:37:33.000000000 +0000 @@ -35,10 +35,10 @@ #endif #define PACKAGE "BitlBee" -#define BITLBEE_VERSION "3.4.1" +#define BITLBEE_VERSION "3.4.2" #define VERSION BITLBEE_VERSION #define BITLBEE_VER(a, b, c) (((a) << 16) + ((b) << 8) + (c)) -#define BITLBEE_VERSION_CODE BITLBEE_VER(3, 4, 1) +#define BITLBEE_VERSION_CODE BITLBEE_VER(3, 4, 2) #define MAX_STRING 511 @@ -157,6 +157,8 @@ int restart; } global_t; +void sighandler_shutdown_setup(void); + int bitlbee_daemon_init(void); int bitlbee_inetd_init(void); diff -Nru bitlbee-3.4.1/conf.c bitlbee-3.4.2/conf.c --- bitlbee-3.4.1/conf.c 2015-06-16 22:30:40.000000000 +0000 +++ bitlbee-3.4.2/conf.c 2016-03-19 23:37:33.000000000 +0000 @@ -36,6 +36,7 @@ #include "proxy.h" static int conf_loadini(conf_t *conf, char *file); +static void conf_free(conf_t *conf); conf_t *conf_load(int argc, char *argv[]) { @@ -72,6 +73,7 @@ i = conf_loadini(conf, global.conf_file); if (i == 0) { fprintf(stderr, "Error: Syntax error in configuration file `%s'.\n", global.conf_file); + conf_free(conf); return NULL; } else if (i == -1) { config_missing++; @@ -103,7 +105,7 @@ if (strcmp(global.conf_file, optarg) != 0) { g_free(global.conf_file); global.conf_file = g_strdup(optarg); - g_free(conf); + conf_free(conf); /* Re-evaluate arguments. Don't use this option twice, you'll end up in an infinite loop! Hope this trick works with all libcs BTW.. */ @@ -134,10 +136,12 @@ " -x Command-line interface to password encryption/hashing\n" " -h Show this help page.\n" " -V Show version info.\n"); + conf_free(conf); return NULL; } else if (opt == 'V') { - printf("BitlBee %s\nAPI version %06x\n", - BITLBEE_VERSION, BITLBEE_VERSION_CODE); + printf("BitlBee %s\nAPI version %06x\nConfigure args: %s\n", + BITLBEE_VERSION, BITLBEE_VERSION_CODE, BITLBEE_CONFIGURE_ARGS); + conf_free(conf); return NULL; } else if (opt == 'u') { g_free(conf->user); @@ -161,12 +165,41 @@ /* Let's treat this as a serious problem so people won't think they're secure when in fact they're not. */ fprintf(stderr, "Error: Could not read CA file %s: %s\n", conf->cafile, strerror(errno)); + conf_free(conf); return NULL; } return conf; } +static void conf_free(conf_t *conf) +{ + /* Free software means users have the four essential freedoms: + 0. to run the program, + 2. to study and change the program in source code form, + 2. to redistribute exact copies, and + 3. to distribute modified versions + */ + g_free(conf->auth_pass); + g_free(conf->cafile); + g_free(conf->configdir); + g_free(conf->ft_listen); + g_free(conf->hostname); + g_free(conf->iface_in); + g_free(conf->iface_out); + g_free(conf->motdfile); + g_free(conf->oper_pass); + g_free(conf->pidfile); + g_free(conf->plugindir); + g_free(conf->port); + g_free(conf->primary_storage); + g_free(conf->user); + g_strfreev(conf->migrate_storage); + g_strfreev(conf->protocols); + g_free(conf); + +} + static int conf_loadini(conf_t *conf, char *file) { ini_t *ini; @@ -218,6 +251,9 @@ } else if (g_strcasecmp(ini->key, "configdir") == 0) { g_free(conf->configdir); conf->configdir = g_strdup(ini->value); + } else if (g_strcasecmp(ini->key, "plugindir") == 0) { + g_free(conf->plugindir); + conf->plugindir = g_strdup(ini->value); } else if (g_strcasecmp(ini->key, "motdfile") == 0) { g_free(conf->motdfile); conf->motdfile = g_strdup(ini->value); @@ -258,6 +294,8 @@ proxytype = PROXY_SOCKS4; } else if (url->proto == PROTO_SOCKS5) { proxytype = PROXY_SOCKS5; + } else if (url->proto == PROTO_SOCKS4A) { + proxytype = PROXY_SOCKS4A; } g_free(url); diff -Nru bitlbee-3.4.1/configure bitlbee-3.4.2/configure --- bitlbee-3.4.1/configure 2015-06-16 22:30:40.000000000 +0000 +++ bitlbee-3.4.2/configure 2016-03-19 23:37:33.000000000 +0000 @@ -22,6 +22,9 @@ ipcsocket='' pcdir='$prefix/lib/pkgconfig' systemlibdirs="/lib64 /usr/lib64 /usr/local/lib64 /lib /usr/lib /usr/local/lib" +sysroot='' + +configure_args="$@" # Set these to default-on to let it be overriden by either the user or purple # @@ -50,8 +53,8 @@ pie=1 -arch=`uname -s` -cpu=`uname -m` +arch=$(uname -s) +cpu=$(uname -m) GLIB_MIN_VERSION=2.16 @@ -102,7 +105,7 @@ echo BitlBee configure while [ -n "$1" ]; do - e="`expr "X$1" : 'X--\(.*=.*\)'`" + e="$(expr "X$1" : 'X--\(.*=.*\)')" if [ -z "$e" ]; then cat<Makefile.settings ## BitlBee settings, generated by configure + +# ./configure $configure_args + PREFIX=$prefix BINDIR=$bindir SBINDIR=$sbindir @@ -226,6 +233,8 @@ Do *NOT* use any of these defines in your code without thinking twice, most of them can/will be overridden at run-time */ +#define BITLBEE_CONFIGURE_ARGS "$configure_args" + #define CONFIG "$config" #define ETCDIR "$etcdir" #define VARDIR "$datadir" @@ -239,12 +248,33 @@ if [ -n "$target" ]; then - PKG_CONFIG_LIBDIR=/usr/$target/lib/pkgconfig - export PKG_CONFIG_LIBDIR - PATH=/usr/$target/bin:$PATH + # prepend sysroot to system lib dirs + + systemlibdirs_cross='' + for i in $systemlibdirs; do + systemlibdirs_cross="$systemlibdirs_cross $sysroot$i" + done + systemlibdirs=$systemlibdirs_cross + unset systemlibdirs_cross + + # backward compatibility + + if [ -z "$PKG_CONFIG_LIBDIR" ]; then + PKG_CONFIG_LIBDIR=/usr/$target/lib/pkgconfig + export PKG_CONFIG_LIBDIR + fi + + if [ -d /usr/$target/bin ]; then + PATH=/usr/$target/bin:$PATH + fi + + if [ -d /usr/$target/lib ]; then + systemlibdirs="$systemlibdirs /usr/$target/lib" + fi + CC=$target-cc LD=$target-ld - systemlibdirs="/usr/$target/lib" + STRIP=$target-strip fi if [ "$asan" = "1" ]; then @@ -307,12 +337,12 @@ 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) +CFLAGS+=$($PKG_CONFIG --cflags glib-2.0 gmodule-2.0) EOF else echo - echo 'Found glib2 '`$PKG_CONFIG glib-2.0 --modversion`', but version '$GLIB_MIN_VERSION' or newer is required.' + echo 'Found glib2 '$($PKG_CONFIG glib-2.0 --modversion)', but version '$GLIB_MIN_VERSION' or newer is required.' exit 1 fi else @@ -348,19 +378,19 @@ { if $PKG_CONFIG --exists gnutls; then cat <>Makefile.settings -EFLAGS+=`$PKG_CONFIG --libs gnutls` `libgcrypt-config --libs` -CFLAGS+=`$PKG_CONFIG --cflags gnutls` `libgcrypt-config --cflags` +EFLAGS+=$($PKG_CONFIG --libs gnutls) $(libgcrypt-config --libs) +CFLAGS+=$($PKG_CONFIG --cflags gnutls) $(libgcrypt-config --cflags) EOF ssl=gnutls - if ! pkg-config gnutls --atleast-version=2.8; then + if ! $PKG_CONFIG gnutls --atleast-version=2.8; then echo echo 'Warning: With GnuTLS versions <2.8, certificate expire dates are not verified.' fi ret=1 elif libgnutls-config --version > /dev/null 2> /dev/null; then cat <>Makefile.settings -EFLAGS+=`libgnutls-config --libs` `libgcrypt-config --libs` -CFLAGS+=`libgnutls-config --cflags` `libgcrypt-config --cflags` +EFLAGS+=$(libgnutls-config --libs) $(libgcrypt-config --libs) +CFLAGS+=$(libgnutls-config --cflags) $(libgcrypt-config --cflags) EOF ssl=gnutls @@ -374,8 +404,8 @@ { if $PKG_CONFIG --version > /dev/null 2>/dev/null && $PKG_CONFIG nss; then cat<>Makefile.settings -EFLAGS+=`$PKG_CONFIG --libs nss` -CFLAGS+=`$PKG_CONFIG --cflags nss` +EFLAGS+=$($PKG_CONFIG --libs nss) +CFLAGS+=$($PKG_CONFIG --cflags nss) EOF ssl=nss @@ -620,7 +650,7 @@ if [ -z "$systemdsystemunitdir" ]; then if $PKG_CONFIG --exists systemd; then - systemdsystemunitdir=`$PKG_CONFIG --variable=systemdsystemunitdir systemd` + systemdsystemunitdir=$($PKG_CONFIG --variable=systemdsystemunitdir systemd) fi fi if [ -n "$systemdsystemunitdir" ]; then @@ -642,37 +672,29 @@ fi otrprefix="" -for i in / /usr /usr/local; do - if [ -f ${i}/lib/libotr.a ]; then - otrprefix=${i} - break - fi -done if [ "$otr" = "auto" ]; then - if [ -n "$otrprefix" ]; then - otr=1 - else - otr=0 - fi + ! $PKG_CONFIG --exists libotr + otr=$? fi + +if [ "$otr" != 0 ] && ! $PKG_CONFIG --atleast-version=4.0 --print-errors libotr; then + exit 1 +fi + if [ "$otr" = 1 ]; then # BI == built-in echo '#define OTR_BI' >> config.h - echo "EFLAGS+=-L${otrprefix}/lib -lotr -lgcrypt" >> Makefile.settings - echo "CFLAGS+=-I${otrprefix}/include" >> Makefile.settings + echo "EFLAGS+=$($PKG_CONFIG --libs libotr) $(libgcrypt-config --libs)" >> Makefile.settings + echo "CFLAGS+=$($PKG_CONFIG --cflags libotr) $(libgcrypt-config --cflags)" >> Makefile.settings echo 'OTR_BI=otr.o' >> Makefile.settings elif [ "$otr" = "plugin" ]; then + # for some mysterious reason beyond the comprehension of my mortal mind, + # the libgcrypt flags aren't needed when building as plugin. add them anyway. echo '#define OTR_PI' >> config.h - echo "OTRFLAGS=-L${otrprefix}/lib -lotr" >> Makefile.settings - echo "CFLAGS+=-I${otrprefix}/include" >> Makefile.settings + echo "OTRFLAGS=$($PKG_CONFIG --libs libotr) $(libgcrypt-config --libs)" >> Makefile.settings + echo "CFLAGS+=$($PKG_CONFIG --cflags libotr) $(libgcrypt-config --cflags)" >> Makefile.settings echo 'OTR_PI=otr.so' >> Makefile.settings fi -if [ "$otr" != 0 ] && ! pkg-config libotr --atleast-version=4.0; then - echo - echo 'WARNING: Your libotr seems to be old. BitlBee now needs at least libotr 4.0.' - # Not hard-failing because the code above doesn't use pkg-config, so who knows - # what's true at this point... -fi if [ "$skype" = "1" -o "$skype" = "plugin" ]; then if [ "$arch" = "Darwin" ]; then diff -Nru bitlbee-3.4.1/dcc.c bitlbee-3.4.2/dcc.c --- bitlbee-3.4.1/dcc.c 2015-06-16 22:30:40.000000000 +0000 +++ bitlbee-3.4.2/dcc.c 2016-03-19 23:37:33.000000000 +0000 @@ -264,7 +264,7 @@ return dcc_abort(df, "Remote end closed connection"); } - /* How likely is it that a 32-bit integer gets split accross + /* How likely is it that a 32-bit integer gets split across packet boundaries? Chances are rarely 0 so let's be sure. */ if ((df->acked_len = (df->acked_len + ret) % 4) > 0) { return TRUE; diff -Nru bitlbee-3.4.1/debian/changelog bitlbee-3.4.2/debian/changelog --- bitlbee-3.4.1/debian/changelog 2016-03-20 18:39:18.000000000 +0000 +++ bitlbee-3.4.2/debian/changelog 2016-03-20 18:39:18.000000000 +0000 @@ -1,3 +1,9 @@ +bitlbee (3.4.2-0ubuntu1) xenial; urgency=medium + + * New upstream release. + + -- Unit 193 Sat, 19 Mar 2016 22:47:44 -0400 + bitlbee (3.4.1-1build1) xenial; urgency=medium * No-change rebuild for gnutls transition. diff -Nru bitlbee-3.4.1/debian/control bitlbee-3.4.2/debian/control --- bitlbee-3.4.1/debian/control 2016-03-20 18:39:18.000000000 +0000 +++ bitlbee-3.4.2/debian/control 2016-03-20 18:39:18.000000000 +0000 @@ -1,7 +1,8 @@ Source: bitlbee Section: net Priority: optional -Maintainer: Wilmer van der Gaast +Maintainer: Ubuntu Developers +XSBC-Original-Maintainer: Wilmer van der Gaast Uploaders: Jelmer Vernooij Standards-Version: 3.9.5 Build-Depends: libglib2.0-dev (>= 2.4), libevent-dev, libgnutls28-dev | libgnutls-dev | gnutls-dev, po-debconf, libpurple-dev, libotr5-dev, debhelper (>= 6.0.7~) diff -Nru bitlbee-3.4.1/doc/CHANGES bitlbee-3.4.2/doc/CHANGES --- bitlbee-3.4.1/doc/CHANGES 2015-06-16 22:39:11.000000000 +0000 +++ bitlbee-3.4.2/doc/CHANGES 2016-03-19 23:38:02.000000000 +0000 @@ -3,6 +3,53 @@ https://github.com/bitlbee/bitlbee/commits/master +Version 3.4.2: +- irc: + * Self-messages (messages sent by yourself from other IM clients), given + support by the IM protocols and your IRC client. See this for details: + https://wiki.bitlbee.org/SelfMessages + * IRCv3.1 support and part of 3.2: cap-3.2, sasl-3.2, multi-prefix, + away-notify, extended-join, userhost-in-names + * Send numeric errors when failing to join a channel, to not confuse clients + * Channel autojoins should be more reliable now. +- jabber: + * Carbons (XEP-0280), for self-message support. It's not widely supported + by most public XMPP servers (easier if you host your own), but this will + probably change in the next few years. Thanks kormat for the original patch. + * Fix typing notifications between two bitlbee users or with gtalk users + * Remove facebook XMPP code, point people at bitlbee-facebook. + * Show groupchat kick/ban/leave reasons + * SASL ANONYMOUS (XEP-0175), for "guest" logins, see "help set anonymous" + * Hipchat: 'chat add hipchat "channel name"' now tries to guess the JID +- purple: + * Fix problems remembering SSL certificates as trusted + * Fix /join #channel, which joined a differently named channel + * Fix crash when doing "chat with" with skypeweb + * Fix html entities appearing in some protocols + * Fix setting away states in jabber, which failed silently + * Implement notify_message UI op, to be able to show some error messages. +- skype: + * Show all messages as groupchats since we can't tell which ones are private. + * This plugin is mostly-deprecated and mostly-broken but it's still useful + for p2p-based groupchats, which aren't delivered over newer protocols. + Everyone else should use the skypeweb purple plugin or msn instead. +- msn: + * Minor tweaks. Faster login, better error reporting, fixed add/remove. + Still MSNP21. Disregard that "Next release!" in the previous release. +- otr: + * Don't use NOTICE for user messages (revmischa) + * Fix crashes when using the jabber xmlconsole + * A few minor fixes: color multiline messages, filter incoming color codes. +- Packaging: + * Show ./configure args in bitlbee -V, config.h and Makefile.settings + * Allow setting the plugin dir in bitlbee.conf, for NixOS (anderspapitto) + * Improved cross compiler support (gamaral) +- Other important bugfixes: + * Fix potential crashes when leaving temporary channels + * Fix all sorts of crashing bugs when cancelling in-progress connections. + +Finished 19 Mar 2017 + Version 3.4.1: - msn: * Upgraded protocol to MSNP21, works again (dx) @@ -428,7 +475,7 @@ - Preserving case in Jabber resources of buddies, since these should officially be treated as case sensitive. - Fully stripping spaces from AIM screennames, this didn't happen completely - which severly breaks the IRC protocol. + which severely breaks the IRC protocol. - Removed all the yellow tape around daemon mode, it's pretty mature by now: testing.bitlbee.org serves all (~30) SSL users from one daemon mode process without any serious stability issues. @@ -628,7 +675,7 @@ - Most likely fixed the bug that caused BitlBee to use 100% CPU time in some situations. - Outgoing MSN typing notifications are now understood correctly by the - orignal MS Mac/Windows clients (again). + original MS Mac/Windows clients (again). - Added "account add $protocol" to the documentation, got rid of a lot of over-markup (i.e. overuse of bold-tags), reviewed some other parts. - Small changes to help.xsl to fix small flaws in the help.txt formatting. diff -Nru bitlbee-3.4.1/doc/HACKING bitlbee-3.4.2/doc/HACKING --- bitlbee-3.4.1/doc/HACKING 2015-06-16 22:30:40.000000000 +0000 +++ bitlbee-3.4.2/doc/HACKING 2016-03-19 23:37:33.000000000 +0000 @@ -13,7 +13,7 @@ just a few functions renamed for slightly better consistency, added some calls and arguments where that seemed useful, etc. -However, up to late in the 1.2 series, the IRC core was still spread accross +However, up to late in the 1.2 series, the IRC core was still spread across several files, mostly irc.c + irc_commands.c and pieces and bits in nogaim.c. If you're looking for a textbook example of layer violation, start there. diff -Nru bitlbee-3.4.1/doc/README bitlbee-3.4.2/doc/README --- bitlbee-3.4.1/doc/README 2015-06-16 22:30:40.000000000 +0000 +++ bitlbee-3.4.2/doc/README 2016-03-19 23:37:33.000000000 +0000 @@ -8,6 +8,9 @@ to set up the build system. If configure succeeds, run make to build BitlBee. make install will move all the files to the right places. +RUN MODES +========= + --- (Fork)Daemon mode These days ForkDaemon mode is the recommended way of running BitlBee. The @@ -88,20 +91,10 @@ PORTABILITY ISSUES ================== -Cygwin NOTE: You'll need a glib installation to run BitlBee. However, Cygwin -doesn't provide a glib package. You can download a binary tar.gz from: -. When you installed it, BitlBee should work -fine. You'll probably like bitlbeed or xinetd to get it running on the -network. - -On some non-Linux systems the program still suffers from some random bugs. -Please do report them, we might be able to fix them if they're not too -mysterious. - -Also, the configure script is known to not work very well with non-Bash -shells, so if you experience problems, make sure you use bash to run the -script. Same for the Makefile, it only works well with GNU make. (gmake on -most BSD systems) +The configure script is may not work very well with some non-bash shells (but +dash is supported), so if you experience problems, make sure you use bash to +run the script. Same for the Makefile, it only works well with GNU make. (gmake +on most BSD systems) If someone can tell us how to write Makefiles that work with both/all versions of make, we'd love to hear it, but it seems this just isn't @@ -152,28 +145,14 @@ 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 -now uses a better format (and real encryption (salted MD5 and RC4)) to store -the passwords. This means that people who somehow get their hands on your -configuration files can't easily extract your passwords from them anymore. +BitlBee currently uses salted MD5 and RC4 to store the passwords. This means +that people who somehow get their hands on your configuration files can't +easily extract your passwords from them anymore. However, once you log into the BitlBee server and send your password, an intruder with tcpdump can still read your passwords. This can't really be -avoided, of course. The new format is a lot more reliable (because it can't -be cracked with just very basic crypto analysis anymore), but you still have -to be careful. The main extra protection offered by the new format is that -the files can only be cracked with some help from the user (by sending the -password at login time). - -So if you run a public server, it's most important that you don't give root -access to people who like to play with tcpdump. Also, it's a good idea to -delete all *.nicks/*.accounts files as soon as BitlBee converted them to the -new format (which happens as soon as the user logs in, it can't be done -automatically because it needs the password for that account). You won't -need them anymore (unless you want to switch back to an older BitlBee -version) and they only make it easier for others to crack your passwords. - +avoided, of course. So if you run a public server, it's most important that you +don't give root access to people who like to play with tcpdump. LEGAL ===== @@ -181,11 +160,6 @@ BitlBee is distributed under the GPL (GNU General Public License). See the file COPYING for this license. -The MD5 algorithm code is licensed under the Aladdin license. This license -can be found in the files, to which this applies. The SHA1 algorithm code -is licensed under the Mozilla Public License, see http://www.mozilla.org/MPL/ -for details. - The Yahoo! library used by BitlBee is libyahoo2 , also licensed under the GPL. diff -Nru bitlbee-3.4.1/doc/user-guide/commands.xml bitlbee-3.4.2/doc/user-guide/commands.xml --- bitlbee-3.4.1/doc/user-guide/commands.xml 2015-06-16 22:30:40.000000000 +0000 +++ bitlbee-3.4.2/doc/user-guide/commands.xml 2016-03-19 23:37:33.000000000 +0000 @@ -181,7 +181,7 @@ - For more infomation about a setting, see help set <setting>. + For more information about a setting, see help set <setting>. @@ -239,7 +239,7 @@ - For more infomation about a setting, see help set <setting>. + For more information about a setting, see help set <setting>. @@ -788,6 +788,22 @@ + + true + + + + Jabber specific. "Message carbons" (XEP-0280) is a server feature to get copies of outgoing messages sent from other clients connected to the same account. It's not widely supported by most public XMPP servers (easier if you host your own), but this will probably change in the next few years. + + + This defaults to true, which will enable it if the server supports it, or fail silently if it's not. This setting only exists to allow disabling the feature if anyone considers it undesirable. + + + See also the self_messages setting. + + + + utf-8 you can get a list of all possible values by doing 'iconv -l' in a shell @@ -1092,7 +1108,7 @@ - With modes "chat" and "many", you can send direct messages by /msg'ing your contacts directly. Note, however, that incoming DMs are not fetched yet. + With modes "chat" and "many", you can send direct messages by /msg'ing your contacts directly. Incoming DMs are only fetched if the "stream" setting is on (default). @@ -1157,7 +1173,7 @@ - By default, BitlBee generates a nickname for every contact by taking its handle and chopping off everything after the @. In some cases, this gives very inconvenient nicknames. The Facebook XMPP server is a good example, as all Facebook XMPP handles are numeric. + By default, BitlBee generates a nickname for every contact by taking its handle and chopping off everything after the @. In some cases, this gives very inconvenient nicknames. Some servers use internal identifiers, which are often just numbers. @@ -1171,7 +1187,7 @@ - This enables OAuth authentication for an IM account; right now the Twitter (working for Twitter only) and Jabber (for Google Talk, Facebook and MSN Messenger) module support it. + This enables OAuth authentication for an IM account; right now the Twitter (working for Twitter only) and Jabber (for Google Talk only) module support it. @@ -1185,6 +1201,21 @@ + + false + + + + This enables SASL ANONYMOUS login for jabber accounts, as specified by XEP-0175. + + + + With this setting enabled, if the server allows this method, a password isn't required and the username part of the JID is ignored (you can use anonymous@jabber.example.com). Servers will usually assign you a random numeric username instead. + + + + + both both, root, user, none @@ -1308,6 +1339,26 @@ + + <local><auto> + + + + A list of file transfer proxies for jabber. This isn't the connection proxy. Sorry, look in bitlbee.conf for those. + + + + It's a semicolon-separated list of items that can be either JID,HOST,PORT or two special values, <local> (to try a direct connection first) and <auto> (to try to discover a proxy). For example, "<local>;proxy.somewhere.org,123.123.123.123,7777". + + + The address should point to a SOCKS5 bytestreams server, usually provided by jabber servers. This is only used for sending files. Note that the host address might not match what DNS tells you, and the port isn't always the same. + + + The correct way to get a socks proxy host/port is a mystery, and the file transfer might fail anyway. Maybe just try using dropbox instead. + + + + lifo lifo, fifo @@ -1368,6 +1419,37 @@ + + true + true, false, prefix, prefix_notice + + + + Change this setting to customize how (or whether) to show self-messages, which are messages sent by yourself from other locations (for example, mobile clients), for IM protocols that support it. + + + + When this is set to "true", it will send those messages in the "standard" way, which is a PRIVMSG with source and target fields swapped. + + + + Since this isn't very well supported by some clients (the messages might appear in the wrong window), you can set it to "prefix" to show them as a normal message prefixed with "-> ", or use "prefix_notice" which is the same thing but with a NOTICE instead. + + + + You can also set it to "false" to disable these messages completely. + + + + This setting only applies to private messages. Self messages in groupchats are always shown, since they haven't caused issues in any clients so far. + + + + More information: https://wiki.bitlbee.org/SelfMessages + + + + @@ -1401,21 +1483,22 @@ - online+,away + online+,special%,away Comma-separated list of statuses of users you want in the channel, and any modes they should have. The following statuses are currently recognised: online (i.e. available, not - away), away, and offline. + away), special, away, + and offline. If a status is followed by a valid channel mode character (@, % or +), it will be given to users with that status. - For example, online@,away+,offline will - show all users in the channel. Online people will + For example, online@,special%,away+,offline + will show all users in the channel. Online people will have +o, people who are online but away will have +v, and others will have no special modes. @@ -1875,7 +1958,7 @@ transfer <reject> - Rejects all incoming (not already transferring) file transfers. Since you probably have only one incoming transfer at a time, no id is neccessary. Or is it? + Rejects all incoming (not already transferring) file transfers. Since you probably have only one incoming transfer at a time, no id is necessary. Or is it? diff -Nru bitlbee-3.4.1/doc/user-guide/help.txt bitlbee-3.4.2/doc/user-guide/help.txt --- bitlbee-3.4.1/doc/user-guide/help.txt 1970-01-01 00:00:00.000000000 +0000 +++ bitlbee-3.4.2/doc/user-guide/help.txt 2016-03-19 23:39:01.000000000 +0000 @@ -0,0 +1,1456 @@ +? +These are the available help subjects: + + quickstart - A short introduction into BitlBee + commands - All available commands and settings + channels - About creating and customizing channels + away - About setting away states + groupchats - How to work with groupchats on BitlBee + nick_changes - Changing your nickname without losing any settings + smileys - A summary of some non-standard smileys you might find and fail to understand + +You can read more about them with help  + +Some more help can be found on http://wiki.bitlbee.org/. Bugs can be reported at http://bugs.bitlbee.org/. + +For other things than bug reports, you can join #BitlBee on OFTC (irc.oftc.net) (OFTC, *not* FreeNode!) and flame us right in the face. :-) +% +?index +These are the available help subjects: + + quickstart - A short introduction into BitlBee + commands - All available commands and settings + channels - About creating and customizing channels + away - About setting away states + groupchats - How to work with groupchats on BitlBee + nick_changes - Changing your nickname without losing any settings + smileys - A summary of some non-standard smileys you might find and fail to understand + +You can read more about them with help  +% +?quickstart +Welcome to BitlBee, your IRC gateway to ICQ, MSN, AOL, Jabber, Yahoo! and Twitter. + +The center of BitlBee is the control channel, &bitlbee. Two users will always be there, you (where "you" is the nickname you are using) and the system user, root. + +You need to register so that all your IM settings (passwords, contacts, etc) can be saved on the BitlBee server. It's important that you pick a good password so no one else can access your account. Register with this password using the register command: register  (without the brackets!). + +Be sure to remember your password. The next time you connect to the BitlBee server you will need to identify  so that you will be recognised and logged in to all the IM services automatically. + +When finished, type help quickstart2 to continue. +% +?quickstart2 +Step Two: Add and Connect To your IM Account(s). + +To add an account to the account list you will need to use the account add command: account add []. + +For instance, suppose you have a Jabber account at jabber.org with handle bitlbee@jabber.org with password QuickStart, you would: + + account add jabber bitlbee@jabber.org QuickStart + Account successfully added + +Other available IM protocols are msn, oscar, yahoo and twitter. OSCAR is the protocol used by ICQ and AOL. For more information about the account add command, see help account add. + +When you are finished adding your account(s) use the account on command to enable all your accounts, type help quickstart3 to continue. +% +?quickstart3 +Now you might want to add some contacts, to do this we will use the add command. It needs two arguments: a connection ID (which can be a number (try account list), protocol name or (part of) the screenname) and the user's handle. It is used in the following way: add  + + add 0 r2d2@example.com +* r2d2 has joined &bitlbee + +In this case r2d2 is online, since he/she joins the channel immediately. If the user is not online you will not see them join until they log on. + +Lets say you accidentally added r2d3@example.com rather than r2d2@example.com, or maybe you just want to remove a user from your list because you never talk to them. To remove a name you will want to use the remove command: remove r2d3 + +Finally, if you have multiple users with similar names you may use the rename command to make it easier to remember: rename r2d2_ r2d2_aim + +When finished, type help quickstart4 to continue. +% +?quickstart4 +Step Five: Chatting. + +First of all, a person must be on your contact list for you to chat with them (unless it's a group chat, help groupchats for more). If someone not on your contact list sends you a message, simply add them to the proper account with the add command. Once they are on your list and online, you can chat with them in &bitlbee: + + tux: hey, how's the weather down there? + you: a bit chilly! + +Note that, although all contacts are in the &bitlbee channel, only tux will actually receive this message. The &bitlbee channel shouldn't be confused with a real IRC channel. + +If you prefer chatting in a separate window, use the /msg or /query command, just like on real IRC. BitlBee will remember how you talk to someone and show his/her responses the same way. If you want to change the default behaviour (for people you haven't talked to yet), see help set private. + +You know the basics. If you want to know about some of the neat features BitlBee offers, please type help quickstart5. +% +?quickstart5 +So you want more than just chatting? Or maybe you're just looking for more features? + +With multiple channel support you can have contacts for specific protocols in their own channels, for instance, if you /join &msn you will join a channel that only contains your MSN contacts. + +Account tagging allows you to use the given account name rather than a number when referencing your account. If you wish to turn off your gtalk account, you may account gtalk off rather than account 3 off where "3" is the account number. + +You can type help set to learn more about the possible BitlBee user settings. Among these user settings you will find options for common issues, such as changing the charset, HTML stripping and automatic connecting (simply type set to see current user settings). + +For more subjects (like groupchats and away states), please type help index. + +If you're still looking for something, please visit us in #bitlbee on the OFTC network (you can connect via irc.bitlbee.org), or mail us your problem/suggestion. Good luck and enjoy the Bee! +% +?commands +These are all root commands. See help  for more details on each command. + + * account - IM-account list maintenance + * channel - Channel list maintenance + * chat - Chatroom list maintenance + * add - Add a buddy to your contact list + * info - Request user information + * remove - Remove a buddy from your contact list + * block - Block someone + * allow - Unblock someone + * otr - Off-the-Record encryption control + * set - Miscellaneous settings + * help - BitlBee help system + * save - Save your account data + * rename - Rename (renick) a buddy + * yes - Accept a request + * no - Deny a request + * qlist - List all the unanswered questions root asked + * register - Register yourself + * identify - Identify yourself with your password + * drop - Drop your account + * blist - List all the buddies in the current channel + * group - Contact group management + * transfer - Monitor, cancel, or reject file transfers + +Most commands can be shortened. For example instead of account list, try ac l. +% +?account +Syntax: account [] [] + +Available actions: add, del, list, on, off and set. See help account  for more information. +% +?account add +Syntax: account add [] + +Adds an account on the given server with the specified protocol, username and password to the account list. Supported protocols right now are: Jabber, MSN, OSCAR (AIM/ICQ), Yahoo and Twitter. For more information about adding an account, see help account add . + +You can omit the password and enter it separately using the IRC /OPER command. This lets you enter your password without your IRC client echoing it on screen or recording it in logs. +% +?account add jabber +Syntax: account add jabber [] + +The handle should be a full handle, including the domain name. You can specify a servername if necessary. Normally BitlBee doesn't need this though, since it's able to find out the server by doing DNS SRV lookups. + +In previous versions it was also possible to specify port numbers and/or SSL in the server tag. This is deprecated and should now be done using the account set command. This also applies to specifying a resource in the handle (like wilmer@bitlbee.org/work). +% +?account add msn +Syntax: account add msn [] + +For MSN connections there are no special arguments. +% +?account add oscar +Syntax: account add oscar [] + +OSCAR is the protocol used to connect to AIM and/or ICQ. The servers will automatically detect if you're using a numeric or non-numeric username so there's no need to tell which network you want to connect to. + +Example: + account add oscar 72696705 hobbelmeeuw + Account successfully added +% +?account add twitter +Syntax: account add twitter + +This module gives you simple access to Twitter and Twitter API compatible services. + +By default all your Twitter contacts will appear in a new channel called #twitter_yourusername. You can change this behaviour using the mode setting (see help set mode). + +To send tweets yourself, send them to the twitter_(yourusername) contact, or just write in the groupchat channel if you enabled that option. + +Since Twitter now requires OAuth authentication, you should not enter your Twitter password into BitlBee. Just type a bogus password. The first time you log in, BitlBee will start OAuth authentication. (See help set oauth.) + +To use a non-Twitter service, change the base_url setting. For identi.ca, you can simply use account add identica. +% +?account add identica +Syntax: account add identica + +Same protocol as twitter, but defaults to a base_url pointing at identi.ca. It also works with OAuth (so don't specify your password). +% +?account add yahoo +Syntax: account add yahoo [] + +For Yahoo! connections there are no special arguments. +% +?account del +Syntax: account del + +This command deletes an account from your account list. You should signoff the account before deleting it. + +The account ID can be a number/tag (see account list), the protocol name or (part of) the screenname, as long as it matches only one connection. +% +?account on +Syntax: account [] on + +This command will try to log into the specified account. If no account is specified, BitlBee will log into all the accounts that have the auto_connect flag set. + +The account ID can be a number/tag (see account list), the protocol name or (part of) the screenname, as long as it matches only one connection. +% +?account off +Syntax: account [] off + +This command disconnects the connection for the specified account. If no account is specified, BitlBee will deactivate all active accounts and cancel all pending reconnects. + +The account ID can be a number/tag (see account list), the protocol name or (part of) the screenname, as long as it matches only one connection. +% +?account list +Syntax: account list + +This command gives you a list of all the accounts known by BitlBee. +% +?account set +Syntax: account set +Syntax: account set +Syntax: account set +Syntax: account set -del + +This command can be used to change various settings for IM accounts. For all protocols, this command can be used to change the handle or the password BitlBee uses to log in and if it should be logged in automatically. Some protocols have additional settings. You can see the settings available for a connection by typing account set. + +For more information about a setting, see help set . + +The account ID can be a number/tag (see account list), the protocol name or (part of) the screenname, as long as it matches only one connection. +% +?channel +Syntax: channel [] [] + +Available actions: del, list, set. See help channel  for more information. + +There is no channel add command. To create a new channel, just use the IRC /join command. See also help channels and help groupchats. +% +?channel del +Syntax: channel del + +Remove a channel and forget all its settings. You can only remove channels you're not currently in, and can't remove the main control channel. (You can, however, leave it.) +% +?channel list +Syntax: channel list + +This command gives you a list of all the channels you configured. +% +?channel set +Syntax: channel [] set +Syntax: channel [] set +Syntax: channel [] set +Syntax: channel [] set -del + +This command can be used to change various settings for channels. Different channel types support different settings. You can see the settings available for a channel by typing channel set. + +For more information about a setting, see help set . + +The channel ID can be a number (see channel list), or (part of) its name, as long as it matches only one channel. If you want to change settings of the current channel, you can omit the channel ID. +% +?chat +Syntax: chat [] + +Available actions: add, with. See help chat  for more information. +% +?chat add +Syntax: chat add [] + +Add a chatroom to the list of chatrooms you're interested in. BitlBee needs this list to map room names to a proper IRC channel name. + +After adding a room to your list, you can simply use the IRC /join command to enter the room. Also, you can tell BitlBee to automatically join the room when you log in. (See chat set) + +Password-protected rooms work exactly like on IRC, by passing the password as an extra argument to /join. +% +?chat with +Syntax: chat with + +While most chat subcommands are about named chatrooms, this command can be used to open an unnamed groupchat with one or more persons. This command is what /join #nickname used to do in older BitlBee versions. +% +?add +Syntax: add [] +Syntax: add -tmp [] + +Adds the given buddy at the specified connection to your buddy list. The account ID can be a number (see account list), the protocol name or (part of) the screenname, as long as it matches only one connection. + +If you want, you can also tell BitlBee what nick to give the new contact. The -tmp option adds the buddy to the internal BitlBee structures only, not to the real contact list (like done by set handle_unknown add). This allows you to talk to people who are not in your contact list. This normally won't show you any presence notifications. + +If you use this command in a control channel containing people from only one group, the new contact will be added to that group automatically. + +Example: + add 3 gryp@jabber.org grijp +* grijp has joined &bitlbee +% +?info +Syntax: info +Syntax: info + +Requests IM-network-specific information about the specified user. The amount of information you'll get differs per protocol. For some protocols (ATM Yahoo! and MSN) it'll give you an URL which you can visit with a normal web browser to get the information. + +Example: + info 0 72696705 + User info - UIN: 72696705 Nick: Lintux First/Last name: Wilmer van der Gaast E-mail: lintux@lintux.cx +% +?remove +Syntax: remove + +Removes the specified nick from your buddy list. + +Example: + remove gryp +* gryp has quit [Leaving...] +% +?block +Syntax: block +Syntax: block +Syntax: block + +Puts the specified user on your ignore list. Either specify the user's nick when you have him/her in your contact list or a connection number and a user handle. + +When called with only a connection specification as an argument, the command displays the current block list for that connection. +% +?allow +Syntax: allow +Syntax: allow + +Reverse of block. Unignores the specified user or user handle on specified connection. + +When called with only a connection specification as an argument, the command displays the current allow list for that connection. +% +?otr +Syntax: otr [] + +Available subcommands: connect, disconnect, reconnect, smp, smpq, trust, info, keygen, and forget. See help otr  for more information. +% +?otr connect +Syntax: otr connect + +Attempts to establish an encrypted connection with the specified user by sending a magic string. +% +?otr disconnect +Syntax: otr disconnect +Syntax: otr disconnect * + +Resets the connection with the specified user/all users to cleartext. +% +?otr reconnect +Syntax: otr reconnect + +Breaks and re-establishes the encrypted connection with the specified user. Useful if something got desynced. + +Equivalent to otr disconnect followed by otr connect. +% +?otr smp +Syntax: otr smp + +Attempts to authenticate the given user's active fingerprint via the Socialist Millionaires' Protocol. + +If an SMP challenge has been received from the given user, responds with the specified secret/answer. Otherwise, sends a challenge for the given secret. + +Note that there are two flavors of SMP challenges: "shared-secret" and "question & answer". This command is used to respond to both of them, or to initiate a shared-secret style exchange. Use the otr smpq command to initiate a "Q&A" session. + +When responding to a "Q&A" challenge, the local trust value is not altered. Only the asking party sets trust in the case of success. Use otr smpq to pose your challenge. In a shared-secret exchange, both parties set their trust according to the outcome. +% +?otr smpq +Syntax: otr smpq + +Attempts to authenticate the given user's active fingerprint via the Socialist Millionaires' Protocol, Q&A style. + +Initiates an SMP session in "question & answer" style. The question is transmitted with the initial SMP packet and used to prompt the other party. You must be confident that only they know the answer. If the protocol succeeds (i.e. they answer correctly), the fingerprint will be trusted. Note that the answer must be entered exactly, case and punctuation count! + +Note that this style of SMP only affects the trust setting on your side. Expect your opponent to send you their own challenge. Alternatively, if you and the other party have a shared secret, use the otr smp command. +% +?otr trust +Syntax: otr trust + +Manually affirms trust in the specified fingerprint, given as five blocks of precisely eight (hexadecimal) digits each. +% +?otr info +Syntax: otr info +Syntax: otr info + +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 +Syntax: otr keygen + +Generates a new OTR private key for the given account. +% +?otr forget +Syntax: otr forget + +Forgets some part of our OTR userstate. Available things: fingerprint, context, and key. See help otr forget  for more information. +% +?otr forget fingerprint +Syntax: otr forget 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 +Syntax: otr forget context + +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 +Syntax: otr forget key + +Forgets an OTR private key matching the specified fingerprint. It is allowed to specify only a (unique) prefix of the fingerprint. +% +?set +Syntax: set +Syntax: set +Syntax: set +Syntax: set -del + +Without any arguments, this command lists all the set variables. You can also specify a single argument, a variable name, to get that variable's value. To change this value, specify the new value as the second argument. With -del you can reset a setting to its default value. + +To get more help information about a setting, try: + +Example: + help set private +% +?help +Syntax: help [subject] + +This command gives you the help information you're reading right now. If you don't give any arguments, it'll give a short help index. +% +?save +Syntax: save + +This command saves all your nicks and accounts immediately. Handy if you have the autosave functionality disabled, or if you don't trust the program's stability... ;-) +% +?rename +Syntax: rename +Syntax: rename -del + +Renick a user in your buddy list. Very useful, in fact just very important, if you got a lot of people with stupid account names (or hard ICQ numbers). + +rename -del can be used to erase your manually set nickname for a contact and reset it to what was automatically generated. + +Example: + rename itsme_ you +* itsme_ is now known as you +% +?yes +Syntax: yes [] + +Sometimes an IM-module might want to ask you a question. (Accept this user as your buddy or not?) To accept a question, use the yes command. + +By default, this answers the first unanswered question. You can also specify a different question as an argument. You can use the qlist command for a list of questions. +% +?no +Syntax: no [] + +Sometimes an IM-module might want to ask you a question. (Accept this user as your buddy or not?) To reject a question, use the no command. + +By default, this answers the first unanswered question. You can also specify a different question as an argument. You can use the qlist command for a list of questions. +% +?qlist +Syntax: qlist + +This gives you a list of all the unanswered questions from root. +% +?register +Syntax: register [] + +BitlBee can save your settings so you won't have to enter all your IM passwords every time you log in. If you want the Bee to save your settings, use the register command. + +Please do pick a secure password, don't just use your nick as your password. Please note that IRC is not an encrypted protocol, so the passwords still go over the network in plaintext. Evil people with evil sniffers will read it all. (So don't use your root password.. ;-) + +To identify yourself in later sessions, you can use the identify command. To change your password later, you can use the set password command. + +You can omit the password and enter it separately using the IRC /OPER command. This lets you enter your password without your IRC client echoing it on screen or recording it in logs. +% +?identify +Syntax: identify [-noload|-force] [] + +BitlBee saves all your settings (contacts, accounts, passwords) on-server. To prevent other users from just logging in as you and getting this information, you'll have to identify yourself with your password. You can register this password using the register command. + +Once you're registered, you can change your password using set password . + +The -noload and -force flags can be used to identify when you're logged into some IM accounts already. -force will let you identify yourself and load all saved accounts (and keep the accounts you're logged into already). + +-noload will log you in but not load any accounts and settings saved under your current nickname. These will be overwritten once you save your settings (i.e. when you disconnect). + +You can omit the password and enter it separately using the IRC /OPER command. This lets you enter your password without your IRC client echoing it on screen or recording it in logs. +% +?drop +Syntax: drop + +Drop your BitlBee registration. Your account files will be removed and your password will be forgotten. For obvious security reasons, you have to specify your NickServ password to make this command work. +% +?blist +Syntax: blist [all|online|offline|away] [] + +You can get a more readable buddy list using the blist command. If you want a complete list (including the offline users) you can use the all argument. + +A perl-compatible regular expression can be supplied as pattern to filter the results (case-insensitive). +% +?group +Syntax: group [ list | info ] + +The group list command shows a list of all groups defined so far. + +The group info command shows a list of all members of a the group . + +If you want to move contacts between groups, you can use the IRC /invite command. Also, if you use the add command in a control channel configured to show just one group, the new contact will automatically be added to that group. +% +?transfer +Syntax: transfer [ id | ] + +Without parameters the currently pending file transfers and their status will be listed. Available actions are cancel and reject. See help transfer  for more information. + + transfer +% +?transfer cancel +Syntax: transfer id + +Cancels the file transfer with the given id + +Example: + transfer cancel 1 + Canceling file transfer for test +% +?transfer reject +Syntax: transfer + +Rejects all incoming (not already transferring) file transfers. Since you probably have only one incoming transfer at a time, no id is necessary. Or is it? + +Example: + transfer reject +% +?set account +Type: string +Scope: channel + +For control channels with fill_by set to account: Set this setting to the account id (numeric, or part of the username) of the account containing the contacts you want to see in this channel. +% +?set allow_takeover +Type: boolean +Scope: global +Default: true + +When you're already connected to a BitlBee server and you connect (and identify) again, BitlBee will offer to migrate your existing session to the new connection. If for whatever reason you don't want this, you can disable this setting. +% +?set auto_connect +Type: boolean +Scope: account,global +Default: true + +With this option enabled, when you identify BitlBee will automatically connect to your accounts, with this disabled it will not do this. + +This setting can also be changed for specific accounts using the account set command. (However, these values will be ignored if the global auto_connect setting is disabled!) +% +?set auto_join +Type: boolean +Scope: channel +Default: false + +With this option enabled, BitlBee will automatically join this channel when you log in. +% +?set auto_reconnect +Type: boolean +Scope: account,global +Default: true + +If an IM-connections breaks, you're supposed to bring it back up yourself. Having BitlBee do this automatically might not always be a good idea, for several reasons. If you want the connections to be restored automatically, you can enable this setting. + +See also the auto_reconnect_delay setting. + +This setting can also be changed for specific accounts using the account set command. (However, these values will be ignored if the global auto_reconnect setting is disabled!) +% +?set auto_reconnect_delay +Type: string +Scope: global +Default: 5*3<900 + +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. + +See also the auto_reconnect setting. +% +?set auto_reply_timeout +Type: integer +Scope: account +Default: 10800 + +For Twitter accounts: If you respond to Tweets IRC-style (like "nickname: reply"), this will automatically be converted to the usual Twitter format ("@screenname reply"). + +By default, BitlBee will then also add a reference to that person's most recent Tweet, unless that message is older than the value of this setting in seconds. + +If you want to disable this feature, just set this to 0. Alternatively, if you want to write a message once that is not a reply, use the Twitter reply syntax (@screenname). +% +?set away +Type: string +Scope: account,global + +To mark yourself as away, it is recommended to just use /away, like on normal IRC networks. If you want to mark yourself as away on only one IM network, you can use this per-account setting. + +You can set it to any value and BitlBee will try to map it to the most appropriate away state for every open IM connection, or set it as a free-form away message where possible. + +Any per-account away setting will override globally set away states. To un-set the setting, use set -del away. +% +?set away_devoice +Type: boolean +Scope: global +Default: true + +With this option enabled, the root user devoices people when they go away (just away, not offline) and gives the voice back when they come back. You might dislike the voice-floods you'll get if your contact list is huge, so this option can be disabled. + +Replaced with the show_users setting. See help show_users. +% +?set away_reply_timeout +Type: integer +Scope: global +Default: 3600 + +Most IRC servers send a user's away message every time s/he gets a private message, to inform the sender that they may not get a response immediately. With this setting set to 0, BitlBee will also behave like this. + +Since not all IRC clients do an excellent job at suppressing these messages, this setting lets BitlBee do it instead. BitlBee will wait this many seconds (or until the away state/message changes) before re-informing you that the person's away. +% +?set base_url +Type: string +Scope: account +Default: http://api.twitter.com/1 + +There are more services that understand the Twitter API than just Twitter.com. BitlBee can connect to all Twitter API implementations. + +For example, set this setting to http://identi.ca/api to use Identi.ca. + +Keep two things in mind: When not using Twitter, you must also disable the oauth setting as it currently only works with Twitter. If you're still having issues, make sure there is no slash at the end of the URL you enter here. +% +?set carbons +Type: boolean +Scope: account +Default: true + +Jabber specific. "Message carbons" (XEP-0280) is a server feature to get copies of outgoing messages sent from other clients connected to the same account. It's not widely supported by most public XMPP servers (easier if you host your own), but this will probably change in the next few years. + +This defaults to true, which will enable it if the server supports it, or fail silently if it's not. This setting only exists to allow disabling the feature if anyone considers it undesirable. + +See also the self_messages setting. +% +?set charset +Type: string +Scope: global +Default: utf-8 +Possible Values: you can get a list of all possible values by doing 'iconv -l' in a shell + +This setting tells BitlBee what your IRC client sends and expects. It should be equal to the charset setting of your IRC client if you want to be able to send and receive non-ASCII text properly. + +Most systems use UTF-8 these days. On older systems, an iso8859 charset may work better. For example, iso8859-1 is the best choice for most Western countries. You can try to find what works best for you on http://www.unicodecharacter.com/charsets/iso8859.html +% +?set color_encrypted +Type: boolean +Scope: global +Default: true + +If set to true, BitlBee will color incoming encrypted messages according to their fingerprint trust level: untrusted=red, trusted=green. +% +?set chat_type +Type: string +Scope: channel +Default: groupchat +Possible Values: groupchat, room + +There are two kinds of chat channels: simple groupchats (basically normal IM chats with more than two participants) and names chatrooms, more similar to IRC channels. + +BitlBee supports both types. With this setting set to groupchat (the default), you can just invite people into the room and start talking. + +For setting up named chatrooms, it's currently easier to just use the chat add command. +% +?set commands +Type: boolean +Scope: account +Default: true +Possible Values: true, false, strict + +With this setting enabled, you can use some commands in your Twitter channel/query. The commands are simple and not documented in too much detail: + + undo #[] - Delete your last Tweet (or one with the given ID) + rt  - Retweet someone's last Tweet (or one with the given ID) + reply  - Reply to a Tweet (with a reply-to reference) + rawreply  - Reply to a Tweet (with no reply-to reference) + report  - Report the given user (or the user who posted the tweet with the given ID) for sending spam. This will also block them. + follow  - Start following a person + unfollow  - Stop following a person + favourite  - Favourite the given user's most recent tweet, or the given tweet ID. + post  - Post a tweet + url  - Show URL for a tweet to open it in a browser (and see context) + +Anything that doesn't look like a command will be treated as a tweet. Watch out for typos, or to avoid this behaviour, you can set this setting to strict, which causes the post command to become mandatory for posting a tweet. +% +?set debug +Type: boolean +Scope: global +Default: false + +Some debugging messages can be logged if you wish. They're probably not really useful for you, unless you're doing some development on BitlBee. + +This feature is not currently used for anything so don't expect this to generate any output. +% +?set default_target +Type: string +Scope: global +Default: root +Possible Values: root, last + +With this value set to root, lines written in a control channel without any nickname in front of them will be interpreted as commands. If you want BitlBee to send those lines to the last person you addressed in that control channel, set this to last. +% +?set display_name +Type: string +Scope: account + +Currently only available for MSN connections, and for jabber groupchats. + +For MSN: This setting allows you to read and change your "friendly name" for this connection. Since this is a server-side setting, it can't be changed when the account is off-line. + +For jabber groupchats: this sets the default value of 'nick' for newly created groupchats. There is no way to set an account-wide nick like MSN. +% +?set display_namechanges +Type: boolean +Scope: global +Default: false + +With this option enabled, root will inform you when someone in your buddy list changes his/her "friendly name". +% +?set display_timestamps +Type: boolean +Scope: global +Default: true + +When incoming messages are old (i.e. offline messages and channel backlogs), BitlBee will prepend them with a timestamp. If you find them ugly or useless, you can use this setting to hide them. +% +?set fill_by +Type: string +Scope: channel +Default: all +Possible Values: all, group, account, protocol + +For control channels only: This setting determines which contacts the channel gets populated with. + +By default, control channels will contain all your contacts. You instead select contacts by buddy group, IM account or IM protocol. + +Change this setting and the corresponding account/group/protocol setting to set up this selection. + +With a ! prefix an inverted channel can be created, for example with this setting set to !group you can create a channel with all users not in that group. + +Note that, when creating a new channel, BitlBee will try to preconfigure the channel for you, based on the channel name. See help channels. +% +?set group +Type: string +Scope: channel + +For control channels with fill_by set to group: Set this setting to the name of the group containing the contacts you want to see in this channel. +% +?set handle_unknown +Type: string +Scope: global +Default: add_channel +Possible Values: root, add, add_private, add_channel, ignore + +By default, messages from people who aren't in your contact list are shown in a control channel instead of as a private message. + +If you prefer to ignore messages from people you don't know, you can set this one to "ignore". "add_private" and "add_channel" are like add, but you can use them to make messages from unknown buddies appear in the channel instead of a query window. + +Although these users will appear in your control channel, they aren't added to your real contact list. When you restart BitlBee, these auto-added users will be gone. If you want to keep someone in your list, you have to fixate the add using the add command. +% +?set ignore_auth_requests +Type: boolean +Scope: account +Default: false + +Only supported by OSCAR so far, you can use this setting to ignore ICQ authorization requests, which are hardly used for legitimate (i.e. non-spam) reasons anymore. +% +?set lcnicks +Type: boolean +Scope: global +Default: true + +Hereby you can change whether you want all lower case nick names or leave the case as it intended by your peer. +% +?set local_display_name +Type: boolean +Scope: account +Default: false + +Mostly meant to work around a bug in MSN servers (forgetting the display name set by the user), this setting tells BitlBee to store your display name locally and set this name on the MSN servers when connecting. +% +?set mail_notifications +Type: boolean +Scope: account +Default: false + +Some protocols (MSN, Yahoo!, GTalk) can notify via IM about new e-mail. Since most people use their Hotmail/Yahoo! addresses as a spam-box, this is disabled default. If you want these notifications, you can enable this setting. +% +?set mail_notifications_handle +Type: string +Scope: account +Default: empty + +This setting is available for protocols with e-mail notification functionality. If set to empty all e-mail notifications will go to control channel, if set to some string - this will be the name of a contact who will PRIVMSG you on every new notification. +% +?set message_length +Type: integer +Scope: account +Default: 140 + +Since Twitter rejects messages longer than 140 characters, BitlBee can count message length and emit a warning instead of waiting for Twitter to reject it. + +You can change this limit here but this won't disable length checks on Twitter's side. You can also set it to 0 to disable the check in case you believe BitlBee doesn't count the characters correctly. +% +?set stream +Type: boolean +Scope: account +Default: true + +For Twitter accounts, this setting enables use of the Streaming API. This automatically gives you incoming DMs as well. + +For other Twitter-like services, this setting is not supported. +% +?set target_url_length +Type: integer +Scope: account +Default: 20 + +Twitter replaces every URL with fixed-length t.co URLs. BitlBee is able to take t.co urls into account when calculating message_length replacing the actual URL length with target_url_length. Setting target_url_length to 0 disables this feature. + +This setting is disabled for identica accounts by default and will not affect anything other than message safety checks (i.e. Twitter will still replace your URLs with t.co links, even if that makes them longer). +% +?set mode +Type: string +Scope: account +Default: chat +Possible Values: one, many, chat + +By default, BitlBee will create a separate channel (called #twitter_yourusername) for all your Twitter contacts/messages. + +If you don't want an extra channel, you can set this setting to "one" (everything will come from one nick, twitter_yourusername), or to "many" (individual nicks for everyone). + +With modes "chat" and "many", you can send direct messages by /msg'ing your contacts directly. Incoming DMs are only fetched if the "stream" setting is on (default). + +With modes "many" and "one", you can post tweets by /msg'ing the twitter_yourusername contact. In mode "chat", messages posted in the Twitter channel will also be posted as tweets. +% +?set mobile_is_away +Type: boolean +Scope: global +Default: false + +Most IM networks have a mobile version of their client. People who use these may not be paying that much attention to messages coming in. By enabling this setting, people using mobile clients will always be shown as away. +% +?set nick +Type: string +Scope: chat + +You can use this option to set your nickname in a chatroom. You won't see this nickname yourself, but other people in the room will. By default, BitlBee will use your username as the chatroom nickname. +% +?set nick_format +Type: string +Scope: account,global +Default: %-@nick + +By default, BitlBee tries to derive sensible nicknames for all your contacts from their IM handles. In some cases, IM modules (ICQ for example) will provide a nickname suggestion, which will then be used instead. This setting lets you change this behaviour. + +Whenever this setting is set for an account, it will be used for all its contacts. If it's not set, the global value will be used. + +It's easier to describe this setting using a few examples: + +FB-%full_name will make all nicknames start with "FB-", followed by the person's full name. For example you can set this format for your Facebook account so all Facebook contacts are clearly marked. + +[%group]%-@nick will make all nicknames start with the group the contact is in between square brackets, followed by the nickname suggestions from the IM module if available, or otherwise the handle. Because of the "-@" part, everything from the first @ will be stripped. + +See help nick_format for more information. +% +?set nick_source +Type: string +Scope: account +Default: handle +Possible Values: handle, full_name, first_name + +By default, BitlBee generates a nickname for every contact by taking its handle and chopping off everything after the @. In some cases, this gives very inconvenient nicknames. Some servers use internal identifiers, which are often just numbers. + +With this setting set to full_name, the person's full name is used to generate a nickname. Or if you don't like long nicknames, set this setting to first_name instead and only the first word will be used. Note that the full name can be full of non-ASCII characters which will be stripped off. +% +?set oauth +Type: boolean +Scope: account +Default: true + +This enables OAuth authentication for an IM account; right now the Twitter (working for Twitter only) and Jabber (for Google Talk only) module support it. + +With OAuth enabled, you shouldn't tell BitlBee your account password. Just add your account with a bogus password and type account on. BitlBee will then give you a URL to authenticate with the service. If this succeeds, you will get a PIN code which you can give back to BitlBee to finish the process. + +The resulting access token will be saved permanently, so you have to do this only once. If for any reason you want to/have to reauthenticate, you can use account set to reset the account password to something random. +% +?set anonymous +Type: boolean +Scope: account +Default: false + +This enables SASL ANONYMOUS login for jabber accounts, as specified by XEP-0175. + +With this setting enabled, if the server allows this method, a password isn't required and the username part of the JID is ignored (you can use anonymous@jabber.example.com). Servers will usually assign you a random numeric username instead. +% +?set ops +Type: string +Scope: global +Default: both +Possible Values: both, root, user, none + +Some people prefer themself and root to have operator status in &bitlbee, other people don't. You can change these states using this setting. + +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. +% +?set otr_policy +Type: string +Scope: global +Default: opportunistic +Possible Values: 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. +% +?set password +Type: string +Scope: account,global + +Use this global setting to change your "NickServ" password. + +This setting is also available for all IM accounts to change the password BitlBee uses to connect to the service. + +Note that BitlBee will always say this setting is empty. This doesn't mean there is no password, it just means that, for security reasons, BitlBee stores passwords somewhere else so they can't just be retrieved in plain text. +% +?set paste_buffer +Type: boolean +Scope: global +Default: false + +By default, when you send a message to someone, BitlBee forwards this message to the user immediately. When you paste a large number of lines, the lines will be sent in separate messages, which might not be very nice to read. If you enable this setting, BitlBee will buffer your messages and wait for more data. + +Using the paste_buffer_delay setting you can specify the number of seconds BitlBee should wait for more data before the complete message is sent. + +Please note that if you remove a buddy from your list (or if the connection to that user drops) and there's still data in the buffer, this data will be lost. BitlBee will not try to send the message to the user in those cases. +% +?set paste_buffer_delay +Type: integer +Scope: global +Default: 200 + +Tell BitlBee after how many (mili)seconds a buffered message should be sent. Values greater than 5 will be interpreted as miliseconds, 5 and lower as seconds. + +See also the paste_buffer setting. +% +?set port +Type: integer +Scope: account + +Currently only available for Jabber connections. Specifies the port number to connect to. Usually this should be set to 5222, or 5223 for SSL-connections. +% +?set priority +Type: integer +Scope: account +Default: 0 + +Can be set for Jabber connections. When connecting to one account from multiple places, this priority value will help the server to determine where to deliver incoming messages (that aren't addressed to a specific resource already). + +According to RFC 3921 servers will always deliver messages to the server with the highest priority value. Mmessages will not be delivered to resources with a negative priority setting (and should be saved as an off-line message if all available resources have a negative priority value). +% +?set private +Type: boolean +Scope: global +Default: true + +If value is true, messages from users will appear in separate query windows. If false, messages from users will appear in a control channel. + +This setting is remembered (during one session) per-user, this setting only changes the default state. This option takes effect as soon as you reconnect. +% +?set protocol +Type: string +Scope: channel + +For control channels with fill_by set to protocol: Set this setting to the name of the IM protocol of all contacts you want to see in this channel. +% +?set proxy +Type: string +Scope: account +Default: + +A list of file transfer proxies for jabber. This isn't the connection proxy. Sorry, look in bitlbee.conf for those. + +It's a semicolon-separated list of items that can be either JID,HOST,PORT or two special values,  (to try a direct connection first) and  (to try to discover a proxy). For example, ";proxy.somewhere.org,123.123.123.123,7777". + +The address should point to a SOCKS5 bytestreams server, usually provided by jabber servers. This is only used for sending files. Note that the host address might not match what DNS tells you, and the port isn't always the same. + +The correct way to get a socks proxy host/port is a mystery, and the file transfer might fail anyway. Maybe just try using dropbox instead. +% +?set query_order +Type: string +Scope: global +Default: lifo +Possible Values: lifo, fifo + +This changes the order in which the questions from root (usually authorization requests from buddies) should be answered. When set to lifo, BitlBee immediately displays all new questions and they should be answered in reverse order. When this is set to fifo, BitlBee displays the first question which comes in and caches all the others until you answer the first one. + +Although the fifo setting might sound more logical (and used to be the default behaviour in older BitlBee versions), it turned out not to be very convenient for many users when they missed the first question (and never received the next ones). +% +?set resource +Type: string +Scope: account +Default: BitlBee + +Can be set for Jabber connections. You can use this to connect to your Jabber account from multiple clients at once, with every client using a different resource string. +% +?set resource_select +Type: string +Scope: account +Default: activity +Possible Values: priority, activity + +Because the IRC interface makes it pretty hard to specify the resource to talk to (when a buddy is online through different resources), this setting was added. + +Normally it's set to priority which means messages will always be delivered to the buddy's resource with the highest priority. If the setting is set to activity, messages will be delivered to the resource that was last used to send you a message (or the resource that most recently connected). +% +?set root_nick +Type: string +Scope: global +Default: root + +Normally the "bot" that takes all your BitlBee commands is called "root". If you don't like this name, you can rename it to anything else using the rename command, or by changing this setting. +% +?set save_on_quit +Type: boolean +Scope: global +Default: true + +If enabled causes BitlBee to save all current settings and account details when user disconnects. This is enabled by default, and these days there's not really a reason to have it disabled anymore. +% +?set self_messages +Type: string +Scope: global +Default: true +Possible Values: true, false, prefix, prefix_notice + +Change this setting to customize how (or whether) to show self-messages, which are messages sent by yourself from other locations (for example, mobile clients), for IM protocols that support it. + +When this is set to "true", it will send those messages in the "standard" way, which is a PRIVMSG with source and target fields swapped. + +Since this isn't very well supported by some clients (the messages might appear in the wrong window), you can set it to "prefix" to show them as a normal message prefixed with "-> ", or use "prefix_notice" which is the same thing but with a NOTICE instead. + +You can also set it to "false" to disable these messages completely. + +This setting only applies to private messages. Self messages in groupchats are always shown, since they haven't caused issues in any clients so far. + +More information: https://wiki.bitlbee.org/SelfMessages +% +?set server +Type: string +Scope: account + +Can be set for Jabber- and OSCAR-connections. For Jabber, you might have to set this if the servername isn't equal to the part after the @ in the Jabber handle. For OSCAR this shouldn't be necessary anymore in recent BitlBee versions. +% +?set show_ids +Type: boolean +Scope: account +Default: true + +Enable this setting on a Twitter account to have BitlBee include a two-digit "id" in front of every message. This id can then be used for replies and retweets. +% +?set show_offline +Type: boolean +Scope: global +Default: false + +If enabled causes BitlBee to also show offline users in Channel. Online-users will get op, away-users voice and offline users none of both. This option takes effect as soon as you reconnect. + +Replaced with the show_users setting. See help show_users. +% +?set show_users +Type: string +Scope: channel +Default: online+,special%,away + +Comma-separated list of statuses of users you want in the channel, and any modes they should have. The following statuses are currently recognised: online (i.e. available, not away), special, away, and offline. + +If a status is followed by a valid channel mode character (@, % or +), it will be given to users with that status. For example, online@,special%,away+,offlinewill show all users in the channel. Online people will have +o, people who are online but away will have +v, and others will have no special modes. +% +?set simulate_netsplit +Type: boolean +Scope: global +Default: true + +Some IRC clients parse quit messages sent by the IRC server to see if someone really left or just disappeared because of a netsplit. By default, BitlBee tries to simulate netsplit-like quit messages to keep the control channels window clean. If you don't like this (or if your IRC client doesn't support this) you can disable this setting. +% +?set ssl +Type: boolean +Scope: account +Default: false + +Currently only available for Jabber connections. Set this to true if you want to connect to the server on an SSL-enabled port (usually 5223). + +Please note that this method of establishing a secure connection to the server has long been deprecated. You are encouraged to look at the tls setting instead. +% +?set status +Type: string +Scope: account,global + +Most IM protocols support status messages, similar to away messages. They can be used to indicate things like your location or activity, without showing up as away/busy. + +This setting can be used to set such a message. It will be available as a per-account setting for protocols that support it, and also as a global setting (which will then automatically be used for all protocols that support it). + +Away states set using /away or the away setting will override this setting. To clear the setting, use set -del status. +% +?set strip_html +Type: boolean +Scope: global +Default: true + +Determines what BitlBee should do with HTML in messages. Normally this is turned on and HTML will be stripped from messages, if BitlBee thinks there is HTML. + +If BitlBee fails to detect this sometimes (most likely in AIM messages over an ICQ connection), you can set this setting to always, but this might sometimes accidentally strip non-HTML things too. +% +?set strip_newlines +Type: boolean +Scope: account +Default: false + +Turn on this flag to prevent tweets from spanning over multiple lines. +% +?set show_old_mentions +Type: integer +Scope: account +Default: 20 + +This setting specifies the number of old mentions to fetch on connection. Must be less or equal to 200. Setting it to 0 disables this feature. +% +?set switchboard_keepalives +Type: boolean +Scope: account +Default: false + +Turn on this flag if you have difficulties talking to offline/invisible contacts. + +With this setting enabled, BitlBee will send keepalives to MSN switchboards with offline/invisible contacts every twenty seconds. This should keep the server and client on the other side from shutting it down. + +This is useful because BitlBee doesn't support MSN offline messages yet and the MSN servers won't let the user reopen switchboards to offline users. Once offline messaging is supported, this flag might be removed. +% +?set tag +Type: string +Scope: account + +For every account you have, you can set a tag you can use to uniquely identify that account. This tag can be used instead of the account number (or protocol name, or part of the screenname) when using commands like account, add, etc. You can't have two accounts with one and the same account tag. + +By default, it will be set to the name of the IM protocol. Once you add a second account on an IM network, a numeric suffix will be added, starting with 2. +% +?set timezone +Type: string +Scope: global +Default: local +Possible Values: local, utc, gmt, timezone-spec + +If message timestamps are available for offline messages or chatroom backlogs, BitlBee will display them as part of the message. By default it will use the local timezone. If you're not in the same timezone as the BitlBee server, you can adjust the timestamps using this setting. + +Values local/utc/gmt should be self-explanatory. timezone-spec is a time offset in hours:minutes, for example: -8 for Pacific Standard Time, +2 for Central European Summer Time, +5:30 for Indian Standard Time. +% +?set tls +Type: boolean +Scope: account +Default: true + +By default (with this setting enabled), BitlBee will require Jabber servers to offer encryption via StartTLS and refuse to connect if they don't. + +If you set this to "try", BitlBee will use StartTLS only if it's offered. With the setting disabled, StartTLS support will be ignored and avoided entirely. +% +?set tls_verify +Type: boolean +Scope: account +Default: true + +Currently only available for Jabber connections in combination with the tls setting. Set this to true if you want BitlBee to strictly verify the server's certificate against a list of trusted certificate authorities. + +The hostname used in the certificate verification is the value of the server setting if the latter is nonempty and the domain of the username else. If you get a hostname related error when connecting to Google Talk with a username from the gmail.com or googlemail.com domain, please try to empty the server setting. + +Please note that no certificate verification is performed when the ssl setting is used, or when the CAfile setting in bitlbee.conf is not set. +% +?set to_char +Type: string +Scope: global +Default: ": " + +It's customary that messages meant for one specific person on an IRC channel are prepended by his/her alias followed by a colon ':'. BitlBee does this by default. If you prefer a different character, you can set it using set to_char. + +Please note that this setting is only used for incoming messages. For outgoing messages you can use ':' (colon) or ',' to separate the destination nick from the message, and this is not configurable. +% +?set translate_to_nicks +Type: boolean +Scope: channel +Default: true + +IRC's nickname namespace is quite limited compared to most IM protocols. Not any non-ASCII characters are allowed, in fact nicknames have to be mostly alpha-numeric. Also, BitlBee has to add underscores sometimes to avoid nickname collisions. + +While normally the BitlBee user is the only one seeing these names, they may be exposed to other chatroom participants for example when addressing someone in the channel (with or without tab completion). By default BitlBee will translate these stripped nicknames back to the original nick. If you don't want this, disable this setting. +% +?set type +Type: string +Scope: channel +Default: control +Possible Values: control, chat + +BitlBee supports two kinds of channels: control channels (usually with a name starting with a &) and chatroom channels (name usually starts with a #). + +See help channels for a full description of channel types in BitlBee. +% +?set typing_notice +Type: boolean +Scope: global +Default: false + +Sends you a /notice when a user starts typing a message (if supported by the IM protocol and the user's client). To use this, you most likely want to use a script in your IRC client to show this information in a more sensible way. +% +?set user_agent +Type: string +Scope: account +Default: BitlBee + +Some Jabber servers are configured to only allow a few (or even just one) kinds of XMPP clients to connect to them. + +You can change this setting to make BitlBee present itself as a different client, so that you can still connect to these servers. +% +?set utf8_nicks +Type: boolean +Scope: global +Default: false + +Officially, IRC nicknames are restricted to ASCII. Recently some clients and servers started supporting Unicode nicknames though. To enable UTF-8 nickname support (contacts only) in BitlBee, enable this setting. + +To avoid confusing old clients, this setting is disabled by default. Be careful when you try it, and be prepared to be locked out of your BitlBee in case your client interacts poorly with UTF-8 nicknames. +% +?set web_aware +Type: string +Scope: account +Default: false + +ICQ allows people to see if you're on-line via a CGI-script. (http://status.icq.com/online.gif?icq=UIN) This can be nice to put on your website, but it seems that spammers also use it to see if you're online without having to add you to their contact list. So to prevent ICQ spamming, recent versions of BitlBee disable this feature by default. + +Unless you really intend to use this feature somewhere (on forums or maybe a website), it's probably better to keep this setting disabled. +% +?set xmlconsole +Type: boolean +Scope: account +Default: false + +The Jabber module allows you to add a buddy xmlconsole to your contact list, which will then show you the raw XMPP stream between you and the server. You can also send XMPP packets to this buddy, which will then be sent to the server. + +If you want to enable this XML console permanently (and at login time already), you can set this setting. +% +?misc +% +?smileys +All MSN smileys (except one) are case insensitive and work without the nose too. + + (Y) - Thumbs up + (N) - Thumbs down + (B) - Beer mug + (D) - Martini glass + (X) - Girl + (Z) - Boy + (6) - Devil smiley + :-[ - Vampire bat + (}) - Right hug + ({) - Left hug + (M) - MSN Messenger or Windows Messenger icon (think a BitlBee logo here ;) + :-S - Crooked smiley (Confused smiley) + :-$ - Embarrassed smiley + (H) - Smiley with sunglasses + :-@ - Angry smiley + (A) - Angel smiley + (L) - Red heart (Love) + (U) - Broken heart + (K) - Red lips (Kiss) + (G) - Gift with bow + (F) - Red rose + (W) - Wilted rose + (P) - Camera + (~) - Film strip + (T) - Telephone receiver + (@) - Cat face + (&) - Dog's head + (C) - Coffee cup + (I) - Light bulb + (S) - Half-moon (Case sensitive!) + (*) - Star + (8) - Musical eighth note + (E) - Envelope + (^) - Birthday cake + (O) - Clock +% +?groupchats +BitlBee now supports groupchats on all IM networks. This text will try to explain you how they work. + +As soon as someone invites you into a groupchat, you will be force-joined or invited (depending on the protocol) into a new virtual channel with all the people in there. You can leave the channel at any time, just like you would close the window in regular IM clients. Please note that root-commands don't work in groupchat channels, they only work in control channels (or to root directly). + +Of course you can also create your own groupchats. Type help groupchats2 to see how. +% +?groupchats2 +To open a groupchat, use the chat with command. For example, to start a groupchat with the person lisa_msn in it, just type chat with lisa_msn. BitlBee will create a new virtual channel with root, you and lisa_msn in it. + +Then, just use the ordinary IRC /invite command to invite more people. Please do keep in mind that all the people have to be on the same network and contact list! You can't invite Yahoo! buddies into an MSN groupchat. + +Some protocols (like Jabber) also support named groupchats. BitlBee now supports these too. You can use the chat add command to join them. See help chat add for more information. +% +?away +To mark yourself as away, you can just use the /away command in your IRC client. BitlBee supports most away-states supported by the protocols. + +Away states have different names across different protocols. BitlBee will try to pick the best available option for every connection: + + - Away + - NA + - Busy, DND + - BRB + - Phone + - Lunch, Food + - Invisible, Hidden + +So /away Food will set your state to "Out to lunch" on your MSN connection, and for most other connections the default, "Away" will be chosen. + +You can also add more information to your away message. Setting it to "Busy - Fixing BitlBee bugs" will set your IM-away-states to Busy, but your away message will be more descriptive for people on IRC. Most IM-protocols can also show this additional information to your buddies. + +If you want to set an away state for only one of your connections, you can use the per-account away setting. See help set away. +% +?nick_changes +BitlBee now allows you to change your nickname. So far this was not possible because it made managing saved accounts more complicated. + +The restriction no longer exists now though. When you change your nick (just using the /nick command), your logged-in status will be reset, which means any changes made to your settings/accounts will not be saved. + +To restore your logged-in status, you need to either use the register command to create an account under the new nickname, or use identify -noload to re-identify yourself under the new nickname. The -noload flag tells the command to verify your password and log you in, but not load any new settings. See help identify for more information. +% +?channels +You can have as many channels in BitlBee as you want. You maintain your channel list using the channel command. You can create new channels by just joining them, like on regular IRC networks. + +You can create two kinds of channels. Control channels, and groupchat channels. By default, BitlBee will set up new channels as control channels if their name starts with an &, and as chat channels if it starts with a #. + +Control channels are where you see your contacts. By default, you will have one control channel called &bitlbee, containing all your contacts. But you can create more, if you want, and divide your contact list across several channels. + +For example, you can have one channel with all contacts from your MSN Messenger account in it. Or all contacts from the group called "Work". + +Type help channels2 to read more. +% +?channels2 +When you create a new channel, BitlBee will try to guess from its name which contacts to fill it with. For example, if the channel name (excluding the &) matches the name of a group in which you have one or more contacts, the channel will contain all those contacts. + +Any valid account ID (so a number, protocol name or part of screenname, as long as it's unique) can also be used as a channel name. So if you just join &msn, it will contain all your MSN contacts. And if you have a Facebook account set up, you can see its contacts by just joining &facebook. + +To start a simple group chat, you simply join a channel which a name starting with #, and invite people into it. All people you invite have to be on the same IM network and contact list. + +If you want to configure your own channels, you can use the channel set command. See help channels3 for more information. +% +?channels3 +The most important setting for a control channel is fill_by. It tells BitlBee what information should be used to decide if someone should be shown in the channel or not. After setting this setting to, for example, account, you also have to set the account setting. Example: + + chan &wlm set fill_by account + fill_by = `account' + chan &wlm set account msn + account = `msn' + +Also, each channel has a show_users setting which lets you choose, for example, if you want to see only online contacts in a channel, or also/just offline contacts. Example: + + chan &offline set show_users offline + show_users = `offline' + +See the help information for all these settings for more information. +% +?nick_format +The nick_format setting can be set globally using the set command, or per account using account set (so that you can set a per-account suffix/prefix or have nicknames generated from full names for certain accounts). + +The setting is basically some kind of format string. It can contain normal text that will be copied to the nick, combined with several variables: + + %nick - Nickname suggested for this contact by the IM protocol, or just the handle if no nickname was suggested. + %handle - The handle/screenname of the contact. + %full_name - The full name of the contact. + %first_name - The first name of the contact (the full name up to the first space). + %group - The name of the group this contact is a member of + %account - Account tag of the contact + +Invalid characters (like spaces) will always be stripped. Depending on your locale settings, characters with accents will be converted to ASCII. + +See help nick_format2 for some more information. +% +?nick_format2 +Two modifiers are currently available: You can include only the first few characters of a variable by putting a number right after the %. For example, [%3group]%-@nick will include only the first three characters of the group name in the nick. + +Also, you can truncate variables from a certain character using the - modifier. For example, you may want to leave out everything after the @. %-@handle will expand to everything in the handle up to the first @. +% +?whatsnew010206 +Twitter support. See help account add twitter. +% +?whatsnew010300 +Support for multiple configurable control channels, each with a subset of your contact list. See help channels for more information. + +File transfer support for some protocols (more if you use libpurple). Just /DCC SEND stuff. Incoming files also become DCC transfers. + +Only if you run your own BitlBee instance: You can build a BitlBee that uses libpurple for connecting to IM networks instead of its own code, adding support for some of the more obscure IM protocols and features. + +Many more things, briefly described in help news1.3. +% +?whatsnew030000 +BitlBee can be compiled with support for OTR message encryption (not available on public servers since encryption should be end-to-end). + +The MSN module was heavily updated to support features added to MSN Messenger over the recent years. You can now see/set status messages, send offline messages, and many strange issues caused by Microsoft breaking old-protocol compatibility should now be resolved. + +Twitter extended: IRC-style replies ("BitlBee:") now get converted to proper Twitter replies ("@BitlBee") and get a reference to the original message (see help set auto_reply_timeout). Retweets and some other stuff is also supported now (see help set commands). +% +?news1.3 +Most of the core of BitlBee was rewritten since the last release. This entry should sum up the majority of the changes. + +First of all, you can now have as many control channels as you want. Or you can have none, it's finally possible to leave &bitlbee and still talk to all your contacts. Or you can have a &work with all your work-related contacts, or a &msn with all your MSN Messenger contacts. See help channels for more information about this. + +Also, you can change how nicknames are generated for your contacts. Like automatically adding a [fb] tag to the nicks of all your Facebook contacts. See help nick_format. + +When you're already connected to a BitlBee server and you connect from elsewhere, you can take over the old session. + +Instead of account numbers, accounts now also get tags. These are automatically generated but can be changed (help set tag). You can now use them instead of accounts numbers. (Example: acc gtalk on) + +Last of all: You can finally change your nickname and shorten root commands (try acc li instead of account list). +% +?whatsnew030005 +OAuth2 support in Jabber module (see help set oauth). For better password security when using Google Talk, Facebook XMPP, or for using MSN Messenger via XMPP. Especially recommended on public servers. + +Starting quick groupchats on Jabber is easier now (using the chat with command, or /join + /invite). + +SSL certificate verification. Works only with GnuTLS, and needs to be enabled by updating your bitlbee.conf. +% +?whatsnew030200 +Upgradeed to Twitter API version 1.1. This is necessary because previous versions will stop working from March 2013. At the same time, BitlBee now supports the streaming API and incoming direct messages. +% +?whatsnew030202 +The OTR plugin now uses libotr 4.0 (AKA libotr5 in debian based distros) + +A few minor fixes/additions, like being able to use /oper to change passwords with account tag set -del password +% +?whatsnew030400 +Lots of bugfixes! Important: Recompiling third party plugins such as bitlbee-steam or bitlbee-facebook is required! + + - twitter: Filter channels - Search by keyword/hashtag or a list of users. See the HowtoTwitter wiki page for more details! + - twitter: Add "rawreply" command, like reply but bitlbee won't add @mention. Also add "favorite" / "fav" command aliases. + - twitter: Start stream from last tweet on connect/reconnect to avoid showing duplicate tweets + - jabber: Fixed crashes with file transfers (they still fail at bypassing NATs, but at least they fail without crashing) + - purple: Improved support for gadugadu, whatsapp and telegram. + - msn: disabled in this release since the protocol we used (MSNP18) stopped working. + - Add a 'pattern' parameter to the blist command, to filter it. + - The utf8_nicks setting should be more reliable now. + +See the full changelog for details! +% +?whatsnew030401 + - msn: Upgraded protocol version to MSNP21, works again + - jabber: Add "hipchat" protocol, for smoother login. Takes the same username as the official client. Note that unlike the 'hip-cat' branch, this doesn't preload channels. See the HowtoHipchat wiki page for details + - jabber: Gmail notifications support + - twitter: Show quoted tweets inline. Added "url" command, can be used to quote tweets. +% +?whatsnew030402 + - irc: Self-messages (messages sent by yourself from other IM clients), see help set self_messages. IRCv3.1 support and part of 3.2. Many important groupchat related bugfixes. + - jabber: Carbons, see help set carbons. Removed facebook XMPP, use bitlbee-facebook instead. SASL ANONYMOUS login, see help set anonymous. + - hipchat: Channels can now be added with chat add hipchat "channel name" which tries to guess the channel JID. + - skype: Show all messages as groupchats since we can't tell which ones are private. This plugin is mostly-deprecated and mostly-broken, use the skypeweb purple plugin or msn instead. + - purple: Fix problems remembering SSL certificates as trusted. Groupchat related fixes. Better error reporting. Fixed setting jabber away states. + +And lots of bugfixes / stability improvements. See the full changelog for details! +% diff -Nru bitlbee-3.4.1/doc/user-guide/Installation.xml bitlbee-3.4.2/doc/user-guide/Installation.xml --- bitlbee-3.4.1/doc/user-guide/Installation.xml 2015-06-16 22:30:40.000000000 +0000 +++ bitlbee-3.4.2/doc/user-guide/Installation.xml 2016-03-19 23:37:33.000000000 +0000 @@ -72,7 +72,7 @@ By default, BitlBee runs as the user nobody. You might want -to run it as a seperate user (some computers run named or apache as nobody). +to run it as a separate user (some computers run named or apache as nobody). diff -Nru bitlbee-3.4.1/doc/user-guide/misc.xml bitlbee-3.4.2/doc/user-guide/misc.xml --- bitlbee-3.4.1/doc/user-guide/misc.xml 2015-06-16 22:30:40.000000000 +0000 +++ bitlbee-3.4.2/doc/user-guide/misc.xml 2016-03-19 23:37:33.000000000 +0000 @@ -89,7 +89,7 @@ -Away states have different names accross different protocols. BitlBee will try to pick the best available option for every connection: +Away states have different names across different protocols. BitlBee will try to pick the best available option for every connection: @@ -145,7 +145,7 @@ -Control channels are where you see your contacts. By default, you will have one control channel called &bitlbee, containing all your contacts. But you can create more, if you want, and divide your contact list accross several channels. +Control channels are where you see your contacts. By default, you will have one control channel called &bitlbee, containing all your contacts. But you can create more, if you want, and divide your contact list across several channels. @@ -444,4 +444,21 @@ + +New stuff in BitlBee 3.4.2 + + + irc: Self-messages (messages sent by yourself from other IM clients), see help set self_messages. IRCv3.1 support and part of 3.2. Many important groupchat related bugfixes. + jabber: Carbons, see help set carbons. Removed facebook XMPP, use bitlbee-facebook instead. SASL ANONYMOUS login, see help set anonymous. + hipchat: Channels can now be added with chat add hipchat "channel name" which tries to guess the channel JID. + skype: Show all messages as groupchats since we can't tell which ones are private. This plugin is mostly-deprecated and mostly-broken, use the skypeweb purple plugin or msn instead. + purple: Fix problems remembering SSL certificates as trusted. Groupchat related fixes. Better error reporting. Fixed setting jabber away states. + + + + And lots of bugfixes / stability improvements. See the full changelog for details! + + + + diff -Nru bitlbee-3.4.1/doc/user-guide/quickstart.xml bitlbee-3.4.2/doc/user-guide/quickstart.xml --- bitlbee-3.4.1/doc/user-guide/quickstart.xml 2015-06-16 22:30:40.000000000 +0000 +++ bitlbee-3.4.2/doc/user-guide/quickstart.xml 2016-03-19 23:37:33.000000000 +0000 @@ -10,7 +10,7 @@ -You need register so that all your IM settings (passwords, contacts, etc) can be saved on the BitlBee server. It's important that you pick a good password so no one else can access your account. Register with this password using the register command: register <password> (without the brackets!). +You need to register so that all your IM settings (passwords, contacts, etc) can be saved on the BitlBee server. It's important that you pick a good password so no one else can access your account. Register with this password using the register command: register <password> (without the brackets!). diff -Nru bitlbee-3.4.1/help.c bitlbee-3.4.2/help.c --- bitlbee-3.4.1/help.c 2015-06-16 22:30:40.000000000 +0000 +++ bitlbee-3.4.2/help.c 2016-03-19 23:37:33.000000000 +0000 @@ -104,7 +104,9 @@ h = *help; while (h) { - if (h->fd != last_fd) { + if (h->fd == -1) { + g_free(h->offset.mem_offset); + } else if (h->fd != last_fd) { close(h->fd); last_fd = h->fd; } @@ -145,6 +147,7 @@ if (lseek(h->fd, h->offset.file_offset, SEEK_SET) == -1 || read(h->fd, s, h->length) != h->length) { + g_free(s); return NULL; } } else { diff -Nru bitlbee-3.4.1/ipc.c bitlbee-3.4.2/ipc.c --- bitlbee-3.4.1/ipc.c 2015-06-16 22:30:40.000000000 +0000 +++ bitlbee-3.4.2/ipc.c 2016-03-19 23:37:33.000000000 +0000 @@ -589,7 +589,7 @@ close(*recv_fd); } - *recv_fd = *(int *) CMSG_DATA(cmsg); + memcpy(recv_fd, CMSG_DATA(cmsg), sizeof(int)); /* fprintf( stderr, "pid %d received fd %d\n", (int) getpid(), *recv_fd ); */ @@ -757,7 +757,7 @@ cmsg->cmsg_level = SOL_SOCKET; cmsg->cmsg_type = SCM_RIGHTS; cmsg->cmsg_len = CMSG_LEN(sizeof(send_fd)); - *(int *) CMSG_DATA(cmsg) = send_fd; + memcpy(CMSG_DATA(cmsg), &send_fd, sizeof(int)); msg.msg_controllen = cmsg->cmsg_len; #endif diff -Nru bitlbee-3.4.1/irc.c bitlbee-3.4.2/irc.c --- bitlbee-3.4.1/irc.c 2015-06-16 22:30:40.000000000 +0000 +++ bitlbee-3.4.2/irc.c 2016-03-19 23:37:33.000000000 +0000 @@ -128,6 +128,7 @@ s->flags |= SET_HIDDEN; s = set_add(&b->set, "show_offline", "false", set_eval_bw_compat, irc); s->flags |= SET_HIDDEN; + s = set_add(&b->set, "self_messages", "true", set_eval_self_messages, irc); s = set_add(&b->set, "simulate_netsplit", "true", set_eval_bool, irc); s = set_add(&b->set, "timezone", "local", set_eval_timezone, irc); s = set_add(&b->set, "to_char", ": ", set_eval_to_char, irc); @@ -152,9 +153,9 @@ /* Evaluator sets the iconv/oconv structures. */ set_eval_charset(set_find(&b->set, "charset"), set_getstr(&b->set, "charset")); - irc_write(irc, ":%s NOTICE AUTH :%s", irc->root->host, "BitlBee-IRCd initialized, please go on"); + irc_write(irc, ":%s NOTICE * :%s", irc->root->host, "BitlBee-IRCd initialized, please go on"); if (isatty(irc->fd)) { - irc_write(irc, ":%s NOTICE AUTH :%s", irc->root->host, + irc_write(irc, ":%s NOTICE * :%s", irc->root->host, "If you read this, you most likely accidentally " "started BitlBee in inetd mode on the command line. " "You probably want to run it in (Fork)Daemon mode. " @@ -379,7 +380,7 @@ g_free(conv); conv = NULL; } else { - irc_write(irc, ":%s NOTICE AUTH :%s", irc->root->host, + irc_write(irc, ":%s NOTICE * :%s", irc->root->host, "Warning: invalid characters received at login time."); conv = g_strdup(lines[i]); @@ -726,7 +727,7 @@ int irc_check_login(irc_t *irc) { - if (irc->user->user && irc->user->nick) { + if (irc->user->user && irc->user->nick && !(irc->status & USTATUS_CAP_PENDING)) { if (global.conf->authmode == AUTHMODE_CLOSED && !(irc->status & USTATUS_AUTHORIZED)) { irc_send_num(irc, 464, ":This server is password-protected."); return 0; diff -Nru bitlbee-3.4.1/irc_cap.c bitlbee-3.4.2/irc_cap.c --- bitlbee-3.4.1/irc_cap.c 1970-01-01 00:00:00.000000000 +0000 +++ bitlbee-3.4.2/irc_cap.c 2016-03-19 23:37:33.000000000 +0000 @@ -0,0 +1,193 @@ +/********************************************************************\ + * BitlBee -- An IRC to other IM-networks gateway * + * * + * Copyright 2002-2013 Wilmer van der Gaast and others * + \********************************************************************/ + +/* IRCv3 CAP command + * + * Specs: + * - v3.1: http://ircv3.net/specs/core/capability-negotiation-3.1.html + * - v3.2: http://ircv3.net/specs/core/capability-negotiation-3.2.html + * + * */ + +/* + 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., 51 Franklin St., + Fifth Floor, Boston, MA 02110-1301 USA +*/ + +#include "bitlbee.h" + +typedef struct { + char *name; + irc_cap_flag_t flag; +} cap_info_t; + +static const cap_info_t supported_caps[] = { + {"sasl", CAP_SASL}, + {"multi-prefix", CAP_MULTI_PREFIX}, + {"extended-join", CAP_EXTENDED_JOIN}, + {"away-notify", CAP_AWAY_NOTIFY}, + {"userhost-in-names", CAP_USERHOST_IN_NAMES}, + {NULL}, +}; + +static irc_cap_flag_t cap_flag_from_string(char *cap_name) +{ + int i; + + if (!cap_name || !cap_name[0]) { + return 0; + } + + if (cap_name[0] == '-') { + cap_name++; + } + + for (i = 0; supported_caps[i].name; i++) { + if (strcmp(supported_caps[i].name, cap_name) == 0) { + return supported_caps[i].flag; + } + } + return 0; +} + +static gboolean irc_cmd_cap_req(irc_t *irc, char *caps) +{ + int i; + char *lower = NULL; + char **split = NULL; + irc_cap_flag_t new_caps = irc->caps; + + if (!caps || !caps[0]) { + return FALSE; + } + + lower = g_ascii_strdown(caps, -1); + split = g_strsplit(lower, " ", -1); + g_free(lower); + + for (i = 0; split[i]; i++) { + gboolean remove; + irc_cap_flag_t flag; + + if (!split[i][0]) { + continue; /* skip empty items (consecutive spaces) */ + } + + remove = (split[i][0] == '-'); + flag = cap_flag_from_string(split[i]); + + if (!flag || (remove && !(irc->caps & flag))) { + /* unsupported cap, or removing something that isn't there */ + g_strfreev(split); + return FALSE; + } + + if (remove) { + new_caps &= ~flag; + } else { + new_caps |= flag; + } + } + + /* if we got here, set the new caps and ack */ + irc->caps = new_caps; + + g_strfreev(split); + return TRUE; +} + +/* version can be 0, 302, 303, or garbage from user input. thanks user input */ +static void irc_cmd_cap_ls(irc_t *irc, long version) +{ + int i; + GString *str = g_string_sized_new(256); + + for (i = 0; supported_caps[i].name; i++) { + if (i != 0) { + g_string_append_c(str, ' '); + } + g_string_append(str, supported_caps[i].name); + + if (version >= 302 && supported_caps[i].flag == CAP_SASL) { + g_string_append(str, "=PLAIN"); + } + } + + irc_send_cap(irc, "LS", str->str); + + g_string_free(str, TRUE); +} + +/* this one looks suspiciously similar to cap ls, + * but cap-3.2 will make them very different */ +static void irc_cmd_cap_list(irc_t *irc) +{ + int i; + gboolean first = TRUE; + GString *str = g_string_sized_new(256); + + for (i = 0; supported_caps[i].name; i++) { + if (irc->caps & supported_caps[i].flag) { + if (!first) { + g_string_append_c(str, ' '); + } + first = FALSE; + + g_string_append(str, supported_caps[i].name); + } + } + + irc_send_cap(irc, "LIST", str->str); + + g_string_free(str, TRUE); +} + +void irc_cmd_cap(irc_t *irc, char **cmd) +{ + if (!(irc->status & USTATUS_LOGGED_IN)) { + /* Put registration on hold until CAP END */ + irc->status |= USTATUS_CAP_PENDING; + } + + if (g_strcasecmp(cmd[1], "LS") == 0) { + irc_cmd_cap_ls(irc, cmd[2] ? strtol(cmd[2], NULL, 10) : 0); + + } else if (g_strcasecmp(cmd[1], "LIST") == 0) { + irc_cmd_cap_list(irc); + + } else if (g_strcasecmp(cmd[1], "REQ") == 0) { + gboolean ack = irc_cmd_cap_req(irc, cmd[2]); + + irc_send_cap(irc, ack ? "ACK" : "NAK", cmd[2] ? : ""); + + } else if (g_strcasecmp(cmd[1], "END") == 0) { + irc->status &= ~USTATUS_CAP_PENDING; + + if (irc->status & USTATUS_SASL_PLAIN_PENDING) { + irc_send_num(irc, 906, ":SASL authentication aborted"); + irc->status &= ~USTATUS_SASL_PLAIN_PENDING; + } + + irc_check_login(irc); + + } else { + irc_send_num(irc, 410, "%s :Invalid CAP command", cmd[1]); + } + +} + diff -Nru bitlbee-3.4.1/irc_channel.c bitlbee-3.4.2/irc_channel.c --- bitlbee-3.4.1/irc_channel.c 2015-06-16 22:30:40.000000000 +0000 +++ bitlbee-3.4.2/irc_channel.c 2016-03-19 23:37:33.000000000 +0000 @@ -212,6 +212,11 @@ return SET_INVALID; } + /* Skip the free/init if nothing is being changed */ + if (ic->f == new) { + return value; + } + /* TODO: Return values. */ if (ic->f && ic->f->_free) { ic->f->_free(ic); @@ -254,6 +259,10 @@ irc_channel_user_t *icu; if (!(icu = irc_channel_has_user(ic, iu))) { + if (iu == ic->irc->user && type == IRC_CDU_KICK) { + /* an error happened before joining, inform the client with a numeric */ + irc_send_num(ic->irc, 403, "%s :Error joining channel (check control channel?)", ic->name); + } return 0; } @@ -428,6 +437,18 @@ } } +char irc_channel_user_get_prefix(irc_channel_user_t *icu) +{ + if (icu->flags & IRC_CHANNEL_USER_OP) { + return '@'; + } else if (icu->flags & IRC_CHANNEL_USER_HALFOP) { + return '%'; + } else if (icu->flags & IRC_CHANNEL_USER_VOICE) { + return '+'; + } + return 0; +} + void irc_channel_auto_joins(irc_t *irc, account_t *acc) { GSList *l; @@ -444,10 +465,10 @@ can only auto-join them if their account is online. */ char *acc_s; - if (!aj || (ic->flags & IRC_CHANNEL_JOINED)) { - /* Only continue if this one's marked as auto_join - or if we're in it already. (Possible if the - client auto-rejoined it before identyfing.) */ + if (!aj && !(ic->flags & IRC_CHANNEL_JOINED)) { + /* Only proceed if this one's marked as auto_join + or if we're in it already. (Very likely the IRC + client auto-(re)joining at reconnect time.) */ continue; } else if (!(acc_s = set_getstr(&ic->set, "account"))) { continue; @@ -592,6 +613,16 @@ gsize bytes_written; translit_name = g_convert_with_fallback(hint, -1, "ASCII//TRANSLIT", "UTF-8", "", NULL, &bytes_written, NULL); + + if (!translit_name) { + /* Same thing as in nick_gen() in nick.c, try again without //TRANSLIT */ + translit_name = g_convert_with_fallback(hint, -1, "ASCII", "UTF-8", "", NULL, &bytes_written, NULL); + } + + if (!translit_name) { + return NULL; + } + if (bytes_written > MAX_NICK_LENGTH) { translit_name[MAX_NICK_LENGTH] = '\0'; } diff -Nru bitlbee-3.4.1/irc_commands.c bitlbee-3.4.2/irc_commands.c --- bitlbee-3.4.1/irc_commands.c 2015-06-16 22:30:40.000000000 +0000 +++ bitlbee-3.4.2/irc_commands.c 2016-03-19 23:37:33.000000000 +0000 @@ -27,6 +27,7 @@ #include "bitlbee.h" #include "help.h" #include "ipc.h" +#include "base64.h" static void irc_cmd_pass(irc_t *irc, char **cmd) { @@ -57,6 +58,120 @@ } } +static gboolean irc_sasl_plain_parse(char *input, char **user, char **pass) +{ + int i, part, len; + guint8 *decoded; + char *parts[3]; + + /* bitlbee's base64_decode wrapper adds an extra null terminator at the end */ + len = base64_decode(input, &decoded); + + /* this loop splits the decoded string into the parts array, like this: + "username\0username\0password" -> {"username", "username", "password"} */ + + for (i = 0, part = 0; i < len && part < 3; part++) { + /* set each of parts[] to point to the beginning of a string */ + parts[part] = (char *) decoded + i; + + /* move the cursor forward to the next null terminator*/ + i += strlen(parts[part]) + 1; + } + + /* sanity checks */ + if (part != 3 || i != (len + 1) || (parts[0][0] && strcmp(parts[0], parts[1]) != 0)) { + g_free(decoded); + return FALSE; + } else { + *user = g_strdup(parts[1]); + *pass = g_strdup(parts[2]); + g_free(decoded); + return TRUE; + } +} + +static gboolean irc_sasl_check_pass(irc_t *irc, char *user, char *pass) +{ + storage_status_t status; + + /* just check the password here to be able to reply with useful numerics + * the actual identification will be handled later */ + status = storage_check_pass(user, pass); + + if (status == STORAGE_OK) { + if (!irc->user->nick) { + /* set the nick here so we have it for the following numeric */ + irc->user->nick = g_strdup(user); + } + irc_send_num(irc, 903, ":Password accepted"); + return TRUE; + + } else if (status == STORAGE_INVALID_PASSWORD) { + irc_send_num(irc, 904, ":Incorrect password"); + } else if (status == STORAGE_NO_SUCH_USER) { + irc_send_num(irc, 904, ":The nick is (probably) not registered"); + } else { + irc_send_num(irc, 904, ":Unknown SASL authentication error"); + } + + return FALSE; +} + +static void irc_cmd_authenticate(irc_t *irc, char **cmd) +{ + /* require the CAP to be enabled, and don't allow authentication before server password */ + if (!(irc->caps & CAP_SASL) || + (global.conf->authmode == AUTHMODE_CLOSED && !(irc->status & USTATUS_AUTHORIZED))) { + return; + } + + if (irc->status & USTATUS_SASL_PLAIN_PENDING) { + char *user, *pass; + + irc->status &= ~USTATUS_SASL_PLAIN_PENDING; + + if (!irc_sasl_plain_parse(cmd[1], &user, &pass)) { + irc_send_num(irc, 904, ":SASL authentication failed"); + return; + } + + /* let's not support the nick != user case + * if NICK is received after SASL, it will just fail after registration */ + if (user && irc->user->nick && strcmp(user, irc->user->nick) != 0) { + irc_send_num(irc, 902, ":Your SASL username does not match your nickname"); + + } else if (irc_sasl_check_pass(irc, user, pass)) { + /* and here we do the same thing as the PASS command*/ + if (irc->status & USTATUS_LOGGED_IN) { + char *send_cmd[] = { "identify", pass, NULL }; + root_command(irc, send_cmd); + } else { + /* no check_login here - wait for CAP END */ + irc_setpass(irc, pass); + } + } + + g_free(user); + g_free(pass); + + } else if (irc->status & USTATUS_IDENTIFIED) { + irc_send_num(irc, 907, ":You have already authenticated"); + + } else if (strcmp(cmd[1], "*") == 0) { + irc_send_num(irc, 906, ":SASL authentication aborted"); + irc->status &= ~USTATUS_SASL_PLAIN_PENDING; + + } else if (g_strcasecmp(cmd[1], "PLAIN") == 0) { + irc_write(irc, "AUTHENTICATE +"); + irc->status |= USTATUS_SASL_PLAIN_PENDING; + + } else { + irc_send_num(irc, 908, "PLAIN :is the available SASL mechanism"); + irc_send_num(irc, 904, ":SASL authentication failed"); + irc->status &= ~USTATUS_SASL_PLAIN_PENDING; + } +} + static void irc_cmd_user(irc_t *irc, char **cmd) { irc->user->user = g_strdup(cmd[1]); @@ -82,6 +197,12 @@ irc_setpass(irc, NULL); irc->status &= ~USTATUS_IDENTIFIED; irc_umode_set(irc, "-R", 1); + + if (irc->caps & CAP_SASL) { + irc_send_num(irc, 901, "%s!%s@%s :You are now logged out", + irc->user->nick, irc->user->user, irc->user->host); + } + irc_rootmsg(irc, "Changing nicks resets your identify status. " "Re-identify or register a new account if you want " "your configuration to be saved. See \x02help " @@ -684,6 +805,7 @@ } static const command_t irc_commands[] = { + { "cap", 1, irc_cmd_cap, 0 }, { "pass", 1, irc_cmd_pass, 0 }, { "user", 4, irc_cmd_user, IRC_CMD_PRE_LOGIN }, { "nick", 1, irc_cmd_nick, 0 }, @@ -720,6 +842,7 @@ { "rehash", 0, irc_cmd_rehash, IRC_CMD_OPER_ONLY }, { "restart", 0, NULL, IRC_CMD_OPER_ONLY | IRC_CMD_TO_MASTER }, { "kill", 2, NULL, IRC_CMD_OPER_ONLY | IRC_CMD_TO_MASTER }, + { "authenticate", 1, irc_cmd_authenticate, 0 }, { NULL } }; diff -Nru bitlbee-3.4.1/irc.h bitlbee-3.4.2/irc.h --- bitlbee-3.4.1/irc.h 2015-06-16 22:30:40.000000000 +0000 +++ bitlbee-3.4.2/irc.h 2016-03-19 23:37:33.000000000 +0000 @@ -48,6 +48,8 @@ USTATUS_IDENTIFIED = 4, /* To NickServ (root). */ USTATUS_SHUTDOWN = 8, /* Now used to indicate we're shutting down. Currently just blocks irc_vawrite(). */ + USTATUS_CAP_PENDING = 16, + USTATUS_SASL_PLAIN_PENDING = 32, /* Not really status stuff, but other kinds of flags: For slightly better password security, since the only way to send passwords @@ -64,6 +66,14 @@ IRC_UTF8_NICKS = 0x10000, /* Disable ASCII restrictions on buddy nicks. */ } irc_status_t; +typedef enum { + CAP_SASL = (1 << 0), + CAP_MULTI_PREFIX = (1 << 1), + CAP_EXTENDED_JOIN = (1 << 2), + CAP_AWAY_NOTIFY = (1 << 3), + CAP_USERHOST_IN_NAMES = (1 << 4), +} irc_cap_flag_t; + struct irc_user; typedef struct irc { @@ -101,6 +111,7 @@ TODO: Some mechanism for plugindata. */ struct bee *b; + guint32 caps; } irc_t; typedef enum { @@ -301,6 +312,7 @@ char *irc_channel_name_gen(irc_t *irc, const char *name); gboolean irc_channel_name_hint(irc_channel_t *ic, const char *name); void irc_channel_update_ops(irc_channel_t *ic, char *value); +char irc_channel_user_get_prefix(irc_channel_user_t *icu); char *set_eval_irc_channel_ops(struct set *set, char *value); gboolean irc_channel_wants_user(irc_channel_t *ic, irc_user_t *iu); @@ -330,6 +342,8 @@ void irc_send_channel_user_mode_diff(irc_channel_t *ic, irc_user_t *iu, irc_channel_user_flags_t old_flags, irc_channel_user_flags_t new_flags); void irc_send_invite(irc_user_t *iu, irc_channel_t *ic); +void irc_send_cap(irc_t *irc, char *subcommand, char *body); +void irc_send_away_notify(irc_user_t *iu); /* irc_user.c */ irc_user_t *irc_user_new(irc_t *irc, const char *nick); @@ -343,9 +357,13 @@ /* irc_util.c */ char *set_eval_timezone(struct set *set, char *value); char *irc_format_timestamp(irc_t *irc, time_t msg_ts); +char *set_eval_self_messages(struct set *set, char *value); /* irc_im.c */ void bee_irc_channel_update(irc_t *irc, irc_channel_t *ic, irc_user_t *iu); void bee_irc_user_nick_reset(irc_user_t *iu); +/* irc_cap.c */ +void irc_cmd_cap(irc_t *irc, char **cmd); + #endif diff -Nru bitlbee-3.4.1/irc_im.c bitlbee-3.4.2/irc_im.c --- bitlbee-3.4.1/irc_im.c 2015-06-16 22:30:40.000000000 +0000 +++ bitlbee-3.4.2/irc_im.c 2016-03-19 23:37:33.000000000 +0000 @@ -83,9 +83,9 @@ } } - while ((s = strchr(iu->user, ' '))) { - *s = '_'; - } + /* Sanitize */ + str_reject_chars(iu->user, " ", '_'); + str_reject_chars(iu->host, " ", '_'); if (bu->flags & BEE_USER_LOCAL) { char *s = set_getstr(&bee->set, "handle_unknown"); @@ -147,6 +147,12 @@ bee_irc_channel_update(irc, NULL, iu); + if ((irc->caps & CAP_AWAY_NOTIFY) && + ((bu->flags & BEE_USER_AWAY) != (old->flags & BEE_USER_AWAY) || + (bu->flags & BEE_USER_ONLINE) != (old->flags & BEE_USER_ONLINE))) { + irc_send_away_notify(iu); + } + return TRUE; } @@ -200,14 +206,17 @@ } } -static gboolean bee_irc_user_msg(bee_t *bee, bee_user_t *bu, const char *msg_, time_t sent_at) +static gboolean bee_irc_user_msg(bee_t *bee, bee_user_t *bu, const char *msg_, guint32 flags, time_t sent_at) { irc_t *irc = bee->ui_data; irc_user_t *iu = (irc_user_t *) bu->ui_data; + irc_user_t *src_iu = iu; + irc_user_t *dst_iu = irc->user; const char *dst; char *prefix = NULL; char *wrapped, *ts = NULL; char *msg = g_strdup(msg_); + char *message_type = "PRIVMSG"; GSList *l; if (sent_at > 0 && set_getbool(&irc->b->set, "display_timestamps")) { @@ -215,9 +224,44 @@ } dst = irc_user_msgdest(iu); - if (dst != irc->user->nick) { - /* if not messaging directly, call user by name */ - prefix = g_strdup_printf("%s%s%s", irc->user->nick, set_getstr(&bee->set, "to_char"), ts ? : ""); + + if (flags & OPT_SELFMESSAGE) { + char *setting = set_getstr(&irc->b->set, "self_messages"); + + if (is_bool(setting)) { + if (bool2int(setting)) { + /* set to true, send it with src/dst flipped */ + + dst_iu = iu; + src_iu = irc->user; + + if (dst == irc->user->nick) { + dst = dst_iu->nick; + } + } else { + /* set to false, skip the message completely */ + goto cleanup; + } + } else if (g_strncasecmp(setting, "prefix", 6) == 0) { + /* third state, prefix, loosely imitates the znc privmsg_prefix module */ + + g_free(msg); + if (g_strncasecmp(msg_, "/me ", 4) == 0) { + msg = g_strdup_printf("/me -> %s", msg_ + 4); + } else { + msg = g_strdup_printf("-> %s", msg_); + } + + if (g_strcasecmp(setting, "prefix_notice") == 0) { + message_type = "NOTICE"; + } + } + + } + + if (dst != dst_iu->nick) { + /* if not messaging directly (control channel), call user by name */ + prefix = g_strdup_printf("%s%s%s", dst_iu->nick, set_getstr(&bee->set, "to_char"), ts ? : ""); } else { prefix = ts; ts = NULL; /* don't double-free */ @@ -248,7 +292,7 @@ } wrapped = word_wrap(msg, 425); - irc_send_msg(iu, "PRIVMSG", dst, wrapped, prefix); + irc_send_msg(src_iu, message_type, dst, wrapped, prefix); g_free(wrapped); cleanup: @@ -259,7 +303,7 @@ return TRUE; } -static gboolean bee_irc_user_typing(bee_t *bee, bee_user_t *bu, uint32_t flags) +static gboolean bee_irc_user_typing(bee_t *bee, bee_user_t *bu, guint32 flags) { irc_t *irc = (irc_t *) bee->ui_data; @@ -616,10 +660,10 @@ return TRUE; } -static gboolean bee_irc_chat_msg(bee_t *bee, struct groupchat *c, bee_user_t *bu, const char *msg, time_t sent_at) +static gboolean bee_irc_chat_msg(bee_t *bee, struct groupchat *c, bee_user_t *bu, const char *msg, guint32 flags, time_t sent_at) { irc_t *irc = bee->ui_data; - irc_user_t *iu = bu->ui_data; + irc_user_t *iu = flags & OPT_SELFMESSAGE ? irc->user : bu->ui_data; irc_channel_t *ic = c->ui_data; char *wrapped, *ts = NULL; @@ -653,7 +697,7 @@ return TRUE; } -static gboolean bee_irc_chat_remove_user(bee_t *bee, struct groupchat *c, bee_user_t *bu) +static gboolean bee_irc_chat_remove_user(bee_t *bee, struct groupchat *c, bee_user_t *bu, const char *reason) { irc_t *irc = bee->ui_data; irc_channel_t *ic = c->ui_data; @@ -665,7 +709,7 @@ /* TODO: Possible bug here: If a module removes $user here instead of just using imcb_chat_free() and the channel was IRC_CHANNEL_TEMP, we get into a broken state around here. */ - irc_channel_del_user(ic, bu == bee->user ? irc->user : bu->ui_data, IRC_CDU_PART, NULL); + irc_channel_del_user(ic, bu == bee->user ? irc->user : bu->ui_data, IRC_CDU_PART, reason); return TRUE; } @@ -835,17 +879,23 @@ if ((acc_s = set_getstr(&ic->set, "account")) && (room = set_getstr(&ic->set, "room")) && (acc = account_get(ic->irc->b, acc_s)) && - acc->ic && acc->prpl->chat_join) { + acc->ic && (acc->ic->flags & OPT_LOGGED_IN) && + acc->prpl->chat_join) { char *nick; + struct groupchat *gc; if (!(nick = set_getstr(&ic->set, "nick"))) { nick = ic->irc->user->nick; } ic->flags |= IRC_CHANNEL_CHAT_PICKME; - acc->prpl->chat_join(acc->ic, room, nick, NULL, &ic->set); + gc = acc->prpl->chat_join(acc->ic, room, nick, NULL, &ic->set); ic->flags &= ~IRC_CHANNEL_CHAT_PICKME; + if (!gc) { + irc_send_num(ic->irc, 403, "%s :Error joining channel (check control channel?)", ic->name); + } + return FALSE; } else { irc_send_num(ic->irc, 403, "%s :Can't join channel, account offline?", ic->name); @@ -861,8 +911,11 @@ c->ic->acc->prpl->chat_leave(c); } - /* Remove the reference. We don't need it anymore. */ - ic->data = NULL; + if (!(ic->flags & IRC_CHANNEL_TEMP)) { + /* Remove the reference. + * We only need it for temp channels that are being freed */ + ic->data = NULL; + } return TRUE; } @@ -1055,6 +1108,13 @@ } } +static void bee_irc_log(bee_t *bee, const char *tag, const char *msg) +{ + irc_t *irc = (irc_t *) bee->ui_data; + + irc_rootmsg(irc, "%s - %s", tag, msg); +} + const struct bee_ui_funcs irc_ui_funcs = { bee_irc_imc_connected, bee_irc_imc_disconnected, @@ -1083,4 +1143,6 @@ bee_irc_ft_out_start, bee_irc_ft_close, bee_irc_ft_finished, + + bee_irc_log, }; diff -Nru bitlbee-3.4.1/irc_send.c bitlbee-3.4.2/irc_send.c --- bitlbee-3.4.1/irc_send.c 2015-06-16 22:30:40.000000000 +0000 +++ bitlbee-3.4.2/irc_send.c 2016-03-19 23:37:33.000000000 +0000 @@ -171,7 +171,11 @@ { irc_t *irc = ic->irc; - irc_write(irc, ":%s!%s@%s JOIN :%s", iu->nick, iu->user, iu->host, ic->name); + if (irc->caps & CAP_EXTENDED_JOIN) { + irc_write(irc, ":%s!%s@%s JOIN %s * :%s", iu->nick, iu->user, iu->host, ic->name, iu->fullname); + } else { + irc_write(irc, ":%s!%s@%s JOIN :%s", iu->nick, iu->user, iu->host, ic->name); + } if (iu == irc->user) { if (ic->topic && *ic->topic) { @@ -197,39 +201,50 @@ kicker->host, ic->name, iu->nick, reason ? : ""); } +#define IRC_NAMES_LEN 385 + void irc_send_names(irc_channel_t *ic) { GSList *l; - char namelist[385] = ""; + GString *namelist = g_string_sized_new(IRC_NAMES_LEN); + gboolean uhnames = (ic->irc->caps & CAP_USERHOST_IN_NAMES); /* RFCs say there is no error reply allowed on NAMES, so when the channel is invalid, just give an empty reply. */ for (l = ic->users; l; l = l->next) { irc_channel_user_t *icu = l->data; irc_user_t *iu = icu->iu; + size_t extra_len = strlen(iu->nick); + char prefix; - if (strlen(namelist) + strlen(iu->nick) > sizeof(namelist) - 4) { - irc_send_num(ic->irc, 353, "= %s :%s", ic->name, namelist); - *namelist = 0; + if (uhnames) { + extra_len += strlen(iu->user) + strlen(iu->host) + 2; } - if (icu->flags & IRC_CHANNEL_USER_OP) { - strcat(namelist, "@"); - } else if (icu->flags & IRC_CHANNEL_USER_HALFOP) { - strcat(namelist, "%"); - } else if (icu->flags & IRC_CHANNEL_USER_VOICE) { - strcat(namelist, "+"); + if (namelist->len + extra_len > IRC_NAMES_LEN - 4) { + irc_send_num(ic->irc, 353, "= %s :%s", ic->name, namelist->str); + g_string_truncate(namelist, 0); } - strcat(namelist, iu->nick); - strcat(namelist, " "); + if ((prefix = irc_channel_user_get_prefix(icu))) { + g_string_append_c(namelist, prefix); + } + + if (uhnames) { + g_string_append_printf(namelist, "%s!%s@%s ", iu->nick, iu->user, iu->host); + } else { + g_string_append(namelist, iu->nick); + g_string_append_c(namelist, ' '); + } } - if (*namelist) { - irc_send_num(ic->irc, 353, "= %s :%s", ic->name, namelist); + if (namelist->len) { + irc_send_num(ic->irc, 353, "= %s :%s", ic->name, namelist->str); } irc_send_num(ic->irc, 366, "%s :End of /NAMES list", ic->name); + + g_string_free(namelist, TRUE); } void irc_send_topic(irc_channel_t *ic, gboolean topic_change) @@ -248,6 +263,32 @@ } } +/* msg1 and msg2 are output parameters. If msg2 is non-null, msg1 is guaranteed to be non-null too. + The idea is to defer the formatting of "$msg1 ($msg2)" to later calls to avoid a g_strdup_printf() here. */ +static void get_status_message(bee_user_t *bu, char **msg1, char **msg2) +{ + *msg1 = NULL; + *msg2 = NULL; + + if (!(bu->flags & BEE_USER_ONLINE)) { + *msg1 = "User is offline"; + + } else if ((bu->status && *bu->status) || + (bu->status_msg && *bu->status_msg)) { + + if (bu->status && bu->status_msg) { + *msg1 = bu->status; + *msg2 = bu->status_msg; + } else { + *msg1 = bu->status ? : bu->status_msg; + } + } + + if (*msg1 && !**msg1) { + *msg1 = (bu->flags & BEE_USER_AWAY) ? "Away" : NULL; + } +} + void irc_send_whois(irc_user_t *iu) { irc_t *irc = iu->irc; @@ -257,22 +298,21 @@ if (iu->bu) { bee_user_t *bu = iu->bu; + char *msg1, *msg2; + int num; irc_send_num(irc, 312, "%s %s.%s :%s network", iu->nick, bu->ic->acc->user, bu->ic->acc->server && *bu->ic->acc->server ? bu->ic->acc->server : "", bu->ic->acc->prpl->name); - if ((bu->status && *bu->status) || - (bu->status_msg && *bu->status_msg)) { - int num = bu->flags & BEE_USER_AWAY ? 301 : 320; + num = (bu->flags & BEE_USER_AWAY || !(bu->flags & BEE_USER_ONLINE)) ? 301 : 320; - if (bu->status && bu->status_msg) { - irc_send_num(irc, num, "%s :%s (%s)", iu->nick, bu->status, bu->status_msg); - } else { - irc_send_num(irc, num, "%s :%s", iu->nick, bu->status ? : bu->status_msg); - } - } else if (!(bu->flags & BEE_USER_ONLINE)) { - irc_send_num(irc, 301, "%s :%s", iu->nick, "User is offline"); + get_status_message(bu, &msg1, &msg2); + + if (msg1 && msg2) { + irc_send_num(irc, num, "%s :%s (%s)", iu->nick, msg1, msg2); + } else if (msg1) { + irc_send_num(irc, num, "%s :%s", iu->nick, msg1); } if (bu->idle_time || bu->login_time) { @@ -293,15 +333,31 @@ gboolean is_channel = strchr(CTYPES, channel[0]) != NULL; while (l) { - irc_user_t *iu = l->data; + irc_user_t *iu; + + /* Null terminated string with three chars, respectively: + * { , <@|%|+|\0>, \0 } */ + char status_prefix[3] = {0}; + if (is_channel) { - iu = ((irc_channel_user_t *) iu)->iu; + irc_channel_user_t *icu = l->data; + status_prefix[1] = irc_channel_user_get_prefix(icu); + iu = icu->iu; + } else { + iu = l->data; } - /* TODO(wilmer): Restore away/channel information here */ - irc_send_num(irc, 352, "%s %s %s %s %s %c :0 %s", + + /* If this is the account nick, check configuration to see if away */ + if (iu == irc->user) { + /* rfc1459 doesn't mention this: G means gone, H means here */ + status_prefix[0] = set_getstr(&irc->b->set, "away") ? 'G' : 'H'; + } else { + status_prefix[0] = iu->flags & IRC_USER_AWAY ? 'G' : 'H'; + } + + irc_send_num(irc, 352, "%s %s %s %s %s %s :0 %s", is_channel ? channel : "*", iu->user, iu->host, irc->root->host, - iu->nick, iu->flags & IRC_USER_AWAY ? 'G' : 'H', - iu->fullname); + iu->nick, status_prefix, iu->fullname); l = l->next; } @@ -427,3 +483,34 @@ irc_write(iu->irc, ":%s!%s@%s INVITE %s :%s", iu->nick, iu->user, iu->host, irc->user->nick, ic->name); } + +void irc_send_cap(irc_t *irc, char *subcommand, char *body) +{ + char *nick = irc->user->nick ? : "*"; + + irc_write(irc, ":%s CAP %s %s :%s", irc->root->host, nick, subcommand, body); +} + +void irc_send_away_notify(irc_user_t *iu) +{ + bee_user_t *bu = iu->bu; + + if (!bu) { + return; + } + + if (bu->flags & BEE_USER_AWAY || !(bu->flags & BEE_USER_ONLINE)) { + char *msg1, *msg2; + + get_status_message(bu, &msg1, &msg2); + + if (msg2) { + irc_write(iu->irc, ":%s!%s@%s AWAY :%s (%s)", iu->nick, iu->user, iu->host, msg1, msg2); + } else { + irc_write(iu->irc, ":%s!%s@%s AWAY :%s", iu->nick, iu->user, iu->host, msg1); + } + } else { + irc_write(iu->irc, ":%s!%s@%s AWAY", iu->nick, iu->user, iu->host); + } +} + diff -Nru bitlbee-3.4.1/irc_util.c bitlbee-3.4.2/irc_util.c --- bitlbee-3.4.1/irc_util.c 2015-06-16 22:30:40.000000000 +0000 +++ bitlbee-3.4.2/irc_util.c 2016-03-19 23:37:33.000000000 +0000 @@ -118,3 +118,15 @@ msg.tm_hour, msg.tm_min, msg.tm_sec); } } + + +char *set_eval_self_messages(set_t *set, char *value) +{ + if (is_bool(value) || + g_strcasecmp(value, "prefix") == 0 || + g_strcasecmp(value, "prefix_notice") == 0) { + return value; + } else { + return SET_INVALID; + } +} diff -Nru bitlbee-3.4.1/lib/http_client.c bitlbee-3.4.2/lib/http_client.c --- bitlbee-3.4.1/lib/http_client.c 2015-06-16 22:30:40.000000000 +0000 +++ bitlbee-3.4.2/lib/http_client.c 2016-03-19 23:37:33.000000000 +0000 @@ -161,7 +161,9 @@ req->status_string = g_strdup("Error while writing HTTP request"); } - req->func(req); + if (req->func != NULL) { + req->func(req); + } http_free(req); return FALSE; } @@ -298,7 +300,9 @@ req->status_string ? req->status_string : "NULL"); } - req->func(req); + if (req->func != NULL) { + req->func(req); + } http_free(req); return FALSE; } @@ -411,7 +415,7 @@ req->body_size = req->sblen - pos; } - if ((req->flags & HTTPC_STREAMING) && req->reply_body) { + if ((req->flags & HTTPC_STREAMING) && req->reply_body && req->func != NULL) { req->func(req); } @@ -695,7 +699,7 @@ if (req->ssl) { ssl_disconnect(req->ssl); } else { - closesocket(req->fd); + proxy_disconnect(req->fd); } http_free(req); diff -Nru bitlbee-3.4.1/lib/ini.c bitlbee-3.4.2/lib/ini.c --- bitlbee-3.4.1/lib/ini.c 2015-06-16 22:30:40.000000000 +0000 +++ bitlbee-3.4.2/lib/ini.c 2016-03-19 23:37:33.000000000 +0000 @@ -27,11 +27,11 @@ ini_t *ini_open(char *file) { - int fd; + int fd = -1; ini_t *ini = NULL; struct stat fi; - if ((fd = open(file, O_RDONLY)) != -1 && + if (file && (fd = open(file, O_RDONLY)) != -1 && fstat(fd, &fi) == 0 && fi.st_size <= 16384 && (ini = g_malloc(sizeof(ini_t) + fi.st_size + 1)) && diff -Nru bitlbee-3.4.1/lib/json.c bitlbee-3.4.2/lib/json.c --- bitlbee-3.4.1/lib/json.c 2015-06-16 22:30:40.000000000 +0000 +++ bitlbee-3.4.2/lib/json.c 2016-03-19 23:37:33.000000000 +0000 @@ -508,7 +508,7 @@ case ']': - if (top->type == json_array) { + if (top && top->type == json_array) { flags = (flags & ~(flag_need_comma | flag_seek_value)) | flag_next; } else { sprintf(error, "%d:%d: Unexpected ]", cur_line, e_off); goto e_failed; } diff -Nru bitlbee-3.4.1/lib/misc.c bitlbee-3.4.2/lib/misc.c --- bitlbee-3.4.1/lib/misc.c 2015-06-16 22:30:40.000000000 +0000 +++ bitlbee-3.4.2/lib/misc.c 2016-03-19 23:37:33.000000000 +0000 @@ -186,6 +186,10 @@ *(s++) = '\x1f'; } else if (g_strncasecmp(cs + 1, "br", taglen) == 0) { *(s++) = '\n'; + } else if (g_strncasecmp(cs + 1, "br/", taglen) == 0) { + *(s++) = '\n'; + } else if (g_strncasecmp(cs + 1, "br /", taglen) == 0) { + *(s++) = '\n'; } in++; } else { @@ -294,7 +298,7 @@ } /* Warning: This one explodes the string. Worst-cases can make the string 3x its original size! */ -/* This fuction is safe, but make sure you call it safely as well! */ +/* This function is safe, but make sure you call it safely as well! */ void http_encode(char *s) { char t[strlen(s) + 1]; @@ -766,3 +770,19 @@ return TRUE; } +/* Filters all the characters in 'blacklist' replacing them with 'replacement'. + * Modifies the string in-place and returns the string itself. + * For the opposite, use g_strcanon() */ +char *str_reject_chars(char *string, const char *reject, char replacement) +{ + char *c = string; + + while (*c) { + c += strcspn(c, reject); + if (*c) { + *c = replacement; + } + } + + return string; +} diff -Nru bitlbee-3.4.1/lib/misc.h bitlbee-3.4.2/lib/misc.h --- bitlbee-3.4.1/lib/misc.h 2015-06-16 22:30:40.000000000 +0000 +++ bitlbee-3.4.2/lib/misc.h 2016-03-19 23:37:33.000000000 +0000 @@ -149,5 +149,6 @@ G_MODULE_EXPORT char *get_rfc822_header(const char *text, const char *header, int len); G_MODULE_EXPORT int truncate_utf8(char *string, int maxlen); G_MODULE_EXPORT gboolean parse_int64(char *string, int base, guint64 *number); +G_MODULE_EXPORT char *str_reject_chars(char *string, const char *reject, char replacement); #endif diff -Nru bitlbee-3.4.1/lib/proxy.c bitlbee-3.4.2/lib/proxy.c --- bitlbee-3.4.1/lib/proxy.c 2015-06-16 22:30:40.000000000 +0000 +++ bitlbee-3.4.2/lib/proxy.c 2016-03-19 23:37:33.000000000 +0000 @@ -50,6 +50,8 @@ #define AI_ADDRCONFIG 0 #endif +static GHashTable *phb_hash = NULL; + struct PHB { b_event_handler func, proxy_func; gpointer data, proxy_data; @@ -60,17 +62,49 @@ struct addrinfo *gai, *gai_cur; }; +typedef int (*proxy_connect_func)(const char *host, unsigned short port_, struct PHB *phb); + static int proxy_connect_none(const char *host, unsigned short port_, struct PHB *phb); -static gboolean phb_close(struct PHB *phb) +static gboolean phb_free(struct PHB *phb, gboolean success) { - close(phb->fd); - phb->func(phb->data, -1, B_EV_IO_READ); + g_hash_table_remove(phb_hash, &phb->fd); + + if (!success) { + if (phb->fd > 0) { + closesocket(phb->fd); + } + if (phb->func) { + phb->func(phb->data, -1, B_EV_IO_READ); + } + } + if (phb->gai) { + freeaddrinfo(phb->gai); + } g_free(phb->host); g_free(phb); return FALSE; } +/* calls phb->func safely by ensuring that the phb struct doesn't exist in the + * case that proxy_disconnect() is called down there */ +static gboolean phb_connected(struct PHB *phb, gint source) +{ + /* save func and data here */ + b_event_handler func = phb->func; + gpointer data = phb->data; + + /* free the struct so that it can't be freed by the callback */ + phb_free(phb, TRUE); + + /* if any proxy_disconnect() call happens here, it will use the + * fd (still open), look it up in the hash table, get NULL, and + * proceed to close the fd and do nothing else */ + func(data, source, B_EV_IO_READ); + + return FALSE; +} + static gboolean proxy_connected(gpointer data, gint source, b_input_condition cond) { struct PHB *phb = data; @@ -88,6 +122,7 @@ closesocket(source); dup2(new_fd, source); closesocket(new_fd); + phb->fd = source; phb->inpa = b_input_add(source, B_EV_IO_WRITE, proxy_connected, phb); return FALSE; } @@ -100,13 +135,15 @@ } freeaddrinfo(phb->gai); + phb->gai = NULL; + b_event_remove(phb->inpa); phb->inpa = 0; + if (phb->proxy_func) { phb->proxy_func(phb->proxy_data, source, B_EV_IO_READ); } else { - phb->func(phb->data, source, B_EV_IO_READ); - g_free(phb); + phb_connected(phb, source); } return FALSE; @@ -170,7 +207,7 @@ } if (fd < 0 && host) { - g_free(phb); + phb_free(phb, TRUE); } return fd; @@ -202,13 +239,10 @@ if ((memcmp(HTTP_GOODSTRING, inputline, strlen(HTTP_GOODSTRING)) == 0) || (memcmp(HTTP_GOODSTRING2, inputline, strlen(HTTP_GOODSTRING2)) == 0)) { - phb->func(phb->data, source, B_EV_IO_READ); - g_free(phb->host); - g_free(phb); - return FALSE; + return phb_connected(phb, source); } - return phb_close(phb); + return phb_free(phb, FALSE); } static gboolean http_canwrite(gpointer data, gint source, b_input_condition cond) @@ -223,14 +257,14 @@ } len = sizeof(error); if (getsockopt(source, SOL_SOCKET, SO_ERROR, &error, &len) < 0) { - return phb_close(phb); + return phb_free(phb, FALSE); } sock_make_blocking(source); g_snprintf(cmd, sizeof(cmd), "CONNECT %s:%d HTTP/1.1\r\nHost: %s:%d\r\n", phb->host, phb->port, phb->host, phb->port); if (send(source, cmd, strlen(cmd), 0) < 0) { - return phb_close(phb); + return phb_free(phb, FALSE); } if (strlen(proxyuser) > 0) { @@ -241,13 +275,13 @@ g_snprintf(cmd, sizeof(cmd), "Proxy-Authorization: Basic %s\r\n", t2); g_free(t2); if (send(source, cmd, strlen(cmd), 0) < 0) { - return phb_close(phb); + return phb_free(phb, FALSE); } } g_snprintf(cmd, sizeof(cmd), "\r\n"); if (send(source, cmd, strlen(cmd), 0) < 0) { - return phb_close(phb); + return phb_free(phb, FALSE); } phb->inpa = b_input_add(source, B_EV_IO_READ, http_canread, phb); @@ -277,13 +311,10 @@ memset(packet, 0, sizeof(packet)); if (read(source, packet, 9) >= 4 && packet[1] == 90) { - phb->func(phb->data, source, B_EV_IO_READ); - g_free(phb->host); - g_free(phb); - return FALSE; + return phb_connected(phb, source); } - return phb_close(phb); + return phb_free(phb, FALSE); } static gboolean s4_canwrite(gpointer data, gint source, b_input_condition cond) @@ -293,32 +324,47 @@ struct PHB *phb = data; socklen_t len; int error = ETIMEDOUT; + gboolean is_socks4a = (proxytype == PROXY_SOCKS4A); if (phb->inpa > 0) { b_event_remove(phb->inpa); } len = sizeof(error); if (getsockopt(source, SOL_SOCKET, SO_ERROR, &error, &len) < 0) { - return phb_close(phb); + return phb_free(phb, FALSE); } sock_make_blocking(source); - /* XXX does socks4 not support host name lookups by the proxy? */ - if (!(hp = gethostbyname(phb->host))) { - return phb_close(phb); + if (!is_socks4a && !(hp = gethostbyname(phb->host))) { + return phb_free(phb, FALSE); } packet[0] = 4; packet[1] = 1; packet[2] = phb->port >> 8; packet[3] = phb->port & 0xff; - packet[4] = (unsigned char) (hp->h_addr_list[0])[0]; - packet[5] = (unsigned char) (hp->h_addr_list[0])[1]; - packet[6] = (unsigned char) (hp->h_addr_list[0])[2]; - packet[7] = (unsigned char) (hp->h_addr_list[0])[3]; + if (is_socks4a) { + packet[4] = 0; + packet[5] = 0; + packet[6] = 0; + packet[7] = 1; + } else { + packet[4] = (unsigned char) (hp->h_addr_list[0])[0]; + packet[5] = (unsigned char) (hp->h_addr_list[0])[1]; + packet[6] = (unsigned char) (hp->h_addr_list[0])[2]; + packet[7] = (unsigned char) (hp->h_addr_list[0])[3]; + } packet[8] = 0; if (write(source, packet, 9) != 9) { - return phb_close(phb); + return phb_free(phb, FALSE); + } + + if (is_socks4a) { + size_t host_len = strlen(phb->host) + 1; /* include the \0 */ + + if (write(source, phb->host, host_len) != host_len) { + return phb_free(phb, FALSE); + } } phb->inpa = b_input_add(source, B_EV_IO_READ, s4_canread, phb); @@ -347,17 +393,13 @@ b_event_remove(phb->inpa); if (read(source, buf, 10) < 10) { - return phb_close(phb); + return phb_free(phb, FALSE); } if ((buf[0] != 0x05) || (buf[1] != 0x00)) { - return phb_close(phb); + return phb_free(phb, FALSE); } - phb->func(phb->data, source, B_EV_IO_READ); - g_free(phb->host); - g_free(phb); - - return FALSE; + return phb_connected(phb, source); } static void s5_sendconnect(gpointer data, gint source) @@ -376,7 +418,7 @@ buf[5 + strlen(phb->host) + 1] = phb->port & 0xff; if (write(source, buf, (5 + strlen(phb->host) + 2)) < (5 + strlen(phb->host) + 2)) { - phb_close(phb); + phb_free(phb, FALSE); return; } @@ -391,11 +433,11 @@ b_event_remove(phb->inpa); if (read(source, buf, 2) < 2) { - return phb_close(phb); + return phb_free(phb, FALSE); } if ((buf[0] != 0x01) || (buf[1] != 0x00)) { - return phb_close(phb); + return phb_free(phb, FALSE); } s5_sendconnect(phb, source); @@ -411,11 +453,11 @@ b_event_remove(phb->inpa); if (read(source, buf, 2) < 2) { - return phb_close(phb); + return phb_free(phb, FALSE); } if ((buf[0] != 0x05) || (buf[1] == 0xff)) { - return phb_close(phb); + return phb_free(phb, FALSE); } if (buf[1] == 0x02) { @@ -426,7 +468,7 @@ buf[2 + i] = j; memcpy(buf + 2 + i + 1, proxypass, j); if (write(source, buf, 3 + i + j) < 3 + i + j) { - return phb_close(phb); + return phb_free(phb, FALSE); } phb->inpa = b_input_add(source, B_EV_IO_READ, s5_readauth, phb); @@ -450,7 +492,7 @@ } len = sizeof(error); if (getsockopt(source, SOL_SOCKET, SO_ERROR, &error, &len) < 0) { - return phb_close(phb); + return phb_free(phb, FALSE); } sock_make_blocking(source); @@ -468,7 +510,7 @@ } if (write(source, buf, i) < i) { - return phb_close(phb); + return phb_free(phb, FALSE); } phb->inpa = b_input_add(source, B_EV_IO_READ, s5_canread, phb); @@ -486,12 +528,25 @@ return(proxy_connect_none(proxyhost, proxyport, phb)); } +static const proxy_connect_func proxy_connect_funcs_array[] = { + proxy_connect_none, /* PROXY_NONE */ + proxy_connect_http, /* PROXY_HTTP */ + proxy_connect_socks4, /* PROXY_SOCKS4 */ + proxy_connect_socks5, /* PROXY_SOCKS5 */ + proxy_connect_socks4, /* PROXY_SOCKS4A */ +}; /* Export functions */ int proxy_connect(const char *host, int port, b_event_handler func, gpointer data) { struct PHB *phb; + proxy_connect_func fun; + int fd; + + if (!phb_hash) { + phb_hash = g_hash_table_new(g_int_hash, g_int_equal); + } if (!host || port <= 0 || !func || strlen(host) > 128) { return -1; @@ -501,16 +556,39 @@ phb->func = func; phb->data = data; - if (proxytype == PROXY_NONE || !proxyhost[0] || proxyport <= 0) { - return proxy_connect_none(host, port, phb); - } else if (proxytype == PROXY_HTTP) { - return proxy_connect_http(host, port, phb); - } else if (proxytype == PROXY_SOCKS4) { - return proxy_connect_socks4(host, port, phb); - } else if (proxytype == PROXY_SOCKS5) { - return proxy_connect_socks5(host, port, phb); + if (proxyhost[0] && proxyport > 0 && proxytype >= 0 && proxytype < G_N_ELEMENTS(proxy_connect_funcs_array)) { + fun = proxy_connect_funcs_array[proxytype]; + } else { + fun = proxy_connect_none; } - g_free(phb); - return -1; + fd = fun(host, port, phb); + + if (fd != -1) { + g_hash_table_insert(phb_hash, &phb->fd, phb); + } + + return fd; +} + +void proxy_disconnect(int fd) +{ + struct PHB *phb = g_hash_table_lookup(phb_hash, &fd); + + if (!phb) { + /* not in the early part of the connection - just close the fd */ + closesocket(fd); + return; + } + + if (phb->inpa) { + b_event_remove(phb->inpa); + phb->inpa = 0; + } + + /* avoid calling the callback, which might result in double-free */ + phb->func = NULL; + + /* close and free */ + phb_free(phb, FALSE); } diff -Nru bitlbee-3.4.1/lib/proxy.h bitlbee-3.4.2/lib/proxy.h --- bitlbee-3.4.1/lib/proxy.h 2015-06-16 22:30:40.000000000 +0000 +++ bitlbee-3.4.2/lib/proxy.h 2016-03-19 23:37:33.000000000 +0000 @@ -39,6 +39,7 @@ #define PROXY_HTTP 1 #define PROXY_SOCKS4 2 #define PROXY_SOCKS5 3 +#define PROXY_SOCKS4A 4 extern char proxyhost[128]; extern int proxyport; @@ -47,5 +48,6 @@ extern char proxypass[128]; G_MODULE_EXPORT int proxy_connect(const char *host, int port, b_event_handler func, gpointer data); +G_MODULE_EXPORT void proxy_disconnect(int fd); #endif /* _PROXY_H_ */ diff -Nru bitlbee-3.4.1/lib/ssl_gnutls.c bitlbee-3.4.2/lib/ssl_gnutls.c --- bitlbee-3.4.1/lib/ssl_gnutls.c 2015-06-16 22:30:40.000000000 +0000 +++ bitlbee-3.4.2/lib/ssl_gnutls.c 2016-03-19 23:37:33.000000000 +0000 @@ -454,7 +454,7 @@ gnutls_bye(conn->session, GNUTLS_SHUT_WR); } - closesocket(conn->fd); + proxy_disconnect(conn->fd); if (conn->session) { gnutls_deinit(conn->session); diff -Nru bitlbee-3.4.1/lib/ssl_nss.c bitlbee-3.4.2/lib/ssl_nss.c --- bitlbee-3.4.1/lib/ssl_nss.c 2015-06-16 22:30:40.000000000 +0000 +++ bitlbee-3.4.2/lib/ssl_nss.c 2016-03-19 23:37:33.000000000 +0000 @@ -225,8 +225,8 @@ if (conn->prfd) { PR_Close(conn->prfd); - } - if (source >= 0) { + } else if (source >= 0) { + /* proxy_disconnect() would be redundant here */ closesocket(source); } g_free(conn->hostname); @@ -304,6 +304,8 @@ if (conn->prfd) { PR_Close(conn->prfd); + } else if (conn->fd) { + proxy_disconnect(conn->fd); } g_free(conn->hostname); diff -Nru bitlbee-3.4.1/lib/ssl_openssl.c bitlbee-3.4.2/lib/ssl_openssl.c --- bitlbee-3.4.1/lib/ssl_openssl.c 2015-06-16 22:30:40.000000000 +0000 +++ bitlbee-3.4.2/lib/ssl_openssl.c 2016-03-19 23:37:33.000000000 +0000 @@ -130,7 +130,7 @@ /* Right now we don't have any verification functionality for OpenSSL. */ conn->func(conn->data, 1, NULL, cond); if (source >= 0) { - closesocket(source); + proxy_disconnect(source); } ssl_conn_free(conn); @@ -275,7 +275,7 @@ SSL_shutdown(conn->ssl); } - closesocket(conn->fd); + proxy_disconnect(conn->fd); ssl_conn_free(conn); } diff -Nru bitlbee-3.4.1/lib/url.c bitlbee-3.4.2/lib/url.c --- bitlbee-3.4.1/lib/url.c 2015-06-16 22:30:40.000000000 +0000 +++ bitlbee-3.4.2/lib/url.c 2016-03-19 23:37:33.000000000 +0000 @@ -47,6 +47,8 @@ url->proto = PROTO_SOCKS4; } else if (g_strncasecmp(set_url, "socks5", i - set_url) == 0) { url->proto = PROTO_SOCKS5; + } else if (g_strncasecmp(set_url, "socks4a", i - set_url) == 0) { + url->proto = PROTO_SOCKS4A; } else { return 0; } diff -Nru bitlbee-3.4.1/lib/url.h bitlbee-3.4.2/lib/url.h --- bitlbee-3.4.1/lib/url.h 2015-06-16 22:30:40.000000000 +0000 +++ bitlbee-3.4.2/lib/url.h 2016-03-19 23:37:33.000000000 +0000 @@ -29,6 +29,7 @@ #define PROTO_HTTPS 5 #define PROTO_SOCKS4 3 #define PROTO_SOCKS5 4 +#define PROTO_SOCKS4A 5 #define PROTO_DEFAULT PROTO_HTTP typedef struct url { diff -Nru bitlbee-3.4.1/log.c bitlbee-3.4.2/log.c --- bitlbee-3.4.1/log.c 2015-06-16 22:30:40.000000000 +0000 +++ bitlbee-3.4.2/log.c 2016-03-19 23:37:33.000000000 +0000 @@ -50,50 +50,28 @@ void log_link(int level, int output) { - /* I know it's ugly, but it works and I didn't feel like messing with pointer to function pointers */ + void (*output_function)(int level, const char *logmessage) = &log_null; + + if (output == LOGOUTPUT_NULL) { + output_function = &log_null; + } else if (output == LOGOUTPUT_IRC) { + output_function = &log_irc; + } else if (output == LOGOUTPUT_SYSLOG) { + output_function = &log_syslog; + } else if (output == LOGOUTPUT_CONSOLE) { + output_function = &log_console; + } if (level == LOGLVL_INFO) { - if (output == LOGOUTPUT_NULL) { - logoutput.informational = &log_null; - } else if (output == LOGOUTPUT_IRC) { - logoutput.informational = &log_irc; - } else if (output == LOGOUTPUT_SYSLOG) { - logoutput.informational = &log_syslog; - } else if (output == LOGOUTPUT_CONSOLE) { - logoutput.informational = &log_console; - } + logoutput.informational = output_function; } else if (level == LOGLVL_WARNING) { - if (output == LOGOUTPUT_NULL) { - logoutput.warning = &log_null; - } else if (output == LOGOUTPUT_IRC) { - logoutput.warning = &log_irc; - } else if (output == LOGOUTPUT_SYSLOG) { - logoutput.warning = &log_syslog; - } else if (output == LOGOUTPUT_CONSOLE) { - logoutput.warning = &log_console; - } + logoutput.warning = output_function; } else if (level == LOGLVL_ERROR) { - if (output == LOGOUTPUT_NULL) { - logoutput.error = &log_null; - } else if (output == LOGOUTPUT_IRC) { - logoutput.error = &log_irc; - } else if (output == LOGOUTPUT_SYSLOG) { - logoutput.error = &log_syslog; - } else if (output == LOGOUTPUT_CONSOLE) { - logoutput.error = &log_console; - } + logoutput.error = output_function; } #ifdef DEBUG else if (level == LOGLVL_DEBUG) { - if (output == LOGOUTPUT_NULL) { - logoutput.debug = &log_null; - } else if (output == LOGOUTPUT_IRC) { - logoutput.debug = &log_irc; - } else if (output == LOGOUTPUT_SYSLOG) { - logoutput.debug = &log_syslog; - } else if (output == LOGOUTPUT_CONSOLE) { - logoutput.debug = &log_console; - } + logoutput.debug = output_function; } #endif return; diff -Nru bitlbee-3.4.1/Makefile bitlbee-3.4.2/Makefile --- bitlbee-3.4.1/Makefile 2015-06-16 22:30:40.000000000 +0000 +++ bitlbee-3.4.2/Makefile 2016-03-19 23:37:33.000000000 +0000 @@ -9,7 +9,7 @@ -include Makefile.settings # Program variables -objects = bitlbee.o dcc.o help.o ipc.o irc.o irc_im.o irc_channel.o irc_commands.o irc_send.o irc_user.o irc_util.o nick.o $(OTR_BI) query.o root_commands.o set.o storage.o $(STORAGE_OBJS) unix.o conf.o log.o +objects = bitlbee.o dcc.o help.o ipc.o irc.o irc_im.o irc_cap.o irc_channel.o irc_commands.o irc_send.o irc_user.o irc_util.o nick.o $(OTR_BI) query.o root_commands.o set.o storage.o $(STORAGE_OBJS) unix.o conf.o log.o headers = $(wildcard $(_SRCDIR_)*.h $(_SRCDIR_)lib/*.h $(_SRCDIR_)protocols/*.h) subdirs = lib protocols @@ -29,15 +29,19 @@ @echo -e '\nmake uninstall does not remove files in '$(DESTDIR)$(ETCDIR)', you can use make uninstall-etc to do that.\n' install: install-bin install-doc install-plugins + @echo + @echo Installed successfully + @echo @if ! [ -d $(DESTDIR)$(CONFIG) ]; then echo -e '\nThe configuration directory $(DESTDIR)$(CONFIG) does not exist yet, don'\''t forget to create it!'; fi @if ! [ -e $(DESTDIR)$(ETCDIR)/bitlbee.conf ]; then echo -e '\nNo files are installed in '$(DESTDIR)$(ETCDIR)' by make install. Run make install-etc to do that.'; fi ifdef SYSTEMDSYSTEMUNITDIR @echo If you want to start BitlBee using systemd, try \"make install-systemd\". endif + @echo To be able to compile third party plugins, run \"make install-dev\" @echo -.PHONY: install install-bin install-etc install-doc install-plugins install-systemd \ - uninstall uninstall-bin uninstall-etc uninstall-doc \ +.PHONY: install install-bin install-etc install-doc install-plugins install-systemd install-dev \ + uninstall uninstall-bin uninstall-etc uninstall-doc uninstall-etc \ all clean distclean tar $(subdirs) doc Makefile.settings: diff -Nru bitlbee-3.4.1/otr.c bitlbee-3.4.2/otr.c --- bitlbee-3.4.1/otr.c 2015-06-16 22:30:40.000000000 +0000 +++ bitlbee-3.4.2/otr.c 2016-03-19 23:37:33.000000000 +0000 @@ -186,7 +186,7 @@ /* 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 */ + i.e. msgstate should be announced separately */ int otr_update_modeflags(irc_t *irc, irc_user_t *u); /* show general info about the OTR subsystem; called by 'otr info' */ @@ -215,9 +215,16 @@ /* close all active OTR connections */ void otr_disconnect_all(irc_t *irc); +/* modifies string in-place, replacing \x03 with '?', + as a quick way to prevent remote users from messing with irc colors */ +static char *otr_filter_colors(char *msg); + /* functions to be called for certain events */ static const struct irc_plugin otr_plugin; +#define OTR_COLOR_TRUSTED "03" /* green */ +#define OTR_COLOR_UNTRUSTED "05" /* red */ + /*** routines declared in otr.h: ***/ #ifdef OTR_BI @@ -433,7 +440,8 @@ struct im_connection *ic = iu->bu->ic; /* don't do OTR on certain (not classic IM) protocols, e.g. twitter */ - if (ic->acc->prpl->options & OPT_NOOTR) { + if (ic->acc->prpl->options & OPT_NOOTR || + iu->bu->flags & BEE_USER_NOOTR) { return msg; } @@ -450,7 +458,7 @@ return NULL; } else if (!newmsg) { /* this was a non-OTR message */ - return msg; + return otr_filter_colors(msg); } else { /* we're done with the original msg, which will be caller-freed. */ return newmsg; @@ -471,7 +479,8 @@ */ /* don't do OTR on certain (not classic IM) protocols, e.g. twitter */ - if (ic->acc->prpl->options & OPT_NOOTR) { + if (ic->acc->prpl->options & OPT_NOOTR || + iu->bu->flags & BEE_USER_NOOTR) { return msg; } @@ -741,6 +750,51 @@ } } +static char *otr_filter_colors(char *msg) +{ + return str_reject_chars(msg, "\x02\x03", '?'); +} + +/* returns newly allocated string */ +static char *otr_color_encrypted(char *msg, char *color, gboolean is_query) { + char **lines; + GString *out; + int i; + + lines = g_strsplit(msg, "\n", -1); + + /* up to 4 extra chars per line (e.g., '\x03' + ("03"|"05") + ' ') */ + out = g_string_sized_new(strlen(msg) + g_strv_length(lines) * 4); + + for (i = 0; lines[i]; i++) { + char *line = lines[i]; + + if (i != 0) { + g_string_append_c(out, '\n'); + + } else if (is_query && g_strncasecmp(line, "/me ", 4) == 0) { + /* in a query window, keep "/me " uncolored at the beginning */ + line += 4; + g_string_append(out, "/me "); + } + + g_string_append_c(out, '\x03'); + g_string_append(out, color); + + /* comma in first place could mess with the color code */ + if (line[0] == ',') { + /* insert a space between color spec and message */ + g_string_append_c(out, ' '); + } + + g_string_append(out, otr_filter_colors(line)); + } + + g_strfreev(lines); + + return g_string_free(out, FALSE); +} + void op_convert_msg(void *opdata, ConnContext *ctx, OtrlConvertType typ, char **dst, const char *src) { @@ -751,44 +805,28 @@ if (typ == OTRL_CONVERT_RECEIVING) { char *msg = g_strdup(src); - char *buf = msg; /* HTML decoding */ if (set_getbool(&ic->bee->set, "otr_does_html") && !(ic->flags & OPT_DOES_HTML) && set_getbool(&ic->bee->set, "strip_html")) { strip_html(msg); + + /* msg is borrowed by *dst (unless the next if decides to color it) */ *dst = msg; } /* coloring */ if (set_getbool(&ic->bee->set, "otr_color_encrypted")) { - int color; /* color according to f'print trust */ - char *pre = "", *sep = ""; /* optional parts */ const char *trust = ctx->active_fingerprint->trust; + char *color = (trust && *trust) ? OTR_COLOR_TRUSTED : OTR_COLOR_UNTRUSTED; + gboolean is_query = (irc_user_msgdest(iu) == irc->user->nick); - if (trust && trust[0] != '\0') { - color = 3; /* green */ - } else { - color = 5; /* red */ + /* the return value of otr_color_encrypted() is borrowed by *dst */ + *dst = otr_color_encrypted(msg, color, is_query); - } - /* in a query window, keep "/me " uncolored at the beginning */ - if (g_strncasecmp(msg, "/me ", 4) == 0 - && irc_user_msgdest(iu) == irc->user->nick) { - msg += 4; /* skip */ - pre = "/me "; - } - - /* comma in first place could mess with the color code */ - if (msg[0] == ',') { - /* insert a space between color spec and message */ - sep = " "; - } - - *dst = g_strdup_printf("%s\x03%.2d%s%s\x0F", pre, - color, sep, msg); - g_free(buf); + /* this branch doesn't need msg */ + g_free(msg); } } else { /* HTML encoding */ @@ -1356,8 +1394,8 @@ va_end(va); if (u) { - /* display as a notice from this particular user */ - irc_usernotice(u, "%s", msg); + /* just show this as a regular message */ + irc_usermsg(u, "<<\002OTR\002>> %s", msg); } else { irc_rootmsg(irc, "[otr] %s", msg); } @@ -1694,6 +1732,9 @@ } *p = '\0'; + /* remove trailing whitespace */ + g_strchomp(prefix); + /* find first key which matches the given prefix */ n = strlen(prefix); for (k = irc->otr->us->privkey_root; k; k = k->next) { diff -Nru bitlbee-3.4.1/protocols/account.c bitlbee-3.4.2/protocols/account.c --- bitlbee-3.4.1/protocols/account.c 2015-06-16 22:30:40.000000000 +0000 +++ bitlbee-3.4.2/protocols/account.c 2016-03-19 23:37:33.000000000 +0000 @@ -87,8 +87,6 @@ if (strstr(a->user, "@gmail.com") || strstr(a->user, "@googlemail.com")) { strcpy(tag, "gtalk"); - } else if (strstr(a->user, "@chat.facebook.com")) { - strcpy(tag, "fb"); } } diff -Nru bitlbee-3.4.1/protocols/bee_chat.c bitlbee-3.4.2/protocols/bee_chat.c --- bitlbee-3.4.1/protocols/bee_chat.c 2015-06-16 22:30:40.000000000 +0000 +++ bitlbee-3.4.2/protocols/bee_chat.c 2016-03-19 23:37:33.000000000 +0000 @@ -94,16 +94,15 @@ (ic->acc->prpl->handle_cmp(ic->acc->user, handle) == 0); } -void imcb_chat_msg(struct groupchat *c, const char *who, char *msg, uint32_t flags, time_t sent_at) +void imcb_chat_msg(struct groupchat *c, const char *who, char *msg, guint32 flags, time_t sent_at) { struct im_connection *ic = c->ic; bee_t *bee = ic->bee; bee_user_t *bu; - gboolean temp; + gboolean temp = FALSE; char *s; - /* Gaim sends own messages through this too. IRC doesn't want this, so kill them */ - if (handle_is_self(ic, who)) { + if (handle_is_self(ic, who) && !(flags & OPT_SELFMESSAGE)) { return; } @@ -121,7 +120,7 @@ } if (bee->ui->chat_msg) { - bee->ui->chat_msg(bee, c, bu, msg, sent_at); + bee->ui->chat_msg(bee, c, bu, msg, flags, sent_at); } if (temp) { @@ -229,7 +228,7 @@ } if (bee->ui->chat_remove_user && bu) { - bee->ui->chat_remove_user(bee, c, bu); + bee->ui->chat_remove_user(bee, c, bu, reason); } } diff -Nru bitlbee-3.4.1/protocols/bee.h bitlbee-3.4.2/protocols/bee.h --- bitlbee-3.4.1/protocols/bee.h 2015-06-16 22:30:40.000000000 +0000 +++ bitlbee-3.4.2/protocols/bee.h 2016-03-19 23:37:33.000000000 +0000 @@ -61,6 +61,7 @@ BEE_USER_MOBILE = 8, /* Compatibility with old OPT_MOBILE flag */ BEE_USER_LOCAL = 256, /* Locally-added contacts (not in real contact list) */ BEE_USER_SPECIAL = 512, /* Denotes a user as being special */ + BEE_USER_NOOTR = 4096, /* Per-user version of OPT_NOOTR */ } bee_user_flags_t; typedef struct bee_user { @@ -103,7 +104,7 @@ /* State info is already updated, old is provided in case the UI needs a diff. */ gboolean (*user_status)(bee_t *bee, struct bee_user *bu, struct bee_user *old); /* On every incoming message. sent_at = 0 means unknown. */ - gboolean (*user_msg)(bee_t *bee, bee_user_t *bu, const char *msg, time_t sent_at); + gboolean (*user_msg)(bee_t *bee, bee_user_t *bu, const char *msg, guint32 flags, time_t sent_at); /* Flags currently defined (OPT_TYPING/THINKING) in nogaim.h. */ gboolean (*user_typing)(bee_t *bee, bee_user_t *bu, guint32 flags); /* CTCP-like stuff (buddy action) response */ @@ -116,9 +117,9 @@ gboolean (*chat_free)(bee_t *bee, struct groupchat *c); /* System messages of any kind. */ gboolean (*chat_log)(bee_t *bee, struct groupchat *c, const char *text); - gboolean (*chat_msg)(bee_t *bee, struct groupchat *c, bee_user_t *bu, const char *msg, time_t sent_at); + gboolean (*chat_msg)(bee_t *bee, struct groupchat *c, bee_user_t *bu, const char *msg, guint32 flags, time_t sent_at); gboolean (*chat_add_user)(bee_t *bee, struct groupchat *c, bee_user_t *bu); - gboolean (*chat_remove_user)(bee_t *bee, struct groupchat *c, bee_user_t *bu); + gboolean (*chat_remove_user)(bee_t *bee, struct groupchat *c, bee_user_t *bu, const char *reason); gboolean (*chat_topic)(bee_t *bee, struct groupchat *c, const char *new_topic, bee_user_t *bu); gboolean (*chat_name_hint)(bee_t *bee, struct groupchat *c, const char *name); gboolean (*chat_invite)(bee_t *bee, bee_user_t *bu, const char *name, const char *msg); @@ -127,6 +128,8 @@ gboolean (*ft_out_start)(struct im_connection *ic, struct file_transfer *ft); void (*ft_close)(struct im_connection *ic, struct file_transfer *ft); void (*ft_finished)(struct im_connection *ic, struct file_transfer *ft); + + void (*log)(bee_t *bee, const char *tag, const char *msg); } bee_ui_funcs_t; diff -Nru bitlbee-3.4.1/protocols/bee_user.c bitlbee-3.4.2/protocols/bee_user.c --- bitlbee-3.4.1/protocols/bee_user.c 2015-06-16 22:30:40.000000000 +0000 +++ bitlbee-3.4.2/protocols/bee_user.c 2016-03-19 23:37:33.000000000 +0000 @@ -246,7 +246,7 @@ bu->idle_time = idle; } -void imcb_buddy_msg(struct im_connection *ic, const char *handle, const char *msg, uint32_t flags, time_t sent_at) +void imcb_buddy_msg(struct im_connection *ic, const char *handle, const char *msg, guint32 flags, time_t sent_at) { bee_t *bee = ic->bee; bee_user_t *bu; @@ -264,7 +264,7 @@ } if (bee->ui->user_msg && bu) { - bee->ui->user_msg(bee, bu, msg, sent_at); + bee->ui->user_msg(bee, bu, msg, flags, sent_at); } else { imcb_log(ic, "Message from unknown handle %s:\n%s", handle, msg); } @@ -296,7 +296,7 @@ g_free(msg); } -void imcb_buddy_typing(struct im_connection *ic, const char *handle, uint32_t flags) +void imcb_buddy_typing(struct im_connection *ic, const char *handle, guint32 flags) { bee_user_t *bu; diff -Nru bitlbee-3.4.1/protocols/ft.h bitlbee-3.4.2/protocols/ft.h --- bitlbee-3.4.1/protocols/ft.h 2015-06-16 22:30:40.000000000 +0000 +++ bitlbee-3.4.2/protocols/ft.h 2016-03-19 23:37:33.000000000 +0000 @@ -57,15 +57,15 @@ * | * | accept * V - * /------ /-------------\ /------------------------\ - * out_of_data | | TRANSFERING | -----------------> | TRANSFERING | CANCELED | - * \-----> \-------------/ [canceled,]free \------------------------/ + * /------ /-------------\ /--------------------------\ + * out_of_data | | TRANSFERRING | -----------------> | TRANSFERRING | CANCELED | + * \-----> \-------------/ [canceled,]free \--------------------------/ * | * | finished,free * V - * /------------------------\ - * | TRANSFERING | FINISHED | - * \------------------------/ + * /-------------------------\ + * | TRANSFERRING | FINISHED | + * \-------------------------/ */ typedef struct file_transfer { @@ -114,7 +114,7 @@ gpointer priv; /* - * If set, called after succesful connection setup. + * If set, called after successful connection setup. */ void (*accept)(struct file_transfer *file); diff -Nru bitlbee-3.4.1/protocols/jabber/conference.c bitlbee-3.4.2/protocols/jabber/conference.c --- bitlbee-3.4.1/protocols/jabber/conference.c 2015-06-16 22:30:40.000000000 +0000 +++ bitlbee-3.4.2/protocols/jabber/conference.c 2016-03-19 23:37:33.000000000 +0000 @@ -25,6 +25,7 @@ #include "sha1.h" static xt_status jabber_chat_join_failed(struct im_connection *ic, struct xt_node *node, struct xt_node *orig); +static xt_status jabber_chat_self_message(struct im_connection *ic, struct xt_node *node, struct xt_node *orig); struct groupchat *jabber_chat_join(struct im_connection *ic, const char *room, const char *nick, const char *password) { @@ -120,12 +121,21 @@ jabber_error_free(err); } if (bud) { - jabber_chat_free(jabber_chat_by_jid(ic, bud->bare_jid)); + struct groupchat *c = jabber_chat_by_jid(ic, bud->bare_jid); + if (c) { + jabber_chat_free(c); + } } return XT_HANDLED; } +static xt_status jabber_chat_self_message(struct im_connection *ic, struct xt_node *node, struct xt_node *orig) +{ + /* This is a self message sent by this bitlbee - just drop it */ + return XT_ABORT; +} + struct groupchat *jabber_chat_by_jid(struct im_connection *ic, const char *name) { char *normalized = jabber_normalize(name); @@ -170,13 +180,9 @@ node = xt_new_node("body", message, NULL); node = jabber_make_packet("message", "groupchat", jc->name, node); - if (!jabber_write_packet(ic, node)) { - xt_free_node(node); - return 0; - } - xt_free_node(node); + jabber_cache_add(ic, node, jabber_chat_self_message); - return 1; + return !jabber_write_packet(ic, node); } int jabber_chat_topic(struct groupchat *c, char *topic) @@ -298,9 +304,6 @@ bud->ext_jid[i] = '_'; } } - - /* Some program-specific restrictions. */ - imcb_clean_handle(ic, bud->ext_jid); } bud->flags |= JBFLAG_IS_ANONYMOUS; } @@ -330,17 +333,60 @@ } } else if (type) { /* type can only be NULL or "unavailable" in this function */ if ((bud->flags & JBFLAG_IS_CHATROOM) && bud->ext_jid) { + char *reason = NULL; + char *status = NULL; + char *status_text = NULL; + + if ((c = xt_find_node_by_attr(node->children, "x", "xmlns", XMLNS_MUC_USER))) { + struct xt_node *c2 = c->children; + + while ((c2 = xt_find_node(c2, "status"))) { + char *code = xt_find_attr(c2, "code"); + if (g_strcmp0(code, "301") == 0) { + status = "Banned"; + break; + } else if (g_strcmp0(code, "303") == 0) { + /* This could be handled in a cleverer way, + * but let's just show a literal part/join for now */ + status = "Changing nicks"; + break; + } else if (g_strcmp0(code, "307") == 0) { + status = "Kicked"; + break; + } + c2 = c2->next; + } + + /* Sometimes the status message is in presence/x/item/reason */ + if ((c2 = xt_find_path(c, "item/reason")) && c2->text && c2->text_len) { + status_text = c2->text; + } + } + + /* Sometimes the status message is right inside */ + if ((c = xt_find_node(node->children, "status")) && c->text && c->text_len) { + status_text = c->text; + } + + if (status_text && status) { + reason = g_strdup_printf("%s: %s", status, status_text); + } else { + reason = g_strdup(status_text ? : status); + } + s = strchr(bud->ext_jid, '/'); if (s) { *s = 0; } - imcb_chat_remove_buddy(chat, bud->ext_jid, NULL); + imcb_chat_remove_buddy(chat, bud->ext_jid, reason); if (bud != jc->me && bud->flags & JBFLAG_IS_ANONYMOUS) { - imcb_remove_buddy(ic, bud->ext_jid, NULL); + imcb_remove_buddy(ic, bud->ext_jid, reason); } if (s) { *s = '/'; } + + g_free(reason); } if (bud == jc->me) { @@ -359,6 +405,7 @@ char *nick = NULL; char *final_from = NULL; char *bare_jid = NULL; + guint32 flags = 0; from = (bud) ? bud->full_jid : xt_find_attr(node, "from"); @@ -395,12 +442,13 @@ } if (subject && chat) { - char *subject_text = subject->text_len > 0 ? subject->text : NULL; + char *subject_text = subject->text_len > 0 ? subject->text : ""; if (g_strcmp0(chat->topic, subject_text) != 0) { bare_jid = (bud) ? jabber_get_bare_jid(bud->ext_jid) : NULL; imcb_chat_topic(chat, bare_jid, subject_text, jabber_get_timestamp(node)); g_free(bare_jid); + bare_jid = NULL; } } @@ -421,20 +469,23 @@ } else if (chat != NULL && bud == NULL && nick == NULL) { imcb_chat_log(chat, "From conference server: %s", body->text); return; - } else if (jc && jc->flags & JCFLAG_MESSAGE_SENT && bud == jc->me) { - /* exclude self-messages since they would get filtered out - * but not the ones in the backlog */ + } else if (jc && jc->flags & JCFLAG_MESSAGE_SENT && bud == jc->me && + (jabber_cache_handle_packet(ic, node) == XT_ABORT)) { + /* Self message marked by this bitlbee, don't show it */ return; } - if (bud && jc && bud != jc->me) { + if (bud) { bare_jid = jabber_get_bare_jid(bud->ext_jid ? bud->ext_jid : bud->full_jid); final_from = bare_jid; + if (bud == jc->me || (g_strcasecmp(final_from, ic->acc->user) == 0)) { + flags = OPT_SELFMESSAGE; + } } else { final_from = nick; } - imcb_chat_msg(chat, final_from, body->text, 0, jabber_get_timestamp(node)); + imcb_chat_msg(chat, final_from, body->text, flags, jabber_get_timestamp(node)); g_free(bare_jid); } diff -Nru bitlbee-3.4.1/protocols/jabber/hipchat.c bitlbee-3.4.2/protocols/jabber/hipchat.c --- bitlbee-3.4.1/protocols/jabber/hipchat.c 2015-06-16 22:30:40.000000000 +0000 +++ bitlbee-3.4.2/protocols/jabber/hipchat.c 2016-03-19 23:37:33.000000000 +0000 @@ -42,6 +42,8 @@ *sep = '/'; } + jd->muc_host = g_strdup(xt_find_attr(node, "muc_host")); + /* Hipchat's auth doesn't expect a restart here */ jd->flags &= ~JFLAG_STREAM_RESTART; @@ -91,3 +93,51 @@ return XT_HANDLED; } + +/* Returns a newly allocated string that tries to match the "slug" part of the JID using an + * approximation of the method used by the server. This might fail in some rare conditions + * (old JIDs generated a different way, locale settings unicode, etc) */ +char *hipchat_make_channel_slug(const char *name) +{ + char *lower; + char *new = g_malloc(strlen(name) + 1); + int i = 0; + + do { + if (*name == ' ') { + new[i++] = '_'; + } else if (*name && !strchr("\"&'/:<>@", *name)) { + new[i++] = *name; + } + } while (*(name++)); + + new[i] = '\0'; + + lower = g_utf8_strdown(new, -1); + g_free(new); + + return lower; +} + +char *hipchat_guess_channel_name(struct im_connection *ic, const char *name) +{ + struct jabber_data *jd = ic->proto_data; + char *slug, *retval, *underscore; + + if (!(underscore = strchr(jd->username, '_')) || !jd->muc_host) { + return NULL; + } + + slug = hipchat_make_channel_slug(name); + + /* Get the organization ID from the username, before the underscore */ + *underscore = '\0'; + + retval = g_strdup_printf("%s_%s@%s", jd->username, slug, jd->muc_host); + + *underscore = '_'; + + g_free(slug); + + return retval; +} diff -Nru bitlbee-3.4.1/protocols/jabber/io.c bitlbee-3.4.2/protocols/jabber/io.c --- bitlbee-3.4.1/protocols/jabber/io.c 2015-06-16 22:30:40.000000000 +0000 +++ bitlbee-3.4.2/protocols/jabber/io.c 2016-03-19 23:37:33.000000000 +0000 @@ -146,6 +146,76 @@ } } +static gboolean jabber_feed_input(struct im_connection *ic, char *buf, int size) +{ + struct jabber_data *jd = ic->proto_data; + + /* Allow not passing a size for debugging purposes. + * This never happens when reading from the socket */ + if (size == -1) { + size = strlen(buf); + } + + /* Parse. */ + if (xt_feed(jd->xt, buf, size) < 0) { + imcb_error(ic, "XML stream error"); + imc_logout(ic, TRUE); + return FALSE; + } + + /* Execute all handlers. */ + if (!xt_handle(jd->xt, NULL, 1)) { + /* Don't do anything, the handlers should have + aborted the connection already. */ + return FALSE; + } + + if (jd->flags & JFLAG_STREAM_RESTART) { + jd->flags &= ~JFLAG_STREAM_RESTART; + jabber_start_stream(ic); + } + + /* Garbage collection. */ + xt_cleanup(jd->xt, NULL, 1); + + /* This is a bit hackish, unfortunately. Although xmltree + has nifty event handler stuff, it only calls handlers + when nodes are complete. Since the server should only + send an opening tag, we have to check + this by hand. :-( */ + if (!(jd->flags & JFLAG_STREAM_STARTED) && jd->xt && jd->xt->root) { + if (g_strcasecmp(jd->xt->root->name, "stream:stream") == 0) { + jd->flags |= JFLAG_STREAM_STARTED; + + /* If there's no version attribute, assume + this is an old server that can't do SASL + authentication. */ + if (!set_getbool(&ic->acc->set, "sasl") || !sasl_supported(ic)) { + /* If there's no version= tag, we suppose + this server does NOT implement: XMPP 1.0, + SASL and TLS. */ + if (set_getbool(&ic->acc->set, "tls")) { + imcb_error(ic, "TLS is turned on for this " + "account, but is not supported by this server"); + imc_logout(ic, FALSE); + return FALSE; + } else { + if (!jabber_init_iq_auth(ic)) { + return FALSE; + } + } + } + } else { + imcb_error(ic, "XML stream error"); + imc_logout(ic, TRUE); + return FALSE; + } + } + + return TRUE; +} + + static gboolean jabber_read_callback(gpointer data, gint fd, b_input_condition cond) { struct im_connection *ic = data; @@ -164,59 +234,9 @@ } if (st > 0) { - /* Parse. */ - if (xt_feed(jd->xt, buf, st) < 0) { - imcb_error(ic, "XML stream error"); - imc_logout(ic, TRUE); - return FALSE; - } - - /* Execute all handlers. */ - if (!xt_handle(jd->xt, NULL, 1)) { - /* Don't do anything, the handlers should have - aborted the connection already. */ + if (!jabber_feed_input(ic, buf, st)) { return FALSE; } - - if (jd->flags & JFLAG_STREAM_RESTART) { - jd->flags &= ~JFLAG_STREAM_RESTART; - jabber_start_stream(ic); - } - - /* Garbage collection. */ - xt_cleanup(jd->xt, NULL, 1); - - /* This is a bit hackish, unfortunately. Although xmltree - has nifty event handler stuff, it only calls handlers - when nodes are complete. Since the server should only - send an opening tag, we have to check - this by hand. :-( */ - if (!(jd->flags & JFLAG_STREAM_STARTED) && jd->xt && jd->xt->root) { - if (g_strcasecmp(jd->xt->root->name, "stream:stream") == 0) { - jd->flags |= JFLAG_STREAM_STARTED; - - /* If there's no version attribute, assume - this is an old server that can't do SASL - authentication. */ - if (!set_getbool(&ic->acc->set, "sasl") || !sasl_supported(ic)) { - /* If there's no version= tag, we suppose - this server does NOT implement: XMPP 1.0, - SASL and TLS. */ - if (set_getbool(&ic->acc->set, "tls")) { - imcb_error(ic, "TLS is turned on for this " - "account, but is not supported by this server"); - imc_logout(ic, FALSE); - return FALSE; - } else { - return jabber_init_iq_auth(ic); - } - } - } else { - imcb_error(ic, "XML stream error"); - imc_logout(ic, TRUE); - return FALSE; - } - } } else if (st == 0 || (st < 0 && !ssl_sockerr_again(jd->ssl))) { closesocket(jd->fd); jd->fd = -1; diff -Nru bitlbee-3.4.1/protocols/jabber/iq.c bitlbee-3.4.2/protocols/jabber/iq.c --- bitlbee-3.4.1/protocols/jabber/iq.c 2015-06-16 22:30:40.000000000 +0000 +++ bitlbee-3.4.2/protocols/jabber/iq.c 2016-03-19 23:37:33.000000000 +0000 @@ -27,6 +27,7 @@ static xt_status jabber_parse_roster(struct im_connection *ic, struct xt_node *node, struct xt_node *orig); static xt_status jabber_iq_display_vcard(struct im_connection *ic, struct xt_node *node, struct xt_node *orig); static xt_status jabber_gmail_handle_new(struct im_connection *ic, struct xt_node *node); +static xt_status jabber_iq_carbons_response(struct im_connection *ic, struct xt_node *node, struct xt_node *orig); xt_status jabber_pkt_iq(struct xt_node *node, gpointer data) { @@ -51,9 +52,11 @@ (c = xt_find_node(node->children, "ping")) || (c = xt_find_node(node->children, "time"))) || !(s = xt_find_attr(c, "xmlns"))) { - /* Sigh. Who decided to suddenly invent new elements - instead of just sticking with ? */ - return XT_HANDLED; + + reply = jabber_make_error_packet(node, "service-unavailable", "cancel", NULL); + st = jabber_write_packet(ic, reply); + xt_free_node(reply); + return st; } reply = xt_new_node("query", NULL, NULL); @@ -117,6 +120,7 @@ XMLNS_SI, XMLNS_BYTESTREAMS, XMLNS_FILETRANSFER, + XMLNS_CARBONS, NULL }; const char **f; @@ -133,7 +137,7 @@ } } else { xt_free_node(reply); - reply = jabber_make_error_packet(node, "feature-not-implemented", "cancel", NULL); + reply = jabber_make_error_packet(node, "service-unavailable", "cancel", NULL); pack = 0; } } else if (strcmp(type, "set") == 0) { @@ -229,7 +233,7 @@ if (!(query = xt_find_node(node->children, "query"))) { imcb_log(ic, "Warning: Received incomplete IQ packet while authenticating"); imc_logout(ic, FALSE); - return XT_HANDLED; + return XT_ABORT; } /* Time to authenticate ourselves! */ @@ -282,7 +286,7 @@ if (!(type = xt_find_attr(node, "type"))) { imcb_log(ic, "Warning: Received incomplete IQ packet while authenticating"); imc_logout(ic, FALSE); - return XT_HANDLED; + return XT_ABORT; } if (strcmp(type, "error") == 0) { @@ -384,8 +388,8 @@ { struct xt_node *response; struct jabber_data *jd = ic->proto_data; - - response = jabber_make_packet("iq", "result", g_strdup_printf("%s@%s", jd->username, jd->server), NULL); + + response = jabber_make_packet("iq", "result", jd->me, NULL); jabber_cache_add(ic, response, NULL); if (!jabber_write_packet(ic, response)) { @@ -1003,9 +1007,26 @@ struct xt_node *node, struct xt_node *orig) { struct jabber_data *jd = ic->proto_data; - struct xt_node *id; + struct xt_node *query, *id; + + if (!(query = xt_find_node(node->children, "query"))) { + return XT_HANDLED; + } + + if (xt_find_node_by_attr(query->children, "feature", "var", XMLNS_CARBONS) && + set_getbool(&ic->acc->set, "carbons")) { - if ((id = xt_find_path(node, "query/identity"))) { + struct xt_node *enable, *iq; + + enable = xt_new_node("enable", NULL, NULL); + xt_add_attr(enable, "xmlns", XMLNS_CARBONS); + iq = jabber_make_packet("iq", "set", NULL, enable); + + jabber_cache_add(ic, iq, jabber_iq_carbons_response); + jabber_write_packet(ic, iq); + } + + if ((id = xt_find_node(query->children, "identity"))) { char *cat, *type, *name; if (!(cat = xt_find_attr(id, "category")) || @@ -1021,4 +1042,20 @@ } return XT_HANDLED; +} + +static xt_status jabber_iq_carbons_response(struct im_connection *ic, + struct xt_node *node, struct xt_node *orig) +{ + struct jabber_error *err; + + if ((err = jabber_error_parse(xt_find_node(node->children, "error"), XMLNS_STANZA_ERROR))) { + imcb_error(ic, "Error enabling carbons: %s%s%s", + err->code, err->text ? ": " : "", err->text ? err->text : ""); + jabber_error_free(err); + } else { + imcb_log(ic, "Carbons enabled"); + } + + return XT_HANDLED; } diff -Nru bitlbee-3.4.1/protocols/jabber/jabber.c bitlbee-3.4.2/protocols/jabber/jabber.c --- bitlbee-3.4.1/protocols/jabber/jabber.c 2015-06-16 22:30:40.000000000 +0000 +++ bitlbee-3.4.2/protocols/jabber/jabber.c 2016-03-19 23:37:33.000000000 +0000 @@ -84,7 +84,10 @@ if (strcmp(acc->prpl->name, "hipchat") == 0) { set_setstr(&acc->set, "server", "chat.hipchat.com"); } else { - s = set_add(&acc->set, "oauth", "false", set_eval_oauth, acc); + set_add(&acc->set, "oauth", "false", set_eval_oauth, acc); + + /* this reuses set_eval_oauth, which clears the password */ + set_add(&acc->set, "anonymous", "false", set_eval_oauth, acc); } s = set_add(&acc->set, "ssl", "false", set_eval_bool, acc); @@ -110,6 +113,9 @@ s = set_add(&acc->set, "mail_notifications_handle", NULL, NULL, acc); s->flags |= ACC_SET_OFFLINE_ONLY | SET_NULL_OK; + s = set_add(&acc->set, "carbons", "true", set_eval_bool, acc); + s->flags |= ACC_SET_OFFLINE_ONLY; + acc->flags |= ACC_FLAG_AWAY_MESSAGE | ACC_FLAG_STATUS_MESSAGE | ACC_FLAG_HANDLE_DOMAINS; } @@ -144,6 +150,12 @@ return; } + if (strstr(jd->server, ".facebook.com")) { + imcb_error(ic, "Facebook's XMPP service is gone. Try this instead: https://wiki.bitlbee.org/HowtoFacebookMQTT"); + imc_logout(ic, FALSE); + return; + } + if ((s = strchr(jd->server, '/'))) { *s = 0; set_setstr(&acc->set, "resource", s + 1); @@ -163,11 +175,9 @@ jd->fd = jd->r_inpa = jd->w_inpa = -1; - if (strstr(jd->server, ".facebook.com")) { - jd->oauth2_service = &oauth2_service_facebook; - } else { - jd->oauth2_service = &oauth2_service_google; - } + /* There are no other options atm, so assume google for everything + Facebook and MSN XMPP used to be here. RIP. */ + jd->oauth2_service = &oauth2_service_google; oauth_params_parse(&p_in, ic->acc->pass); @@ -196,6 +206,20 @@ } } +static void jabber_xmlconsole_enable(struct im_connection *ic) +{ + struct jabber_data *jd = ic->proto_data; + const char *handle = JABBER_XMLCONSOLE_HANDLE; + bee_user_t *bu; + + jd->flags |= JFLAG_XMLCONSOLE; + + if (!(bu = bee_user_by_handle(ic->bee, ic, handle))) { + bu = bee_user_new(ic->bee, ic, handle, 0); + bu->flags |= BEE_USER_NOOTR; + } +} + /* Separate this from jabber_login() so we can do OAuth first if necessary. Putting this in io.c would probably be more correct. */ void jabber_connect(struct im_connection *ic) @@ -262,10 +286,7 @@ } if (set_getbool(&acc->set, "xmlconsole")) { - jd->flags |= JFLAG_XMLCONSOLE; - /* Shouldn't really do this at this stage already, maybe. But - I think this shouldn't break anything. */ - imcb_add_buddy(ic, JABBER_XMLCONSOLE_HANDLE, NULL); + jabber_xmlconsole_enable(ic); } if (set_getbool(&acc->set, "mail_notifications")) { @@ -330,7 +351,7 @@ ssl_disconnect(jd->ssl); } if (jd->fd >= 0) { - closesocket(jd->fd); + proxy_disconnect(jd->fd); } if (jd->tx_len) { @@ -341,7 +362,9 @@ g_hash_table_destroy(jd->node_cache); } - jabber_buddy_remove_all(ic); + if (jd->buddies) { + jabber_buddy_remove_all(ic); + } xt_free(jd->xt); @@ -351,6 +374,7 @@ g_free(jd->away_message); g_free(jd->internal_jid); g_free(jd->gmail_tid); + g_free(jd->muc_host); g_free(jd->username); g_free(jd->me); g_free(jd); @@ -409,6 +433,27 @@ bud->flags |= JBFLAG_PROBED_XEP85; } + /* XEP-0364 suggests we add message processing hints (XEP-0334) to OTR messages, + mostly to avoid carbons (XEP-0280) and server-side message archiving. + OTR messages are roughly like this: /^\?OTR(.*\?| Error:|:)/ + But I'm going to simplify it to messages starting with "?OTR". */ + if (g_str_has_prefix(message, "?OTR")) { + int i; + char *hints[] = { + "no-copy", XMLNS_HINTS, + "no-permanent-store", XMLNS_HINTS, + "private", XMLNS_CARBONS, + NULL + }; + + for (i = 0; hints[i]; i += 2) { + struct xt_node *hint; + hint = xt_new_node(hints[i], NULL, NULL); + xt_add_attr(hint, "xmlns", hints[i + 1]); + xt_add_child(node, hint); + } + } + st = jabber_write_packet(ic, node); xt_free_node(node); @@ -468,11 +513,8 @@ static void jabber_add_buddy(struct im_connection *ic, char *who, char *group) { - struct jabber_data *jd = ic->proto_data; - if (g_strcasecmp(who, JABBER_XMLCONSOLE_HANDLE) == 0) { - jd->flags |= JFLAG_XMLCONSOLE; - imcb_add_buddy(ic, JABBER_XMLCONSOLE_HANDLE, NULL); + jabber_xmlconsole_enable(ic); return; } @@ -516,12 +558,24 @@ final_nick = (char *) nick; } + if (jd->flags & JFLAG_HIPCHAT && jd->muc_host && !g_str_has_suffix(room, jd->muc_host)) { + char *guessed_name = hipchat_guess_channel_name(ic, room); + if (guessed_name) { + set_setstr(sets, "room", guessed_name); + g_free(guessed_name); + + /* call this same function again with the fixed name */ + return jabber_chat_join_(ic, set_getstr(sets, "room"), nick, password, sets); + } + } + if (strchr(room, '@') == NULL) { imcb_error(ic, "%s is not a valid Jabber room name. Maybe you mean %s@conference.%s?", room, room, jd->server); } else if (jabber_chat_by_jid(ic, room)) { imcb_error(ic, "Already present in chat `%s'", room); } else { + /* jabber_chat_join without the underscore is the conference.c one */ return jabber_chat_join(ic, room, final_nick, set_getstr(sets, "password")); } @@ -586,20 +640,23 @@ static int jabber_send_typing(struct im_connection *ic, char *who, int typing) { struct jabber_data *jd = ic->proto_data; - struct jabber_buddy *bud; + struct jabber_buddy *bud, *bare; /* Enable typing notification related code from now. */ jd->flags |= JFLAG_WANT_TYPING; - if ((bud = jabber_buddy_by_jid(ic, who, 0)) == NULL) { + if ((bud = jabber_buddy_by_jid(ic, who, 0)) == NULL || + (bare = jabber_buddy_by_jid(ic, who, GET_BUDDY_BARE)) == NULL) { /* Sending typing notifications to unknown buddies is unsupported for now. Shouldn't be a problem, I think. */ return 0; } - if (bud->flags & JBFLAG_DOES_XEP85) { + + if (bud->flags & JBFLAG_DOES_XEP85 || bare->flags & JBFLAG_DOES_XEP85) { /* We're only allowed to send this stuff if we know the other - side supports it. */ + side supports it. If the bare JID has the flag, all other + resources get it, too (That is the case in gtalk) */ struct xt_node *node; char *type; diff -Nru bitlbee-3.4.1/protocols/jabber/jabber.h bitlbee-3.4.2/protocols/jabber/jabber.h --- bitlbee-3.4.1/protocols/jabber/jabber.h 2015-06-16 22:30:40.000000000 +0000 +++ bitlbee-3.4.2/protocols/jabber/jabber.h 2016-03-19 23:37:33.000000000 +0000 @@ -49,8 +49,6 @@ JFLAG_GTALK = 0x100000, /* Is Google Talk, as confirmed by iq discovery */ JFLAG_HIPCHAT = 0x200000, /* Is hipchat, because prpl->name says so */ - - JFLAG_SASL_FB = 0x10000, /* Trying Facebook authentication. */ } jabber_flags_t; typedef enum { @@ -112,6 +110,8 @@ GSList *filetransfers; GSList *streamhosts; int have_streamhosts; + + char *muc_host; }; struct jabber_away_state { @@ -227,6 +227,9 @@ #define XMLNS_DELAY "urn:xmpp:delay" /* XEP-0203 */ #define XMLNS_XDATA "jabber:x:data" /* XEP-0004 */ #define XMLNS_GMAILNOTIFY "google:mail:notify" /* Not a XEP */ +#define XMLNS_CARBONS "urn:xmpp:carbons:2" /* XEP-0280 */ +#define XMLNS_FORWARDING "urn:xmpp:forward:0" /* XEP-0297 */ +#define XMLNS_HINTS "urn:xmpp:hints" /* XEP-0334 */ #define XMLNS_CHATSTATES "http://jabber.org/protocol/chatstates" /* XEP-0085 */ #define XMLNS_DISCO_INFO "http://jabber.org/protocol/disco#info" /* XEP-0030 */ #define XMLNS_DISCO_ITEMS "http://jabber.org/protocol/disco#items" /* XEP-0030 */ @@ -337,7 +340,6 @@ int sasl_oauth2_refresh(struct im_connection *ic, const char *refresh_token); extern const struct oauth2_service oauth2_service_google; -extern const struct oauth2_service oauth2_service_facebook; /* conference.c */ struct groupchat *jabber_chat_join(struct im_connection *ic, const char *room, const char *nick, const char *password); @@ -355,5 +357,7 @@ int jabber_get_hipchat_profile(struct im_connection *ic); xt_status jabber_parse_hipchat_profile(struct im_connection *ic, struct xt_node *node, struct xt_node *orig); xt_status hipchat_handle_success(struct im_connection *ic, struct xt_node *node); +char *hipchat_make_channel_slug(const char *name); +char *hipchat_guess_channel_name(struct im_connection *ic, const char *name); #endif diff -Nru bitlbee-3.4.1/protocols/jabber/jabber_util.c bitlbee-3.4.2/protocols/jabber/jabber_util.c --- bitlbee-3.4.1/protocols/jabber/jabber_util.c 2015-06-16 22:30:40.000000000 +0000 +++ bitlbee-3.4.2/protocols/jabber/jabber_util.c 2016-03-19 23:37:33.000000000 +0000 @@ -513,7 +513,11 @@ jabber_buddy_add(ic, jid_) : NULL; } else if (bud->resource && (flags & GET_BUDDY_EXACT)) { /* We want an exact match, so in thise case there shouldn't be a /resource. */ - return NULL; + if (head != bud && head->resource == NULL) { + return head; + } else { + return NULL; + } } else if (bud->resource == NULL || bud->next == NULL) { /* No need for selection if there's only one option. */ return bud; diff -Nru bitlbee-3.4.1/protocols/jabber/message.c bitlbee-3.4.2/protocols/jabber/message.c --- bitlbee-3.4.1/protocols/jabber/message.c 2015-06-16 22:30:40.000000000 +0000 +++ bitlbee-3.4.2/protocols/jabber/message.c 2016-03-19 23:37:33.000000000 +0000 @@ -23,10 +23,10 @@ #include "jabber.h" -xt_status jabber_pkt_message(struct xt_node *node, gpointer data) +static xt_status jabber_pkt_message_normal(struct xt_node *node, gpointer data, gboolean carbons_sent) { struct im_connection *ic = data; - char *from = xt_find_attr(node, "from"); + char *from = xt_find_attr(node, carbons_sent ? "to" : "from"); char *type = xt_find_attr(node, "type"); char *id = xt_find_attr(node, "id"); struct xt_node *body = xt_find_node(node->children, "body"), *c; @@ -36,16 +36,17 @@ if (!from) { return XT_HANDLED; /* Consider this packet corrupted. */ - } - if (request && id) { + + if (request && id && g_strcmp0(type, "groupchat") != 0 && !carbons_sent) { /* Send a message receipt (XEP-0184), looking like this: - * + * * - * */ + * + * + * MUC messages are excluded, since receipts aren't supposed to be sent over MUCs + * (XEP-0184 section 5.3) and replying to those may result in 'forbidden' errors. + */ struct xt_node *received, *receipt; received = xt_new_node("received", NULL, NULL); @@ -126,7 +127,7 @@ if (fullmsg->len > 0) { imcb_buddy_msg(ic, from, fullmsg->str, - 0, jabber_get_timestamp(node)); + carbons_sent ? OPT_SELFMESSAGE : 0, jabber_get_timestamp(node)); } if (room) { imcb_chat_invite(ic, room, from, reason); @@ -135,16 +136,20 @@ g_string_free(fullmsg, TRUE); /* Handling of incoming typing notifications. */ - if (bud == NULL) { - /* Can't handle these for unknown buddies. */ + if (bud == NULL || carbons_sent) { + /* Can't handle these for unknown buddies. + And ignore them if it's just carbons */ } else if (xt_find_node(node->children, "composing")) { bud->flags |= JBFLAG_DOES_XEP85; imcb_buddy_typing(ic, from, OPT_TYPING); } - /* No need to send a "stopped typing" signal when there's a message. */ - else if (xt_find_node(node->children, "active") && (body == NULL)) { + else if (xt_find_node(node->children, "active")) { bud->flags |= JBFLAG_DOES_XEP85; - imcb_buddy_typing(ic, from, 0); + + /* No need to send a "stopped typing" signal when there's a message. */ + if (body == NULL) { + imcb_buddy_typing(ic, from, 0); + } } else if (xt_find_node(node->children, "paused")) { bud->flags |= JBFLAG_DOES_XEP85; imcb_buddy_typing(ic, from, OPT_THINKING); @@ -157,3 +162,44 @@ return XT_HANDLED; } + +static xt_status jabber_carbons_message(struct xt_node *node, gpointer data) +{ + struct im_connection *ic = data; + struct xt_node *wrap, *fwd, *msg; + gboolean carbons_sent; + + if ((wrap = xt_find_node(node->children, "received"))) { + carbons_sent = FALSE; + } else if ((wrap = xt_find_node(node->children, "sent"))) { + carbons_sent = TRUE; + } + + if (wrap == NULL || g_strcmp0(xt_find_attr(wrap, "xmlns"), XMLNS_CARBONS) != 0) { + return XT_NEXT; + } + + if (!(fwd = xt_find_node(wrap->children, "forwarded")) || + (g_strcmp0(xt_find_attr(fwd, "xmlns"), XMLNS_FORWARDING) != 0) || + !(msg = xt_find_node(fwd->children, "message"))) { + imcb_log(ic, "Error: Invalid carbons message received"); + return XT_ABORT; + } + + return jabber_pkt_message_normal(msg, data, carbons_sent); +} + +xt_status jabber_pkt_message(struct xt_node *node, gpointer data) +{ + struct im_connection *ic = data; + struct jabber_data *jd = ic->proto_data; + char *from = xt_find_attr(node, "from"); + + if (jabber_compare_jid(jd->me, from)) { /* Probably a Carbons message */ + xt_status st = jabber_carbons_message(node, data); + if (st == XT_HANDLED || st == XT_ABORT) { + return st; + } + } + return jabber_pkt_message_normal(node, data, FALSE); +} diff -Nru bitlbee-3.4.1/protocols/jabber/presence.c bitlbee-3.4.2/protocols/jabber/presence.c --- bitlbee-3.4.1/protocols/jabber/presence.c 2015-06-16 22:30:40.000000000 +0000 +++ bitlbee-3.4.2/protocols/jabber/presence.c 2016-03-19 23:37:33.000000000 +0000 @@ -185,7 +185,7 @@ struct jabber_data *jd = ic->proto_data; char *prio = set_getstr(&ic->acc->set, "priority"); - if (jd->away_state->code != NULL) { + if (jd->away_state && jd->away_state->full_name != NULL) { int new_prio = (atoi(prio) - 5); if (new_prio < 0) { new_prio = 0; diff -Nru bitlbee-3.4.1/protocols/jabber/s5bytestream.c bitlbee-3.4.2/protocols/jabber/s5bytestream.c --- bitlbee-3.4.1/protocols/jabber/s5bytestream.c 2015-06-16 22:30:40.000000000 +0000 +++ bitlbee-3.4.2/protocols/jabber/s5bytestream.c 2016-03-19 23:37:33.000000000 +0000 @@ -84,7 +84,7 @@ if ((op) == -1) { \ return jabber_bs_abort(bt, msg ": %s", strerror(errno)); } -gboolean jabber_bs_abort(struct bs_transfer *bt, char *format, ...); +gboolean jabber_bs_abort(struct bs_transfer *bt, char *format, ...) G_GNUC_PRINTF(2, 3); void jabber_bs_canceled(file_transfer_t *ft, char *reason); void jabber_bs_free_transfer(file_transfer_t *ft); gboolean jabber_bs_connect_timeout(gpointer data, gint fd, b_input_condition cond); @@ -535,7 +535,7 @@ /* usually a proxy sends back the 40 bytes address but I encountered at least one (of jabber.cz) * that sends atyp=0 addrlen=0 and only 6 bytes (one less than one would expect). * Therefore I removed the wait for more bytes. Since we don't care about what else the proxy - * is sending, it shouldnt matter */ + * is sending, it should not matter */ if (bt->tf->ft->sending) { jabber_bs_send_activate(bt); @@ -558,7 +558,7 @@ * If the handshake failed we can try the next streamhost, if there is one. * An intelligent sender would probably specify himself as the first streamhost and * a proxy as the second (Kopete and PSI are examples here). That way, a (potentially) - * slow proxy is only used if neccessary. This of course also means, that the timeout + * slow proxy is only used if necessary. This of course also means, that the timeout * per streamhost should be kept short. If one or two firewalled adresses are specified, * they have to timeout first before a proxy is tried. */ diff -Nru bitlbee-3.4.1/protocols/jabber/sasl.c bitlbee-3.4.2/protocols/jabber/sasl.c --- bitlbee-3.4.1/protocols/jabber/sasl.c 2015-06-16 22:30:40.000000000 +0000 +++ bitlbee-3.4.2/protocols/jabber/sasl.c 2016-03-19 23:37:33.000000000 +0000 @@ -37,15 +37,6 @@ "783993391592.apps.googleusercontent.com", "6C-Zgf7Tr7gEQTPlBhMUgo7R", }; -const struct oauth2_service oauth2_service_facebook = -{ - "https://www.facebook.com/dialog/oauth", - "https://graph.facebook.com/oauth/access_token", - "https://www.bitlbee.org/main.php/Facebook/oauth2.html", - "offline_access,xmpp_login", - "126828914005625", - "4b100f0f244d620bf3f15f8b217d4c32", -}; xt_status sasl_pkt_mechanisms(struct xt_node *node, gpointer data) { @@ -53,8 +44,8 @@ struct jabber_data *jd = ic->proto_data; struct xt_node *c, *reply; char *s; - int sup_plain = 0, sup_digest = 0, sup_gtalk = 0, sup_fb = 0; - int want_oauth = FALSE, want_hipchat = FALSE; + int sup_plain = 0, sup_digest = 0, sup_gtalk = 0, sup_anonymous = 0; + int want_oauth = FALSE, want_hipchat = FALSE, want_anonymous = FALSE; GString *mechs; if (!sasl_supported(ic)) { @@ -73,6 +64,7 @@ return XT_ABORT; } + want_anonymous = set_getbool(&ic->acc->set, "anonymous"); want_oauth = set_getbool(&ic->acc->set, "oauth"); want_hipchat = (jd->flags & JFLAG_HIPCHAT); @@ -83,10 +75,10 @@ sup_plain = 1; } else if (c->text && g_strcasecmp(c->text, "DIGEST-MD5") == 0) { sup_digest = 1; + } else if (c->text && g_strcasecmp(c->text, "ANONYMOUS") == 0) { + sup_anonymous = 1; } else if (c->text && g_strcasecmp(c->text, "X-OAUTH2") == 0) { sup_gtalk = 1; - } else if (c->text && g_strcasecmp(c->text, "X-FACEBOOK-PLATFORM") == 0) { - sup_fb = 1; } if (c->text) { @@ -97,7 +89,7 @@ } if (!want_oauth && !sup_plain && !sup_digest) { - if (!sup_gtalk && !sup_fb) { + if (!sup_gtalk) { imcb_error(ic, "This server requires OAuth " "(supported schemes:%s)", mechs->str); } else { @@ -133,15 +125,25 @@ reply->text = base64_encode((unsigned char *) s, len); reply->text_len = strlen(reply->text); g_free(s); - } else if (sup_fb && want_oauth) { - xt_add_attr(reply, "mechanism", "X-FACEBOOK-PLATFORM"); - jd->flags |= JFLAG_SASL_FB; } else if (want_oauth) { imcb_error(ic, "OAuth requested, but not supported by server"); imc_logout(ic, FALSE); xt_free_node(reply); return XT_ABORT; - } else if (sup_digest) { + } else if (want_anonymous && sup_anonymous) { + xt_add_attr(reply, "mechanism", "ANONYMOUS"); + + /* Well, that was easy. */ + } else if (want_anonymous) { + imcb_error(ic, "Anonymous login requested, but not supported by server"); + imc_logout(ic, FALSE); + xt_free_node(reply); + return XT_ABORT; + } else if (sup_digest && !(jd->ssl && sup_plain)) { + /* Only try DIGEST-MD5 if there's no SSL/TLS or if PLAIN isn't supported. + * Which in practice means "don't bother with DIGEST-MD5 most of the time". + * It's weak, pointless over TLS, and often breaks with some servers (hi openfire) */ + xt_add_attr(reply, "mechanism", "DIGEST-MD5"); /* The rest will be done later, when we receive a . */ @@ -282,27 +284,7 @@ dec = frombase64(node->text); - if (jd->flags & JFLAG_SASL_FB) { - /* New-style Facebook OAauth2 support. Instead of sending a refresh - token, they just send an access token that should never expire. */ - GSList *p_in = NULL, *p_out = NULL; - char time[33]; - - oauth_params_parse(&p_in, dec); - oauth_params_add(&p_out, "nonce", oauth_params_get(&p_in, "nonce")); - oauth_params_add(&p_out, "method", oauth_params_get(&p_in, "method")); - oauth_params_free(&p_in); - - g_snprintf(time, sizeof(time), "%lld", (long long) (gettime() * 1000)); - oauth_params_add(&p_out, "call_id", time); - oauth_params_add(&p_out, "api_key", oauth2_service_facebook.consumer_key); - oauth_params_add(&p_out, "v", "1.0"); - oauth_params_add(&p_out, "format", "XML"); - oauth_params_add(&p_out, "access_token", jd->oauth2_access_token); - - reply = oauth_params_string(p_out); - oauth_params_free(&p_out); - } else if (!(s = sasl_get_part(dec, "rspauth"))) { + if (!(s = sasl_get_part(dec, "rspauth"))) { /* See RFC 2831 for for information. */ md5_state_t A1, A2, H; md5_byte_t A1r[16], A2r[16], Hr[16]; diff -Nru bitlbee-3.4.1/protocols/jabber/si.c bitlbee-3.4.2/protocols/jabber/si.c --- bitlbee-3.4.1/protocols/jabber/si.c 2015-06-16 22:30:40.000000000 +0000 +++ bitlbee-3.4.2/protocols/jabber/si.c 2016-03-19 23:37:33.000000000 +0000 @@ -185,7 +185,7 @@ jd->filetransfers = g_slist_prepend(jd->filetransfers, tf); - /* query buddy's features and server's streaming proxies if neccessary */ + /* query buddy's features and server's streaming proxies if necessary */ if (!tf->bud->features) { jabber_iq_query_features(ic, bud->full_jid); @@ -282,7 +282,7 @@ } if (requestok) { - /* Figure out who the transfer should come frome... */ + /* Figure out who the transfer should come from... */ ext_jid = ini_jid; if ((s = strchr(ini_jid, '/'))) { @@ -402,7 +402,7 @@ /* All this means we expect something like this: ( I think ) * * - * [ ] <-- not neccessary + * [ ] <-- not necessary * * * diff -Nru bitlbee-3.4.1/protocols/msn/gw.c bitlbee-3.4.2/protocols/msn/gw.c --- bitlbee-3.4.1/protocols/msn/gw.c 2015-06-16 22:30:40.000000000 +0000 +++ bitlbee-3.4.2/protocols/msn/gw.c 2016-03-19 23:37:33.000000000 +0000 @@ -21,6 +21,7 @@ gw->port = GATEWAY_PORT; gw->ssl = (GATEWAY_PORT == 443); gw->poll_timeout = -1; + gw->write_timeout = -1; gw->ic = ic; gw->md = ic->proto_data; gw->in = g_byte_array_new(); @@ -33,6 +34,11 @@ if (gw->poll_timeout != -1) { b_event_remove(gw->poll_timeout); } + + if (gw->write_timeout != -1) { + b_event_remove(gw->write_timeout); + } + g_byte_array_free(gw->in, TRUE); g_byte_array_free(gw->out, TRUE); g_free(gw->session_id); @@ -188,12 +194,30 @@ return bodylen; } -void msn_gw_write(struct msn_gw *gw, char *buf, size_t len) +static gboolean msn_gw_write_cb(gpointer data, gint source, b_input_condition cond) { - g_byte_array_append(gw->out, (const guint8 *) buf, len); + struct msn_gw *gw; + + if (!(gw = msn_gw_from_ic(data))) { + return FALSE; + } + if (!gw->open) { msn_gw_open(gw); } else if (gw->polling || !gw->waiting) { msn_gw_dorequest(gw, NULL); } + + gw->write_timeout = -1; + return FALSE; +} + +void msn_gw_write(struct msn_gw *gw, char *buf, size_t len) +{ + g_byte_array_append(gw->out, (const guint8 *) buf, len); + + /* do a bit of buffering here to send several commands with a single request */ + if (gw->write_timeout == -1) { + gw->write_timeout = b_timeout_add(1, msn_gw_write_cb, gw->ic); + } } diff -Nru bitlbee-3.4.1/protocols/msn/msn.h bitlbee-3.4.2/protocols/msn/msn.h --- bitlbee-3.4.1/protocols/msn/msn.h 2015-06-16 22:30:40.000000000 +0000 +++ bitlbee-3.4.2/protocols/msn/msn.h 2016-03-19 23:37:33.000000000 +0000 @@ -111,6 +111,7 @@ GByteArray *out; int poll_timeout; + int write_timeout; b_event_handler callback; diff -Nru bitlbee-3.4.1/protocols/msn/msn_util.c bitlbee-3.4.2/protocols/msn/msn_util.c --- bitlbee-3.4.1/protocols/msn/msn_util.c 2015-06-16 22:30:40.000000000 +0000 +++ bitlbee-3.4.2/protocols/msn/msn_util.c 2016-03-19 23:37:33.000000000 +0000 @@ -40,7 +40,7 @@ return NULL; } - return g_markup_printf_escaped("", + return g_markup_printf_escaped("", domain, handle, list); } diff -Nru bitlbee-3.4.1/protocols/msn/ns.c bitlbee-3.4.2/protocols/msn/ns.c --- bitlbee-3.4.1/protocols/msn/ns.c 2015-06-16 22:30:40.000000000 +0000 +++ bitlbee-3.4.2/protocols/msn/ns.c 2016-03-19 23:37:33.000000000 +0000 @@ -38,7 +38,7 @@ static void msn_ns_send_adl_start(struct im_connection *ic); static void msn_ns_send_adl(struct im_connection *ic); static void msn_ns_structured_message(struct msn_data *md, char *msg, int msglen, char **cmd); -static void msn_ns_sdg(struct msn_data *md, char *who, char **parts, char *action); +static void msn_ns_sdg(struct msn_data *md, char *who, char **parts, char *action, gboolean selfmessage); static void msn_ns_nfy(struct msn_data *md, char *who, char **parts, char *action, gboolean is_put); int msn_ns_write(struct im_connection *ic, int fd, const char *fmt, ...) @@ -109,6 +109,9 @@ struct msn_data *md = data; struct im_connection *ic = md->ic; + /* this should be taken from XFR, but hardcoding it for now. it also prevents more redirects. */ + const char *redir_data = "VmVyc2lvbjogMQ0KWGZyQ291bnQ6IDINCklzR2VvWGZyOiB0cnVlDQo="; + if (source == -1 && !md->is_http) { imcb_error(ic, "Could not connect to server"); imc_logout(ic, TRUE); @@ -134,7 +137,13 @@ memcpy(md->uuid, "b171be3e", 8); /* :-P */ } - if (msn_ns_write(ic, source, "VER %d %s CVR0\r\n", ++md->trId, MSNP_VER)) { + /* Having to handle potential errors in each write sure makes these ifs awkward...*/ + + if (msn_ns_write(ic, source, "VER %d %s CVR0\r\n", ++md->trId, MSNP_VER) && + msn_ns_write(ic, source, "CVR %d 0x0409 mac 10.2.0 ppc macmsgs 3.5.1 macmsgs %s %s\r\n", + ++md->trId, ic->acc->user, redir_data) && + msn_ns_write(ic, md->fd, "USR %d SSO I %s\r\n", ++md->trId, ic->acc->user)) { + if (!md->is_http) { md->inpa = b_input_add(md->fd, B_EV_IO_READ, msn_ns_callback, md); } @@ -207,11 +216,8 @@ return(0); } - return(msn_ns_write(ic, md->fd, "CVR %d 0x0409 mac 10.2.0 ppc macmsgs 3.5.1 macmsgs %s VmVyc2lvbjogMQ0KWGZyQ291bnQ6IDINClhmclNlbnRVVENUaW1lOiA2MzU2MTQ3OTU5NzgzOTAwMDANCklzR2VvWGZyOiB0cnVlDQo=\r\n", - ++md->trId, ic->acc->user)); } else if (strcmp(cmd[0], "CVR") == 0) { /* We don't give a damn about the information we just received */ - return msn_ns_write(ic, md->fd, "USR %d SSO I %s\r\n", ++md->trId, ic->acc->user); } else if (strcmp(cmd[0], "XFR") == 0) { char *server; int port; @@ -279,6 +285,8 @@ } else if (num_parts >= 3) { md->msglen = atoi(cmd[2]); } + } else if (strcmp(cmd[0], "RML") == 0) { + /* Move along, nothing to see here */ } else if (strcmp(cmd[0], "CHL") == 0) { char *resp; int st; @@ -474,27 +482,49 @@ return 1; } +/* returns newly allocated string */ +static char *msn_ns_parse_header_address(struct msn_data *md, char *headers, char *header_name) +{ + char *semicolon = NULL; + char *header = NULL; + char *address = NULL; + + if (!(header = get_rfc822_header(headers, header_name, 0))) { + return NULL; + } + + /* either the semicolon or the end of the string */ + semicolon = strchr(header, ';') ? : (header + strlen(header)); + + address = g_strndup(header + 2, semicolon - header - 2); + + g_free(header); + return address; +} + static void msn_ns_structured_message(struct msn_data *md, char *msg, int msglen, char **cmd) { char **parts = NULL; - char *semicolon = NULL; char *action = NULL; - char *from = NULL; char *who = NULL; + gboolean selfmessage = FALSE; parts = g_strsplit(msg, "\r\n\r\n", 4); - if (!(from = get_rfc822_header(parts[0], "From", 0))) { + if (!(who = msn_ns_parse_header_address(md, parts[0], "From"))) { goto cleanup; } - /* either the semicolon or the end of the string */ - semicolon = strchr(from, ';') ? : (from + strlen(from)); - - who = g_strndup(from + 2, semicolon - from - 2); + if (strcmp(who, md->ic->acc->user) == 0) { + selfmessage = TRUE; + g_free(who); + if (!(who = msn_ns_parse_header_address(md, parts[0], "To"))) { + goto cleanup; + } + } if ((strcmp(cmd[0], "SDG") == 0) && (action = get_rfc822_header(parts[2], "Message-Type", 0))) { - msn_ns_sdg(md, who, parts, action); + msn_ns_sdg(md, who, parts, action, selfmessage); } else if ((strcmp(cmd[0], "NFY") == 0) && (action = get_rfc822_header(parts[2], "Uri", 0))) { gboolean is_put = (strcmp(cmd[1], "PUT") == 0); @@ -504,18 +534,17 @@ cleanup: g_strfreev(parts); g_free(action); - g_free(from); g_free(who); } -static void msn_ns_sdg(struct msn_data *md, char *who, char **parts, char *action) +static void msn_ns_sdg(struct msn_data *md, char *who, char **parts, char *action, gboolean selfmessage) { struct im_connection *ic = md->ic; - if (strcmp(action, "Control/Typing") == 0) { + if (strcmp(action, "Control/Typing") == 0 && !selfmessage) { imcb_buddy_typing(ic, who, OPT_TYPING); } else if (strcmp(action, "Text") == 0) { - imcb_buddy_msg(ic, who, parts[3], 0, 0); + imcb_buddy_msg(ic, who, parts[3], selfmessage ? OPT_SELFMESSAGE : 0, 0); } } @@ -594,7 +623,13 @@ msn_ns_write(ic, -1, "USR %d SSO S %s %s {%s}\r\n", ++md->trId, md->tokens[0], token, md->uuid); } else { imcb_error(ic, "Error during Passport authentication: %s", error); - imc_logout(ic, TRUE); + + /* don't reconnect with auth errors */ + if (error && g_str_has_prefix(error, "wsse:FailedAuthentication")) { + imc_logout(ic, FALSE); + } else { + imc_logout(ic, TRUE); + } } } diff -Nru bitlbee-3.4.1/protocols/msn/soap.c bitlbee-3.4.2/protocols/msn/soap.c --- bitlbee-3.4.1/protocols/msn/soap.c 2015-06-16 22:30:40.000000000 +0000 +++ bitlbee-3.4.2/protocols/msn/soap.c 2016-03-19 23:37:33.000000000 +0000 @@ -269,7 +269,6 @@ struct msn_soap_passport_sso_data *sd = soap_req->data; struct im_connection *ic = soap_req->ic; struct msn_data *md = ic->proto_data; - char pass[MAX_PASSPORT_PWLEN + 1]; if (sd->redirect) { soap_req->url = sd->redirect; @@ -285,10 +284,8 @@ soap_req->url = g_strdup(SOAP_PASSPORT_SSO_URL); } - strncpy(pass, ic->acc->pass, MAX_PASSPORT_PWLEN); - pass[MAX_PASSPORT_PWLEN] = '\0'; soap_req->payload = g_markup_printf_escaped(SOAP_PASSPORT_SSO_PAYLOAD, - ic->acc->user, pass, md->pp_policy); + ic->acc->user, ic->acc->pass, md->pp_policy); return MSN_SOAP_OK; } @@ -326,6 +323,7 @@ struct msn_soap_passport_sso_data *sd = soap_req->data; struct xt_node *code = xt_find_node(node->children, "faultcode"); struct xt_node *string = xt_find_node(node->children, "faultstring"); + struct xt_node *reqstatus = xt_find_path(node, "psf:pp/psf:reqstatus"); struct xt_node *url; if (code == NULL || code->text_len == 0) { @@ -334,6 +332,9 @@ (url = xt_find_node(node->children, "psf:redirectUrl")) && url->text_len > 0) { sd->redirect = g_strdup(url->text); + } else if (reqstatus && strcmp(reqstatus->text, "0x800488fe") == 0) { + char *msg = "Location blocked. Log in to live.com, go to recent activity and click 'this was me'"; + sd->error = g_strdup_printf("%s (%s)", code->text, msg); } else { sd->error = g_strdup_printf("%s (%s)", code->text, string && string->text_len ? string->text : "no description available"); @@ -345,6 +346,7 @@ static const struct xt_handler_entry msn_soap_passport_sso_parser[] = { { "wsse:BinarySecurityToken", "wst:RequestedSecurityToken", msn_soap_passport_sso_token }, { "S:Fault", "S:Envelope", msn_soap_passport_failure }, + { "S:Fault", "wst:RequestSecurityTokenResponse", msn_soap_passport_failure }, { NULL, NULL, NULL } }; @@ -773,8 +775,7 @@ if (wtf) { imcb_log(soap_req->ic, "Warning: %d contacts were in both your " "block and your allow list. Assuming they're all " - "allowed. Use the official WLM client once to fix " - "this.", wtf); + "allowed.", wtf); } msn_auth_got_contact_list(soap_req->ic); diff -Nru bitlbee-3.4.1/protocols/msn/soap.h bitlbee-3.4.2/protocols/msn/soap.h --- bitlbee-3.4.1/protocols/msn/soap.h 2015-06-16 22:30:40.000000000 +0000 +++ bitlbee-3.4.2/protocols/msn/soap.h 2016-03-19 23:37:33.000000000 +0000 @@ -60,7 +60,6 @@ #define SOAP_PASSPORT_SSO_URL "https://login.live.com/RST.srf" #define SOAP_PASSPORT_SSO_URL_MSN "https://msnia.login.live.com/pp900/RST.srf" -#define MAX_PASSPORT_PWLEN 16 #define SOAP_PASSPORT_SSO_PAYLOAD \ "name); + disabled_protocols = g_list_append(disabled_protocols, p); } else { protocols = g_list_append(protocols, p); } } -struct prpl *find_protocol(const char *name) +static int proto_name_cmp(const void *proto_, const void *name) { - GList *gl; - - for (gl = protocols; gl; gl = gl->next) { - struct prpl *proto = gl->data; + const struct prpl *proto = proto_; + return g_strcasecmp(proto->name, name); +} - if (g_strcasecmp(proto->name, name) == 0) { - return proto; - } - } +struct prpl *find_protocol(const char *name) +{ + GList *gl = g_list_find_custom(protocols, name, proto_name_cmp); + return gl ? gl->data: NULL; +} - return NULL; +gboolean is_protocol_disabled(const char *name) +{ + return g_list_find_custom(disabled_protocols, name, proto_name_cmp) != NULL; } void nogaim_init() @@ -203,6 +206,10 @@ char *text; account_t *a; + if (!ic->bee->ui->log) { + return; + } + va_start(params, format); text = g_strdup_vprintf(format, params); va_end(params); @@ -221,10 +228,9 @@ /* If we found one, include the screenname in the message. */ if (a) { - /* FIXME(wilmer): ui_log callback or so */ - irc_rootmsg(ic->bee->ui_data, "%s - %s", ic->acc->tag, text); + ic->bee->ui->log(ic->bee, ic->acc->tag, text); } else { - irc_rootmsg(ic->bee->ui_data, "%s - %s", ic->acc->prpl->name, text); + ic->bee->ui->log(ic->bee, ic->acc->prpl->name, text); } g_free(text); @@ -615,7 +621,8 @@ if (away && *away) { GList *m = ic->acc->prpl->away_states(ic); msg = ic->acc->flags & ACC_FLAG_AWAY_MESSAGE ? away : NULL; - away = imc_away_state_find(m, away, &msg) ? : m->data; + away = imc_away_state_find(m, away, &msg) ? : + (imc_away_state_find(m, "away", &msg) ? : m->data); } else if (ic->acc->flags & ACC_FLAG_STATUS_MESSAGE) { away = NULL; msg = set_getstr(&ic->acc->set, "status") ? @@ -734,32 +741,8 @@ ic->acc->prpl->rem_deny(ic, handle); } +/* Deprecated: using this function resulted in merging several handles accidentally + * Also the irc layer handles this decently nowadays */ void imcb_clean_handle(struct im_connection *ic, char *handle) { - /* Accepts a handle and does whatever is necessary to make it - BitlBee-friendly. Currently this means removing everything - outside 33-127 (ASCII printable excl spaces), @ (only one - is allowed) and ! and : */ - char out[strlen(handle) + 1]; - int s, d; - - s = d = 0; - while (handle[s]) { - if (handle[s] > ' ' && handle[s] != '!' && handle[s] != ':' && - (handle[s] & 0x80) == 0) { - if (handle[s] == '@') { - /* See if we got an @ already? */ - out[d] = 0; - if (strchr(out, '@')) { - continue; - } - } - - out[d++] = handle[s]; - } - s++; - } - out[d] = handle[s]; - - strcpy(handle, out); } diff -Nru bitlbee-3.4.1/protocols/nogaim.h bitlbee-3.4.2/protocols/nogaim.h --- bitlbee-3.4.1/protocols/nogaim.h 2015-06-16 22:30:40.000000000 +0000 +++ bitlbee-3.4.2/protocols/nogaim.h 2016-03-19 23:37:33.000000000 +0000 @@ -69,6 +69,7 @@ #define OPT_NOOTR 0x00001000 /* protocol not suitable for OTR */ #define OPT_PONGS 0x00010000 /* Service sends us keep-alives */ #define OPT_PONGED 0x00020000 /* Received a keep-alive during last interval */ +#define OPT_SELFMESSAGE 0x00080000 /* A message sent by self from another location */ /* ok. now the fun begins. first we create a connection structure */ struct im_connection { @@ -273,6 +274,7 @@ void nogaim_init(); G_MODULE_EXPORT GSList *get_connections(); G_MODULE_EXPORT struct prpl *find_protocol(const char *name); +G_MODULE_EXPORT gboolean is_protocol_disabled(const char *name); /* When registering a new protocol, you should allocate space for a new prpl * struct, initialize it (set the function pointers to point to your * functions), finally call this function. */ @@ -319,14 +321,14 @@ * server confirms that the add was successful. Don't forget to do this! */ G_MODULE_EXPORT void imcb_add_buddy(struct im_connection *ic, const char *handle, const char *group); G_MODULE_EXPORT void imcb_remove_buddy(struct im_connection *ic, const char *handle, char *group); -G_MODULE_EXPORT struct buddy *imcb_find_buddy(struct im_connection *ic, char *handle); G_MODULE_EXPORT void imcb_rename_buddy(struct im_connection *ic, const char *handle, const char *realname); G_MODULE_EXPORT void imcb_buddy_nick_hint(struct im_connection *ic, const char *handle, const char *nick); G_MODULE_EXPORT void imcb_buddy_action_response(bee_user_t *bu, const char *action, char * const args[], void *data); -G_MODULE_EXPORT void imcb_buddy_typing(struct im_connection *ic, const char *handle, uint32_t flags); +G_MODULE_EXPORT void imcb_buddy_typing(struct im_connection *ic, const char *handle, guint32 flags); G_MODULE_EXPORT struct bee_user *imcb_buddy_by_handle(struct im_connection *ic, const char *handle); -G_MODULE_EXPORT void imcb_clean_handle(struct im_connection *ic, char *handle); + +G_GNUC_DEPRECATED G_MODULE_EXPORT void imcb_clean_handle(struct im_connection *ic, char *handle); /* Actions, or whatever. */ int imc_away_send_update(struct im_connection *ic); diff -Nru bitlbee-3.4.1/protocols/oscar/aim.h bitlbee-3.4.2/protocols/oscar/aim.h --- bitlbee-3.4.1/protocols/oscar/aim.h 2015-06-16 22:30:40.000000000 +0000 +++ bitlbee-3.4.2/protocols/oscar/aim.h 2016-03-19 23:37:33.000000000 +0000 @@ -21,6 +21,11 @@ #include "bitlbee.h" +#ifdef WITH_PURPLE +/* For compatibility with builds that include both purple and this oscar module */ +#include "aim_prefixes.h" +#endif + /* XXX adjust these based on autoconf-detected platform */ typedef guint32 aim_snacid_t; typedef guint16 flap_seqnum_t; @@ -870,7 +875,7 @@ /* * SNAC Family: Internal Messages * - * This isn't truely a SNAC family either, but using + * This isn't truly a SNAC family either, but using * these, we can integrated non-SNAC services into * the SNAC-centered libfaim callback structure. * diff -Nru bitlbee-3.4.1/protocols/oscar/aim_prefixes.h bitlbee-3.4.2/protocols/oscar/aim_prefixes.h --- bitlbee-3.4.1/protocols/oscar/aim_prefixes.h 1970-01-01 00:00:00.000000000 +0000 +++ bitlbee-3.4.2/protocols/oscar/aim_prefixes.h 2016-03-19 23:37:33.000000000 +0000 @@ -0,0 +1,93 @@ +/* Automatically generated prefixes to avoid symbol conflicts with libpurple. + * Because sometimes people compile both oscar and purple and things break. + * This is awful but i know no better solution other than not running libpurple in the same process. + * + * Made with the following commands: + * + * nm --defined-only oscar_mod.o | awk '{print $3}' | sort | uniq > bee_syms + * nm --defined-only liboscar.so | awk '{print $3}' | sort | uniq > purple_syms + * comm -12 bee_syms purple_syms | sed 's/^.*$/#define & b_&/' > aim_prefixes.h + */ + +#define admin_modfirst b_admin_modfirst +#define aim_admin_changepasswd b_aim_admin_changepasswd +#define aim_admin_getinfo b_aim_admin_getinfo +#define aim_admin_reqconfirm b_aim_admin_reqconfirm +#define aim_admin_setemail b_aim_admin_setemail +#define aim_admin_setnick b_aim_admin_setnick +#define aim_bos_reqrights b_aim_bos_reqrights +#define aim_cachecookie b_aim_cachecookie +#define aim_cachesnac b_aim_cachesnac +#define aim_callhandler b_aim_callhandler +#define aim_caps b_aim_caps +#define aim_chat_join b_aim_chat_join +#define aim_chatnav_createroom b_aim_chatnav_createroom +#define aim_chatnav_reqrights b_aim_chatnav_reqrights +#define aim_chat_readroominfo b_aim_chat_readroominfo +#define aim_chat_send_im b_aim_chat_send_im +#define aim_checkcookie b_aim_checkcookie +#define aim_cleansnacs b_aim_cleansnacs +#define aim_cookie_free b_aim_cookie_free +#define aim__findmodule b_aim__findmodule +#define aim__findmodulebygroup b_aim__findmodulebygroup +#define aim_genericreq_l b_aim_genericreq_l +#define aim_genericreq_n b_aim_genericreq_n +#define aim_genericreq_n_snacid b_aim_genericreq_n_snacid +#define aim_icq_freeinfo b_aim_icq_freeinfo +#define aim_icq_getallinfo b_aim_icq_getallinfo +#define aim_im_sendmtn b_aim_im_sendmtn +#define aim_initsnachash b_aim_initsnachash +#define aim_mkcookie b_aim_mkcookie +#define aim_newsnac b_aim_newsnac +#define aim_putsnac b_aim_putsnac +#define aim__registermodule b_aim__registermodule +#define aim_remsnac b_aim_remsnac +#define aim_request_login b_aim_request_login +#define aim_send_login b_aim_send_login +#define aim__shutdownmodules b_aim__shutdownmodules +#define aim_ssi_enable b_aim_ssi_enable +#define aim_ssi_freelist b_aim_ssi_freelist +#define aim_ssi_getpermdeny b_aim_ssi_getpermdeny +#define aim_ssi_itemlist_add b_aim_ssi_itemlist_add +#define aim_ssi_itemlist_find b_aim_ssi_itemlist_find +#define aim_ssi_itemlist_finditem b_aim_ssi_itemlist_finditem +#define aim_ssi_itemlist_rebuildgroup b_aim_ssi_itemlist_rebuildgroup +#define aim_ssi_modbegin b_aim_ssi_modbegin +#define aim_ssi_modend b_aim_ssi_modend +#define aim_ssi_movebuddy b_aim_ssi_movebuddy +#define aim_ssi_reqrights b_aim_ssi_reqrights +#define aim_uncachecookie b_aim_uncachecookie +#define auth_modfirst b_auth_modfirst +#define bos_modfirst b_bos_modfirst +#define buddylist_modfirst b_buddylist_modfirst +#define chat_modfirst b_chat_modfirst +#define chatnav_modfirst b_chatnav_modfirst +#define extract_name b_extract_name +#define icq_modfirst b_icq_modfirst +#define incomingim b_incomingim +#define incomingim_ch2_chat_free b_incomingim_ch2_chat_free +#define incomingim_ch2_icqserverrelay_free b_incomingim_ch2_icqserverrelay_free +#define locate_modfirst b_locate_modfirst +#define misc_modfirst b_misc_modfirst +#define msgerrreason b_msgerrreason +#define msg_modfirst b_msg_modfirst +#define oscar_add_buddy b_oscar_add_buddy +#define oscar_add_deny b_oscar_add_deny +#define oscar_add_permit b_oscar_add_permit +#define oscar_chat_invite b_oscar_chat_invite +#define oscar_chat_kill b_oscar_chat_kill +#define oscar_chat_leave b_oscar_chat_leave +#define oscar_encoding_to_utf8 b_oscar_encoding_to_utf8 +#define oscar_get_info b_oscar_get_info +#define oscar_init b_oscar_init +#define oscar_keepalive b_oscar_keepalive +#define oscar_login b_oscar_login +#define oscar_rem_deny b_oscar_rem_deny +#define oscar_remove_buddy b_oscar_remove_buddy +#define oscar_rem_permit b_oscar_rem_permit +#define oscar_send_typing b_oscar_send_typing +#define search_modfirst b_search_modfirst +#define snachandler b_snachandler +#define ssi_modfirst b_ssi_modfirst +#define ssi_shutdown b_ssi_shutdown +#define stats_modfirst b_stats_modfirst diff -Nru bitlbee-3.4.1/protocols/oscar/auth.c bitlbee-3.4.2/protocols/oscar/auth.c --- bitlbee-3.4.1/protocols/oscar/auth.c 2015-06-16 22:30:40.000000000 +0000 +++ bitlbee-3.4.2/protocols/oscar/auth.c 2016-03-19 23:37:33.000000000 +0000 @@ -40,7 +40,7 @@ * Normally the FLAP version is sent as the first few bytes of the cookie, * meaning you generally never call this. * - * But there are times when something might want it seperate. Specifically, + * But there are times when something might want it separate. Specifically, * libfaim sends this internally when doing SNAC login. * */ diff -Nru bitlbee-3.4.1/protocols/oscar/conn.c bitlbee-3.4.2/protocols/oscar/conn.c --- bitlbee-3.4.1/protocols/oscar/conn.c 2015-06-16 22:30:40.000000000 +0000 +++ bitlbee-3.4.2/protocols/oscar/conn.c 2016-03-19 23:37:33.000000000 +0000 @@ -14,7 +14,7 @@ /* * In OSCAR, every connection has a set of SNAC groups associated * with it. These are the groups that you can send over this connection - * without being guarenteed a "Not supported" SNAC error. + * without being guaranteed a "Not supported" SNAC error. * * The grand theory of things says that these associations transcend * what libfaim calls "connection types" (conn->type). You can probably @@ -35,7 +35,7 @@ * easy and deliver this SNAC for you, but there isn't one there. * * Here comes the good bit. Without even letting anyone know, particularly - * the module that decided to send this SNAC, and definitly not that twit + * the module that decided to send this SNAC, and definitely not that twit * in Greenland, you send out a service request. In this request, you have * marked the need for a connection supporting group 0x000e. A few seconds * later, you receive a service redirect with an IP address and a cookie in @@ -316,7 +316,7 @@ { if (deadconn->fd >= 3) { - closesocket(deadconn->fd); + proxy_disconnect(deadconn->fd); } deadconn->fd = -1; if (deadconn->handlerlist) { diff -Nru bitlbee-3.4.1/protocols/oscar/im.c bitlbee-3.4.2/protocols/oscar/im.c --- bitlbee-3.4.1/protocols/oscar/im.c 2015-06-16 22:30:40.000000000 +0000 +++ bitlbee-3.4.2/protocols/oscar/im.c 2016-03-19 23:37:33.000000000 +0000 @@ -49,7 +49,7 @@ * notably when the (r) symbol is used), you must use the full UNICODE * encoding for your message. In UNICODE mode, _all_ characters must * occupy 16bits, including ones that are not special. (Remember that - * the first 128 UNICODE symbols are equivelent to ASCII7, however they + * the first 128 UNICODE symbols are equivalent to ASCII7, however they * must be prefixed with a zero high order byte.) * * I strongly discourage the use of UNICODE mode, mainly because none @@ -63,7 +63,7 @@ * Implementation note: Since this is one of the most-used functions * in all of libfaim, it is written with performance in mind. As such, * it is not as clear as it could be in respect to how this message is - * supposed to be layed out. Most obviously, tlvlists should be used + * supposed to be laid out. Most obviously, tlvlists should be used * instead of writing out the bytes manually. * * XXX more precise verification that we never send SNACs larger than 8192 @@ -475,7 +475,7 @@ * open source clients (like encryption or something) -- see faimtest for * examples of how to do this. * - * I would definitly recommend avoiding this feature unless you really + * I would definitely recommend avoiding this feature unless you really * know what you are doing, and/or you have something neat to do with it. * */ @@ -637,7 +637,7 @@ ; /* no subencoding */ } #if 0 - /* XXX this isn't really necesary... */ + /* XXX this isn't really necessary... */ if (((args.flag1 != 0x0000) && (args.flag1 != 0x0002) && (args.flag1 != 0x0003) && @@ -1160,11 +1160,11 @@ * Channel ID. * * Channel 0x0001 is the message channel. There are - * other channels for things called "rendevous" + * other channels for things called "rendezvous" * which represent chat and some of the other new * features of AIM2/3/3.5. * - * Channel 0x0002 is the Rendevous channel, which + * Channel 0x0002 is the Rendezvous channel, which * is where Chat Invitiations and various client-client * connection negotiations come from. * @@ -1180,7 +1180,7 @@ * Note that although this contains TLVs that appear contiguous * with the TLVs read below, they are two different pieces. The * userinfo block contains the number of TLVs that contain user - * information, the rest are not even though there is no seperation. + * information, the rest are not even though there is no separation. * aim_extractuserinfo() returns the number of bytes used by the * userinfo tlvs, so you can start reading the rest of them right * afterward. @@ -1252,7 +1252,7 @@ /* * - * I definitly recommend sending this. If you don't, you'll be stuck + * I definitely recommend sending this. If you don't, you'll be stuck * with the rather unreasonable defaults. You don't want those. Send this. * */ diff -Nru bitlbee-3.4.1/protocols/oscar/misc.c bitlbee-3.4.2/protocols/oscar/misc.c --- bitlbee-3.4.1/protocols/oscar/misc.c 2015-06-16 22:30:40.000000000 +0000 +++ bitlbee-3.4.2/protocols/oscar/misc.c 2016-03-19 23:37:33.000000000 +0000 @@ -2,7 +2,7 @@ /* * aim_misc.c * - * TODO: Seperate a lot of this into an aim_bos.c. + * TODO: Separate a lot of this into an aim_bos.c. * * Other things... * diff -Nru bitlbee-3.4.1/protocols/oscar/msgcookie.c bitlbee-3.4.2/protocols/oscar/msgcookie.c --- bitlbee-3.4.1/protocols/oscar/msgcookie.c 2015-06-16 22:30:40.000000000 +0000 +++ bitlbee-3.4.2/protocols/oscar/msgcookie.c 2016-03-19 23:37:33.000000000 +0000 @@ -141,7 +141,7 @@ * @sess: session to remove the cookie from * @cookiep: the address of a pointer to the cookie struct to remove * - * this function removes the cookie *cookie from teh list of cookies + * this function removes the cookie *cookie from the list of cookies * in sess, and then frees all memory associated with it. including * its data! if you want to use the private data after calling this, * make sure you copy it first. diff -Nru bitlbee-3.4.1/protocols/oscar/oscar.c bitlbee-3.4.2/protocols/oscar/oscar.c --- bitlbee-3.4.1/protocols/oscar/oscar.c 2015-06-16 22:30:40.000000000 +0000 +++ bitlbee-3.4.2/protocols/oscar/oscar.c 2016-03-19 23:37:33.000000000 +0000 @@ -1265,7 +1265,7 @@ ret = incomingim_chan1(sess, fr->conn, userinfo, args); } break; - case 2: { /* rendevous */ + case 2: { /* rendezvous */ struct aim_incomingim_ch2_args *args; args = va_arg(ap, struct aim_incomingim_ch2_args *); ret = incomingim_chan2(sess, fr->conn, userinfo, args); diff -Nru bitlbee-3.4.1/protocols/oscar/rxhandlers.c bitlbee-3.4.2/protocols/oscar/rxhandlers.c --- bitlbee-3.4.1/protocols/oscar/rxhandlers.c 2015-06-16 22:30:40.000000000 +0000 +++ bitlbee-3.4.2/protocols/oscar/rxhandlers.c 2016-03-19 23:37:33.000000000 +0000 @@ -380,7 +380,7 @@ /* * This doesn't have to be called here. It could easily be done - * by a seperate thread or something. It's an administrative operation, + * by a separate thread or something. It's an administrative operation, * and can take a while. Though the less you call it the less memory * you'll have :) */ diff -Nru bitlbee-3.4.1/protocols/oscar/rxqueue.c bitlbee-3.4.2/protocols/oscar/rxqueue.c --- bitlbee-3.4.1/protocols/oscar/rxqueue.c 2015-06-16 22:30:40.000000000 +0000 +++ bitlbee-3.4.2/protocols/oscar/rxqueue.c 2016-03-19 23:37:33.000000000 +0000 @@ -360,7 +360,7 @@ /* * Grab a single command sequence off the socket, and enqueue - * it in the incoming event queue in a seperate struct. + * it in the incoming event queue in a separate struct. */ int aim_get_command(aim_session_t *sess, aim_conn_t *conn) { @@ -478,7 +478,7 @@ } /* - * Purge recieve queue of all handled commands (->handled==1). Also + * Purge receive queue of all handled commands (->handled==1). Also * allows for selective freeing using ->nofree so that the client can * keep the data for various purposes. * diff -Nru bitlbee-3.4.1/protocols/oscar/service.c bitlbee-3.4.2/protocols/oscar/service.c --- bitlbee-3.4.1/protocols/oscar/service.c 2015-06-16 22:30:40.000000000 +0000 +++ bitlbee-3.4.2/protocols/oscar/service.c 2016-03-19 23:37:33.000000000 +0000 @@ -156,7 +156,7 @@ } /* - * OSCAR defines several 'rate classes'. Each class has seperate + * OSCAR defines several 'rate classes'. Each class has separate * rate limiting properties (limit level, alert level, disconnect * level, etc), and a set of SNAC family/type pairs associated with * it. The rate classes, their limiting properties, and the definitions @@ -708,9 +708,9 @@ * on Win32. So far, AOL has only been requesting bytes in static regions * of memory. (I won't put it past them to start requesting data in * less static regions -- regions that are initialized at run time, but still - * before the client recieves this request.) + * before the client receives this request.) * - * When the client recieves the request, it adds it to the current ds + * When the client receives the request, it adds it to the current ds * (0x00400000) and dereferences it, copying the data into a buffer which * it then runs directly through the MD5 hasher. The 16 byte output of * the hash is then sent back to the server. @@ -722,14 +722,14 @@ * for accessing the AOL network using unauthorized software. You can * download a FREE, fully featured, and authorized client, here * http://www.aol.com/aim/download2.html" - * The connection is then closed, recieving disconnect code 1, URL + * The connection is then closed, receiving disconnect code 1, URL * http://www.aim.aol.com/errors/USER_LOGGED_OFF_NEW_LOGIN.html. * * Note, however, that numerous inconsistencies can cause the above error, - * not just sending back a bad hash. Do not immediatly suspect this code + * not just sending back a bad hash. Do not immediately suspect this code * if you get disconnected. AOL and the open/free software community have * played this game for a couple years now, generating the above message - * on numerous ocassions. + * on numerous occasions. * * Anyway, neener. We win again. * diff -Nru bitlbee-3.4.1/protocols/oscar/tlv.c bitlbee-3.4.2/protocols/oscar/tlv.c --- bitlbee-3.4.1/protocols/oscar/tlv.c 2015-06-16 22:30:40.000000000 +0000 +++ bitlbee-3.4.2/protocols/oscar/tlv.c 2016-03-19 23:37:33.000000000 +0000 @@ -24,7 +24,7 @@ * XXX There should be a flag setable here to have the tlvlist contain * bstream references, so that at least the ->value portion of each * element doesn't need to be malloc/memcpy'd. This could prove to be - * just as effecient as the in-place TLV parsing used in a couple places + * just as efficient as the in-place TLV parsing used in a couple places * in libfaim. * */ @@ -134,7 +134,7 @@ /** * aim_addtlvtochain_str - Add a string to a TLV chain - * @list: Desination chain (%NULL pointer if empty) + * @list: Designation chain (%NULL pointer if empty) * @type: TLV type * @str: String to add * @len: Length of string to add (not including %NULL) diff -Nru bitlbee-3.4.1/protocols/oscar/txqueue.c bitlbee-3.4.2/protocols/oscar/txqueue.c --- bitlbee-3.4.1/protocols/oscar/txqueue.c 2015-06-16 22:30:40.000000000 +0000 +++ bitlbee-3.4.2/protocols/oscar/txqueue.c 2016-03-19 23:37:33.000000000 +0000 @@ -66,7 +66,7 @@ * * The overall purpose here is to enqueue the passed in command struct * into the outgoing (tx) queue. Basically... - * 1) Make a scope-irrelevent copy of the struct + * 1) Make a scope-irrelevant copy of the struct * 3) Mark as not-sent-yet * 4) Enqueue the struct into the list * 6) Return diff -Nru bitlbee-3.4.1/protocols/purple/ft.c bitlbee-3.4.2/protocols/purple/ft.c --- bitlbee-3.4.1/protocols/purple/ft.c 2015-06-16 22:30:40.000000000 +0000 +++ bitlbee-3.4.2/protocols/purple/ft.c 2016-03-19 23:37:33.000000000 +0000 @@ -231,11 +231,6 @@ } } -static void prplcb_xfer_dbg(PurpleXfer *xfer) -{ - fprintf(stderr, "prplcb_xfer_dbg 0x%p\n", xfer); -} - /* Sending files (UI->IM): */ static gboolean prpl_xfer_write(struct file_transfer *ft, char *buffer, unsigned int len); @@ -336,13 +331,13 @@ PurpleXferUiOps bee_xfer_uiops = { - prplcb_xfer_new, - prplcb_xfer_destroy, - NULL, /* prplcb_xfer_add, */ - prplcb_xfer_progress, - prplcb_xfer_dbg, - prplcb_xfer_cancel_remote, - NULL, - NULL, - prplcb_xfer_dbg, + prplcb_xfer_new, /* new_xfer */ + prplcb_xfer_destroy, /* destroy */ + NULL, /* add_xfer */ + prplcb_xfer_progress, /* update_progress */ + NULL, /* cancel_local */ + prplcb_xfer_cancel_remote, /* cancel_remote */ + NULL, /* ui_write */ + NULL, /* ui_read */ + NULL, /* data_not_sent */ }; diff -Nru bitlbee-3.4.1/protocols/purple/ft-direct.c bitlbee-3.4.2/protocols/purple/ft-direct.c --- bitlbee-3.4.1/protocols/purple/ft-direct.c 2015-06-16 22:30:40.000000000 +0000 +++ bitlbee-3.4.2/protocols/purple/ft-direct.c 2016-03-19 23:37:33.000000000 +0000 @@ -192,15 +192,15 @@ PurpleXferUiOps bee_xfer_uiops = { - prplcb_xfer_new, - prplcb_xfer_dbg, - prplcb_xfer_dbg, - prplcb_xfer_progress, - prplcb_xfer_dbg, - prplcb_xfer_dbg, - prplcb_xfer_write, - prplcb_xfer_read, - prplcb_xfer_dbg, + prplcb_xfer_new, /* new_xfer */ + prplcb_xfer_dbg, /* destroy */ + prplcb_xfer_dbg, /* add_xfer */ + prplcb_xfer_progress, /* update_progress */ + prplcb_xfer_dbg, /* cancel_local */ + prplcb_xfer_dbg, /* cancel_remote */ + prplcb_xfer_write, /* ui_write */ + prplcb_xfer_read, /* ui_read */ + prplcb_xfer_dbg, /* data_not_sent */ }; static gboolean prplcb_xfer_send_cb(gpointer data, gint fd, b_input_condition cond); diff -Nru bitlbee-3.4.1/protocols/purple/purple.c bitlbee-3.4.2/protocols/purple/purple.c --- bitlbee-3.4.1/protocols/purple/purple.c 2015-06-16 22:30:40.000000000 +0000 +++ bitlbee-3.4.2/protocols/purple/purple.c 2016-03-19 23:37:33.000000000 +0000 @@ -112,6 +112,7 @@ Remember that libpurple is not really meant to be used on public servers anyway! */ if (!dir_fixed) { + PurpleCertificatePool *pool; irc_t *irc = acc->bee->ui_data; char *dir; @@ -121,6 +122,18 @@ purple_blist_load(); purple_prefs_load(); + + if (proxytype == PROXY_SOCKS4A) { + /* do this here after loading prefs. yes, i know, it sucks */ + purple_prefs_set_bool("/purple/proxy/socks4_remotedns", TRUE); + } + + /* re-create the certificate cache directory */ + pool = purple_certificate_find_pool("x509", "tls_peers"); + dir = purple_certificate_pool_mkpath(pool, NULL); + purple_build_dir(dir, 0700); + g_free(dir); + dir_fixed = TRUE; } @@ -348,6 +361,10 @@ return; } + while (ic->groupchats) { + imcb_chat_free(ic->groupchats->data); + } + purple_account_set_enabled(pd->account, "BitlBee", FALSE); purple_connections = g_slist_remove(purple_connections, ic); purple_accounts_remove(pd->account); @@ -633,7 +650,7 @@ /* Call the fucker. */ callback = (void *) mi->callback; - callback(&pb->node, menu->data); + callback(&pb->node, mi->data); return NULL; } @@ -701,11 +718,17 @@ } else if (strcmp(pce->identifier, "passwd") == 0) { g_hash_table_replace(chat_hash, "passwd", g_strdup(password)); } + + g_free(pce); } + g_list_free(info); + serv_join_chat(purple_account_get_connection(pd->account), chat_hash); - return NULL; + g_hash_table_destroy(chat_hash); + + return imcb_chat_new(ic, room); } void purple_transfer_request(struct im_connection *ic, file_transfer_t *ft, char *handle); @@ -727,11 +750,11 @@ static PurpleCoreUiOps bee_core_uiops = { - NULL, - NULL, - purple_ui_init, - NULL, - prplcb_ui_info, + NULL, /* ui_prefs_init */ + NULL, /* debug_ui_init */ + purple_ui_init, /* ui_init */ + NULL, /* quit */ + prplcb_ui_info, /* get_ui_info */ }; static void prplcb_conn_progress(PurpleConnection *gc, const char *text, size_t step, size_t step_count) @@ -758,9 +781,7 @@ // user list needs to be requested for Gadu-Gadu purple_gg_buddylist_import(gc); - if (gc->flags & PURPLE_CONNECTION_HTML) { - ic->flags |= OPT_DOES_HTML; - } + ic->flags |= OPT_DOES_HTML; } static void prplcb_conn_disconnected(PurpleConnection *gc) @@ -794,14 +815,14 @@ static PurpleConnectionUiOps bee_conn_uiops = { - prplcb_conn_progress, - prplcb_conn_connected, - prplcb_conn_disconnected, - prplcb_conn_notice, - NULL, - NULL, - NULL, - prplcb_conn_report_disconnect_reason, + prplcb_conn_progress, /* connect_progress */ + prplcb_conn_connected, /* connected */ + prplcb_conn_disconnected, /* disconnected */ + prplcb_conn_notice, /* notice */ + NULL, /* report_disconnect */ + NULL, /* network_connected */ + NULL, /* network_disconnected */ + prplcb_conn_report_disconnect_reason, /* report_disconnect_reason */ }; static void prplcb_blist_update(PurpleBuddyList *list, PurpleBlistNode *node) @@ -876,11 +897,11 @@ static PurpleBlistUiOps bee_blist_uiops = { - NULL, - prplcb_blist_new, - NULL, - prplcb_blist_update, - prplcb_blist_remove, + NULL, /* new_list */ + prplcb_blist_new, /* new_node */ + NULL, /* show */ + prplcb_blist_update, /* update */ + prplcb_blist_remove, /* remove */ }; void prplcb_conv_new(PurpleConversation *conv) @@ -889,9 +910,17 @@ struct im_connection *ic = purple_ic_by_pa(conv->account); struct groupchat *gc; - gc = imcb_chat_new(ic, conv->name); - if (conv->title != NULL) { - imcb_chat_name_hint(gc, conv->title); + gc = bee_chat_by_title(ic->bee, ic, conv->name); + + if (!gc) { + gc = imcb_chat_new(ic, conv->name); + if (conv->title != NULL) { + imcb_chat_name_hint(gc, conv->title); + } + } + + /* don't set the topic if it's just the name */ + if (conv->title != NULL && strcmp(conv->name, conv->title) != 0) { imcb_chat_topic(gc, NULL, conv->title, 0); } @@ -934,42 +963,43 @@ } } -void prplcb_conv_chat_msg(PurpleConversation *conv, const char *who, const char *message, PurpleMessageFlags flags, - time_t mtime) +/* Generic handler for IM or chat messages, covers write_chat, write_im and write_conv */ +static void handle_conv_msg(PurpleConversation *conv, const char *who, const char *message, guint32 bee_flags, time_t mtime) { + struct im_connection *ic = purple_ic_by_pa(conv->account); struct groupchat *gc = conv->ui_data; PurpleBuddy *buddy; - /* ..._SEND means it's an outgoing message, no need to echo those. */ - if (flags & PURPLE_MESSAGE_SEND) { - return; - } - buddy = purple_find_buddy(conv->account, who); if (buddy != NULL) { who = purple_buddy_get_name(buddy); } - imcb_chat_msg(gc, who, (char *) message, 0, mtime); + if (conv->type == PURPLE_CONV_TYPE_IM) { + imcb_buddy_msg(ic, (char *) who, (char *) message, bee_flags, mtime); + } else if (gc) { + imcb_chat_msg(gc, who, (char *) message, bee_flags, mtime); + } } -static void prplcb_conv_im(PurpleConversation *conv, const char *who, const char *message, PurpleMessageFlags flags, - time_t mtime) +/* Handles write_im and write_chat. Removes echoes of locally sent messages */ +static void prplcb_conv_msg(PurpleConversation *conv, const char *who, const char *message, PurpleMessageFlags flags, time_t mtime) { - struct im_connection *ic = purple_ic_by_pa(conv->account); - PurpleBuddy *buddy; - - /* ..._SEND means it's an outgoing message, no need to echo those. */ - if (flags & PURPLE_MESSAGE_SEND) { - return; + if (!(flags & PURPLE_MESSAGE_SEND)) { + handle_conv_msg(conv, who, message, 0, mtime); } +} - buddy = purple_find_buddy(conv->account, who); - if (buddy != NULL) { - who = purple_buddy_get_name(buddy); +/* Handles write_conv. Only passes self messages from other locations through. + * That is, only writes of PURPLE_MESSAGE_SEND. + * There are more events which might be handled in the future, but some are tricky. + * (images look like , what do i do with that?) */ +static void prplcb_conv_write(PurpleConversation *conv, const char *who, const char *alias, const char *message, + PurpleMessageFlags flags, time_t mtime) +{ + if (flags & PURPLE_MESSAGE_SEND) { + handle_conv_msg(conv, who, message, OPT_SELFMESSAGE, mtime); } - - imcb_buddy_msg(ic, (char *) who, (char *) message, 0, mtime); } /* No, this is not a ui_op but a signal. */ @@ -1002,9 +1032,9 @@ { prplcb_conv_new, /* create_conversation */ prplcb_conv_free, /* destroy_conversation */ - prplcb_conv_chat_msg, /* write_chat */ - prplcb_conv_im, /* write_im */ - NULL, /* write_conv */ + prplcb_conv_msg, /* write_chat */ + prplcb_conv_msg, /* write_im */ + prplcb_conv_write, /* write_conv */ prplcb_conv_add_users, /* chat_add_users */ NULL, /* chat_rename_user */ prplcb_conv_del_users, /* chat_remove_users */ @@ -1146,7 +1176,18 @@ g_hash_table_insert(pd->input_requests, GUINT_TO_POINTER(id), ri); imcb_add_buddy(ic, ri->buddy, NULL); - imcb_buddy_msg(ic, ri->buddy, secondary, 0, 0); + + if (title && *title) { + imcb_buddy_msg(ic, ri->buddy, title, 0, 0); + } + + if (primary && *primary) { + imcb_buddy_msg(ic, ri->buddy, primary, 0, 0); + } + + if (secondary && *secondary) { + imcb_buddy_msg(ic, ri->buddy, secondary, 0, 0); + } return ri; } @@ -1169,13 +1210,13 @@ static PurpleRequestUiOps bee_request_uiops = { - prplcb_request_input, - NULL, - prplcb_request_action, - NULL, - NULL, - prplcb_close_request, - NULL, + prplcb_request_input, /* request_input */ + NULL, /* request_choice */ + prplcb_request_action, /* request_action */ + NULL, /* request_fields */ + NULL, /* request_file */ + prplcb_close_request, /* close_request */ + NULL, /* request_folder */ }; static void prplcb_privacy_permit_added(PurpleAccount *account, const char *name) @@ -1216,10 +1257,10 @@ static PurplePrivacyUiOps bee_privacy_uiops = { - prplcb_privacy_permit_added, - prplcb_privacy_permit_removed, - prplcb_privacy_deny_added, - prplcb_privacy_deny_removed, + prplcb_privacy_permit_added, /* permit_added */ + prplcb_privacy_permit_removed, /* permit_removed */ + prplcb_privacy_deny_added, /* deny_added */ + prplcb_privacy_deny_removed, /* deny_removed */ }; static void prplcb_debug_print(PurpleDebugLevel level, const char *category, const char *arg_s) @@ -1229,7 +1270,7 @@ static PurpleDebugUiOps bee_debug_uiops = { - prplcb_debug_print, + prplcb_debug_print, /* print */ }; static guint prplcb_ev_timeout_add(guint interval, GSourceFunc func, gpointer udata) @@ -1250,12 +1291,33 @@ static PurpleEventLoopUiOps glib_eventloops = { - prplcb_ev_timeout_add, - prplcb_ev_remove, - prplcb_ev_input_add, - prplcb_ev_remove, + prplcb_ev_timeout_add, /* timeout_add */ + prplcb_ev_remove, /* timeout_remove */ + prplcb_ev_input_add, /* input_add */ + prplcb_ev_remove, /* input_remove */ }; +/* Absolutely no connection context at all. Thanks purple! brb crying */ +static void *prplcb_notify_message(PurpleNotifyMsgType type, const char *title, + const char *primary, const char *secondary) +{ + char *text = g_strdup_printf("%s%s - %s%s%s", + (type == PURPLE_NOTIFY_MSG_ERROR) ? "Error: " : "", + title, + primary ?: "", + (primary && secondary) ? " - " : "", + secondary ?: "" + ); + + if (local_bee->ui->log) { + local_bee->ui->log(local_bee, "purple", text); + } + + g_free(text); + + return NULL; +} + static void *prplcb_notify_email(PurpleConnection *gc, const char *subject, const char *from, const char *to, const char *url) { @@ -1316,13 +1378,13 @@ static PurpleNotifyUiOps bee_notify_uiops = { - NULL, - prplcb_notify_email, - NULL, - NULL, - NULL, - NULL, - prplcb_notify_userinfo, + prplcb_notify_message, /* notify_message */ + prplcb_notify_email, /* notify_email */ + NULL, /* notify_emails */ + NULL, /* notify_formatted */ + NULL, /* notify_searchresults */ + NULL, /* notify_searchresults_new_rows */ + prplcb_notify_userinfo, /* notify_userinfo */ }; static void *prplcb_account_request_authorize(PurpleAccount *account, const char *remote_user, @@ -1349,11 +1411,11 @@ static PurpleAccountUiOps bee_account_uiops = { - NULL, - NULL, - NULL, - prplcb_account_request_authorize, - NULL, + NULL, /* notify_added */ + NULL, /* status_changed */ + NULL, /* request_add */ + prplcb_account_request_authorize, /* request_authorize */ + NULL, /* close_account_request */ }; extern PurpleXferUiOps bee_xfer_uiops; @@ -1381,11 +1443,8 @@ GString *help; char *dir; - if (B_EV_IO_READ != PURPLE_INPUT_READ || - B_EV_IO_WRITE != PURPLE_INPUT_WRITE) { - /* FIXME FIXME FIXME FIXME FIXME :-) */ - exit(1); - } + g_assert((int) B_EV_IO_READ == (int) PURPLE_INPUT_READ); + g_assert((int) B_EV_IO_WRITE == (int) PURPLE_INPUT_WRITE); dir = g_strdup_printf("%s/purple", global.conf->configdir); purple_util_set_user_dir(dir); @@ -1403,6 +1462,7 @@ if (proxytype != PROXY_NONE) { PurpleProxyInfo *pi = purple_global_proxy_get_info(); switch (proxytype) { + case PROXY_SOCKS4A: case PROXY_SOCKS4: purple_proxy_info_set_type(pi, PURPLE_PROXY_SOCKS4); break; diff -Nru bitlbee-3.4.1/protocols/skype/README bitlbee-3.4.2/protocols/skype/README --- bitlbee-3.4.1/protocols/skype/README 2015-06-16 22:30:40.000000000 +0000 +++ bitlbee-3.4.2/protocols/skype/README 2016-03-19 23:37:33.000000000 +0000 @@ -202,7 +202,7 @@ channel. * `account skype set skypeconsole_receive true` will make the - `skypeconsole` account dump all the recieved raw traffic for you + `skypeconsole` account dump all the received raw traffic for you - If you want to automatically join bookmarked groupchats right after you logged in, do: diff -Nru bitlbee-3.4.1/protocols/skype/skype.c bitlbee-3.4.2/protocols/skype/skype.c --- bitlbee-3.4.1/protocols/skype/skype.c 2015-06-16 22:30:40.000000000 +0000 +++ bitlbee-3.4.2/protocols/skype/skype.c 2016-03-19 23:37:33.000000000 +0000 @@ -20,7 +20,6 @@ */ #define _XOPEN_SOURCE -#define _BSD_SOURCE #include #include #include @@ -187,7 +186,7 @@ char str[IRC_LINE_SIZE]; va_start(args, fmt); - vsnprintf(str, IRC_LINE_SIZE, fmt, args); + g_vsnprintf(str, IRC_LINE_SIZE, fmt, args); va_end(args); return skype_write(ic, str, strlen(str)); @@ -323,6 +322,24 @@ return NULL; } +static struct groupchat *skype_chat_get_or_create(struct im_connection *ic, char *id) +{ + struct skype_data *sd = ic->proto_data; + struct groupchat *gc = bee_chat_by_title(ic->bee, ic, id); + + if (!gc) { + gc = imcb_chat_new(ic, id); + imcb_chat_name_hint(gc, id); + imcb_chat_add_buddy(gc, sd->username); + + skype_printf(ic, "GET CHAT %s ADDER\n", id); + skype_printf(ic, "GET CHAT %s TOPIC\n", id); + skype_printf(ic, "GET CHAT %s ACTIVEMEMBERS\n", id); + } + + return gc; +} + static void skype_parse_users(struct im_connection *ic, char *line) { char **i, **nicks; @@ -687,7 +704,7 @@ } else if (!strncmp(info, "CHATNAME ", 9)) { info += 9; if (sd->handle && sd->body && sd->type) { - struct groupchat *gc = bee_chat_by_title(ic->bee, ic, info); + struct groupchat *gc = skype_chat_get_or_create(ic, info); int i; for (i = 0; i < g_list_length(sd->body); i++) { char *body = g_list_nth_data(sd->body, i); @@ -1025,16 +1042,9 @@ imcb_chat_free(gc); } if (!strcmp(info, "STATUS MULTI_SUBSCRIBED")) { - gc = bee_chat_by_title(ic->bee, ic, id); - if (!gc) { - gc = imcb_chat_new(ic, id); - imcb_chat_name_hint(gc, id); - } - skype_printf(ic, "GET CHAT %s ADDER\n", id); - skype_printf(ic, "GET CHAT %s TOPIC\n", id); + skype_chat_get_or_create(ic, id); } else if (!strcmp(info, "STATUS DIALOG") && sd->groupchat_with) { - gc = imcb_chat_new(ic, id); - imcb_chat_name_hint(gc, id); + gc = skype_chat_get_or_create(ic, id); /* According to the docs this * is necessary. However it * does not seem the situation @@ -1045,11 +1055,8 @@ g_snprintf(buf, IRC_LINE_SIZE, "%s@skype.com", sd->groupchat_with); imcb_chat_add_buddy(gc, buf); - imcb_chat_add_buddy(gc, sd->username); g_free(sd->groupchat_with); sd->groupchat_with = NULL; - skype_printf(ic, "GET CHAT %s ADDER\n", id); - skype_printf(ic, "GET CHAT %s TOPIC\n", id); } else if (!strcmp(info, "STATUS UNSUBSCRIBED")) { gc = bee_chat_by_title(ic->bee, ic, id); if (gc) { @@ -1256,7 +1263,7 @@ lineptr++; } g_strfreev(lines); - } else if (st == 0 || (st < 0 && !sockerr_again())) { + } else if (st == 0 || (st < 0 && !ssl_sockerr_again(sd->ssl))) { ssl_disconnect(sd->ssl); sd->fd = -1; sd->ssl = NULL; diff -Nru bitlbee-3.4.1/protocols/skype/t/added-no-bitlbee.mock bitlbee-3.4.2/protocols/skype/t/added-no-bitlbee.mock --- bitlbee-3.4.1/protocols/skype/t/added-no-bitlbee.mock 2015-06-16 22:30:40.000000000 +0000 +++ bitlbee-3.4.2/protocols/skype/t/added-no-bitlbee.mock 2016-03-19 23:37:33.000000000 +0000 @@ -1,4 +1,4 @@ ->> NOTICE AUTH +>> NOTICE * << NICK alice << USER alice alice localhost :Alice >> PRIVMSG &bitlbee diff -Nru bitlbee-3.4.1/protocols/skype/t/added-yes-bitlbee.mock bitlbee-3.4.2/protocols/skype/t/added-yes-bitlbee.mock --- bitlbee-3.4.1/protocols/skype/t/added-yes-bitlbee.mock 2015-06-16 22:30:40.000000000 +0000 +++ bitlbee-3.4.2/protocols/skype/t/added-yes-bitlbee.mock 2016-03-19 23:37:33.000000000 +0000 @@ -1,4 +1,4 @@ ->> NOTICE AUTH +>> NOTICE * << NICK alice << USER alice alice localhost :Alice >> PRIVMSG &bitlbee diff -Nru bitlbee-3.4.1/protocols/skype/t/add-yes-bitlbee.mock bitlbee-3.4.2/protocols/skype/t/add-yes-bitlbee.mock --- bitlbee-3.4.1/protocols/skype/t/add-yes-bitlbee.mock 2015-06-16 22:30:40.000000000 +0000 +++ bitlbee-3.4.2/protocols/skype/t/add-yes-bitlbee.mock 2016-03-19 23:37:33.000000000 +0000 @@ -1,4 +1,4 @@ ->> NOTICE AUTH +>> NOTICE * << NICK alice << USER alice alice localhost :Alice >> PRIVMSG &bitlbee diff -Nru bitlbee-3.4.1/protocols/skype/t/away-set-bitlbee.mock bitlbee-3.4.2/protocols/skype/t/away-set-bitlbee.mock --- bitlbee-3.4.1/protocols/skype/t/away-set-bitlbee.mock 2015-06-16 22:30:40.000000000 +0000 +++ bitlbee-3.4.2/protocols/skype/t/away-set-bitlbee.mock 2016-03-19 23:37:33.000000000 +0000 @@ -1,4 +1,4 @@ ->> NOTICE AUTH +>> NOTICE * << NICK alice << USER alice alice localhost :Alice >> PRIVMSG &bitlbee diff -Nru bitlbee-3.4.1/protocols/skype/t/call-bitlbee.mock bitlbee-3.4.2/protocols/skype/t/call-bitlbee.mock --- bitlbee-3.4.1/protocols/skype/t/call-bitlbee.mock 2015-06-16 22:30:40.000000000 +0000 +++ bitlbee-3.4.2/protocols/skype/t/call-bitlbee.mock 2016-03-19 23:37:33.000000000 +0000 @@ -1,4 +1,4 @@ ->> NOTICE AUTH +>> NOTICE * << NICK alice << USER alice alice localhost :alice >> PRIVMSG &bitlbee diff -Nru bitlbee-3.4.1/protocols/skype/t/called-no-bitlbee.mock bitlbee-3.4.2/protocols/skype/t/called-no-bitlbee.mock --- bitlbee-3.4.1/protocols/skype/t/called-no-bitlbee.mock 2015-06-16 22:30:40.000000000 +0000 +++ bitlbee-3.4.2/protocols/skype/t/called-no-bitlbee.mock 2016-03-19 23:37:33.000000000 +0000 @@ -1,4 +1,4 @@ ->> NOTICE AUTH +>> NOTICE * << NICK alice << USER alice alice localhost :Alice >> PRIVMSG &bitlbee diff -Nru bitlbee-3.4.1/protocols/skype/t/called-yes-bitlbee.mock bitlbee-3.4.2/protocols/skype/t/called-yes-bitlbee.mock --- bitlbee-3.4.1/protocols/skype/t/called-yes-bitlbee.mock 2015-06-16 22:30:40.000000000 +0000 +++ bitlbee-3.4.2/protocols/skype/t/called-yes-bitlbee.mock 2016-03-19 23:37:33.000000000 +0000 @@ -1,4 +1,4 @@ ->> NOTICE AUTH +>> NOTICE * << NICK alice << USER alice alice localhost :Alice >> PRIVMSG &bitlbee diff -Nru bitlbee-3.4.1/protocols/skype/t/call-failed-bitlbee.mock bitlbee-3.4.2/protocols/skype/t/call-failed-bitlbee.mock --- bitlbee-3.4.1/protocols/skype/t/call-failed-bitlbee.mock 2015-06-16 22:30:40.000000000 +0000 +++ bitlbee-3.4.2/protocols/skype/t/call-failed-bitlbee.mock 2016-03-19 23:37:33.000000000 +0000 @@ -1,4 +1,4 @@ ->> NOTICE AUTH +>> NOTICE * << NICK alice << USER alice alice localhost :alice >> PRIVMSG &bitlbee diff -Nru bitlbee-3.4.1/protocols/skype/t/ctcp-help-bitlbee.mock bitlbee-3.4.2/protocols/skype/t/ctcp-help-bitlbee.mock --- bitlbee-3.4.1/protocols/skype/t/ctcp-help-bitlbee.mock 2015-06-16 22:30:40.000000000 +0000 +++ bitlbee-3.4.2/protocols/skype/t/ctcp-help-bitlbee.mock 2016-03-19 23:37:33.000000000 +0000 @@ -1,4 +1,4 @@ ->> NOTICE AUTH +>> NOTICE * << NICK alice << USER alice alice localhost :Alice >> PRIVMSG &bitlbee diff -Nru bitlbee-3.4.1/protocols/skype/t/filetransfer-bitlbee.mock bitlbee-3.4.2/protocols/skype/t/filetransfer-bitlbee.mock --- bitlbee-3.4.1/protocols/skype/t/filetransfer-bitlbee.mock 2015-06-16 22:30:40.000000000 +0000 +++ bitlbee-3.4.2/protocols/skype/t/filetransfer-bitlbee.mock 2016-03-19 23:37:33.000000000 +0000 @@ -1,4 +1,4 @@ ->> NOTICE AUTH +>> NOTICE * << NICK alice << USER alice alice localhost :Alice >> PRIVMSG &bitlbee diff -Nru bitlbee-3.4.1/protocols/skype/t/group-add-bitlbee.mock bitlbee-3.4.2/protocols/skype/t/group-add-bitlbee.mock --- bitlbee-3.4.1/protocols/skype/t/group-add-bitlbee.mock 2015-06-16 22:30:40.000000000 +0000 +++ bitlbee-3.4.2/protocols/skype/t/group-add-bitlbee.mock 2016-03-19 23:37:33.000000000 +0000 @@ -1,4 +1,4 @@ ->> NOTICE AUTH +>> NOTICE * << NICK alice << USER alice alice localhost :Alice >> PRIVMSG &bitlbee diff -Nru bitlbee-3.4.1/protocols/skype/t/groupchat-invite-bitlbee.mock bitlbee-3.4.2/protocols/skype/t/groupchat-invite-bitlbee.mock --- bitlbee-3.4.1/protocols/skype/t/groupchat-invite-bitlbee.mock 2015-06-16 22:30:40.000000000 +0000 +++ bitlbee-3.4.2/protocols/skype/t/groupchat-invite-bitlbee.mock 2016-03-19 23:37:33.000000000 +0000 @@ -1,4 +1,4 @@ ->> NOTICE AUTH +>> NOTICE * << NICK alice << USER alice alice localhost :Alice >> PRIVMSG &bitlbee @@ -6,6 +6,6 @@ << PRIVMSG &bitlbee :account skype on >> :bob!bob@skype.com JOIN :&bitlbee << PRIVMSG &bitlbee :chat with bob ->> 353 alice = ##alice/$bob;a7ab206ec780 :@alice bob @root -<< INVITE cecil ##alice/$bob;a7ab206ec780 ->> cecil@skype.com JOIN :##alice/$bob;a7ab206ec780 +>> 353 alice = ##alice/$bob;a7ab206ec78 :@alice @root +<< INVITE cecil ##alice/$bob;a7ab206ec78 +>> cecil@skype.com JOIN :##alice/$bob;a7ab206ec78 diff -Nru bitlbee-3.4.1/protocols/skype/t/groupchat-invited-bitlbee.mock bitlbee-3.4.2/protocols/skype/t/groupchat-invited-bitlbee.mock --- bitlbee-3.4.1/protocols/skype/t/groupchat-invited-bitlbee.mock 2015-06-16 22:30:40.000000000 +0000 +++ bitlbee-3.4.2/protocols/skype/t/groupchat-invited-bitlbee.mock 2016-03-19 23:37:33.000000000 +0000 @@ -1,8 +1,8 @@ ->> NOTICE AUTH +>> NOTICE * << NICK alice << USER alice alice localhost :Alice >> PRIVMSG &bitlbee << PRIVMSG &bitlbee :account add skype alice foo << PRIVMSG &bitlbee :account skype on ->> JOIN :##cecil/$bob;4d8cc9965791 ->> 353 alice = ##cecil/$bob;4d8cc9965791 :@alice bob cecil @root +>> JOIN :##cecil/$bob;4d8cc996579 +>> 353 alice = ##cecil/$bob;4d8cc996579 :@alice @root diff -Nru bitlbee-3.4.1/protocols/skype/t/groupchat-invite-skyped.mock bitlbee-3.4.2/protocols/skype/t/groupchat-invite-skyped.mock --- bitlbee-3.4.1/protocols/skype/t/groupchat-invite-skyped.mock 2015-06-16 22:30:40.000000000 +0000 +++ bitlbee-3.4.2/protocols/skype/t/groupchat-invite-skyped.mock 2016-03-19 23:37:33.000000000 +0000 @@ -25,6 +25,8 @@ << CHAT #alice/$bob;a7ab206ec78060f1 NAME #alice/$bob;a7ab206ec78060f1 >> GET CHAT #alice/$bob;a7ab206ec78060f1 TOPIC << CHAT #alice/$bob;a7ab206ec78060f1 TOPIC +>> GET CHAT #alice/$bob;a7ab206ec78060f1 ACTIVEMEMBERS +<< CHAT #alice/$bob;a7ab206ec78060f1 ACTIVEMEMBERS << CHATMESSAGE 206 STATUS SENDING << CHAT #alice/$bob;a7ab206ec78060f1 STATUS DIALOG << CHATMEMBER 204 ROLE USER diff -Nru bitlbee-3.4.1/protocols/skype/t/groupchat-leave-bitlbee.mock bitlbee-3.4.2/protocols/skype/t/groupchat-leave-bitlbee.mock --- bitlbee-3.4.1/protocols/skype/t/groupchat-leave-bitlbee.mock 2015-06-16 22:30:40.000000000 +0000 +++ bitlbee-3.4.2/protocols/skype/t/groupchat-leave-bitlbee.mock 2016-03-19 23:37:33.000000000 +0000 @@ -1,11 +1,11 @@ ->> NOTICE AUTH +>> NOTICE * << NICK alice << USER alice alice localhost :Alice >> PRIVMSG &bitlbee << PRIVMSG &bitlbee :account add skype alice foo << PRIVMSG &bitlbee :account skype set skypeconsole_receive true << PRIVMSG &bitlbee :account skype on ->> JOIN :##cecil/$bob;4d8cc9965791 ->> 353 alice = ##cecil/$bob;4d8cc9965791 :@alice bob cecil @root -<< PART ##cecil/$bob;4d8cc9965791 +>> JOIN :##cecil/$bob;4d8cc996579 +>> 353 alice = ##cecil/$bob;4d8cc996579 :@alice @root +<< PART ##cecil/$bob;4d8cc996579 >> PRIVMSG &bitlbee :alice: CHAT #cecil/$bob;4d8cc9965791c6b9 STATUS UNSUBSCRIBED diff -Nru bitlbee-3.4.1/protocols/skype/t/groupchat-leave-skyped.mock bitlbee-3.4.2/protocols/skype/t/groupchat-leave-skyped.mock --- bitlbee-3.4.1/protocols/skype/t/groupchat-leave-skyped.mock 2015-06-16 22:30:40.000000000 +0000 +++ bitlbee-3.4.2/protocols/skype/t/groupchat-leave-skyped.mock 2016-03-19 23:37:33.000000000 +0000 @@ -35,14 +35,8 @@ << CHAT #cecil/$bob;4d8cc9965791c6b9 ADDER bob >> GET CHAT #cecil/$bob;4d8cc9965791c6b9 TOPIC << CHAT #cecil/$bob;4d8cc9965791c6b9 TOPIC ->> GET CHAT #cecil/$bob;4d8cc9965791c6b9 ADDER -<< CHAT #cecil/$bob;4d8cc9965791c6b9 ADDER bob ->> GET CHAT #cecil/$bob;4d8cc9965791c6b9 TOPIC -<< CHAT #cecil/$bob;4d8cc9965791c6b9 TOPIC ->> GET CHAT #cecil/$bob;4d8cc9965791c6b9 ADDER -<< CHAT #cecil/$bob;4d8cc9965791c6b9 ADDER bob ->> GET CHAT #cecil/$bob;4d8cc9965791c6b9 TOPIC -<< CHAT #cecil/$bob;4d8cc9965791c6b9 TOPIC +>> GET CHAT #cecil/$bob;4d8cc9965791c6b9 ACTIVEMEMBERS +<< CHAT #cecil/$bob;4d8cc9965791c6b9 ACTIVEMEMBERS >> GET CHATMESSAGE 188 FROM_HANDLE << CHATMESSAGE 188 FROM_HANDLE bob >> GET CHATMESSAGE 188 BODY diff -Nru bitlbee-3.4.1/protocols/skype/t/groupchat-msg-bitlbee.mock bitlbee-3.4.2/protocols/skype/t/groupchat-msg-bitlbee.mock --- bitlbee-3.4.1/protocols/skype/t/groupchat-msg-bitlbee.mock 2015-06-16 22:30:40.000000000 +0000 +++ bitlbee-3.4.2/protocols/skype/t/groupchat-msg-bitlbee.mock 2016-03-19 23:37:33.000000000 +0000 @@ -1,11 +1,11 @@ ->> NOTICE AUTH +>> NOTICE * << NICK alice << USER alice alice localhost :Alice >> PRIVMSG &bitlbee << PRIVMSG &bitlbee :account add skype alice foo << PRIVMSG &bitlbee :account skype set skypeconsole_receive true << PRIVMSG &bitlbee :account skype on ->> JOIN :##cecil/$bob;4d8cc9965791 ->> 353 alice = ##cecil/$bob;4d8cc9965791 :@alice bob cecil @root -<< PRIVMSG ##cecil/$bob;4d8cc9965791 :hello +>> JOIN :##cecil/$bob;4d8cc996579 +>> 353 alice = ##cecil/$bob;4d8cc996579 :@alice @root +<< PRIVMSG ##cecil/$bob;4d8cc996579 :hello >> PRIVMSG &bitlbee :alice: CHAT #cecil/$bob;4d8cc9965791c6b9 ACTIVITY_TIMESTAMP diff -Nru bitlbee-3.4.1/protocols/skype/t/groupchat-msg-skyped.mock bitlbee-3.4.2/protocols/skype/t/groupchat-msg-skyped.mock --- bitlbee-3.4.1/protocols/skype/t/groupchat-msg-skyped.mock 2015-06-16 22:30:40.000000000 +0000 +++ bitlbee-3.4.2/protocols/skype/t/groupchat-msg-skyped.mock 2016-03-19 23:37:33.000000000 +0000 @@ -35,14 +35,8 @@ << CHAT #cecil/$bob;4d8cc9965791c6b9 ADDER bob >> GET CHAT #cecil/$bob;4d8cc9965791c6b9 TOPIC << CHAT #cecil/$bob;4d8cc9965791c6b9 TOPIC ->> GET CHAT #cecil/$bob;4d8cc9965791c6b9 ADDER -<< CHAT #cecil/$bob;4d8cc9965791c6b9 ADDER bob ->> GET CHAT #cecil/$bob;4d8cc9965791c6b9 TOPIC -<< CHAT #cecil/$bob;4d8cc9965791c6b9 TOPIC ->> GET CHAT #cecil/$bob;4d8cc9965791c6b9 ADDER -<< CHAT #cecil/$bob;4d8cc9965791c6b9 ADDER bob ->> GET CHAT #cecil/$bob;4d8cc9965791c6b9 TOPIC -<< CHAT #cecil/$bob;4d8cc9965791c6b9 TOPIC +>> GET CHAT #cecil/$bob;4d8cc9965791c6b9 ACTIVEMEMBERS +<< CHAT #cecil/$bob;4d8cc9965791c6b9 ACTIVEMEMBERS bob cecil alice >> GET CHATMESSAGE 188 FROM_HANDLE << CHATMESSAGE 188 FROM_HANDLE bob >> GET CHATMESSAGE 188 BODY diff -Nru bitlbee-3.4.1/protocols/skype/t/groupchat-topic-bitlbee.mock bitlbee-3.4.2/protocols/skype/t/groupchat-topic-bitlbee.mock --- bitlbee-3.4.1/protocols/skype/t/groupchat-topic-bitlbee.mock 2015-06-16 22:30:40.000000000 +0000 +++ bitlbee-3.4.2/protocols/skype/t/groupchat-topic-bitlbee.mock 2016-03-19 23:37:33.000000000 +0000 @@ -1,10 +1,10 @@ ->> NOTICE AUTH +>> NOTICE * << NICK alice << USER alice alice localhost :Alice >> PRIVMSG &bitlbee << PRIVMSG &bitlbee :account add skype alice foo << PRIVMSG &bitlbee :account skype on ->> JOIN :##cecil/$bob;4d8cc9965791 ->> 353 alice = ##cecil/$bob;4d8cc9965791 :@alice bob cecil @root -<< TOPIC ##cecil/$bob;4d8cc9965791 :topic ->> TOPIC ##cecil/$bob;4d8cc9965791 :topic +>> JOIN :##cecil/$bob;4d8cc996579 +>> 353 alice = ##cecil/$bob;4d8cc996579 :@alice @root +<< TOPIC ##cecil/$bob;4d8cc996579 :topic +>> TOPIC ##cecil/$bob;4d8cc996579 :topic diff -Nru bitlbee-3.4.1/protocols/skype/t/group-read-bitlbee.mock bitlbee-3.4.2/protocols/skype/t/group-read-bitlbee.mock --- bitlbee-3.4.1/protocols/skype/t/group-read-bitlbee.mock 2015-06-16 22:30:40.000000000 +0000 +++ bitlbee-3.4.2/protocols/skype/t/group-read-bitlbee.mock 2016-03-19 23:37:33.000000000 +0000 @@ -1,4 +1,4 @@ ->> NOTICE AUTH +>> NOTICE * << NICK alice << USER alice alice localhost :Alice >> PRIVMSG &bitlbee diff -Nru bitlbee-3.4.1/protocols/skype/t/info-bitlbee.mock bitlbee-3.4.2/protocols/skype/t/info-bitlbee.mock --- bitlbee-3.4.1/protocols/skype/t/info-bitlbee.mock 2015-06-16 22:30:40.000000000 +0000 +++ bitlbee-3.4.2/protocols/skype/t/info-bitlbee.mock 2016-03-19 23:37:33.000000000 +0000 @@ -1,4 +1,4 @@ ->> NOTICE AUTH +>> NOTICE * << NICK alice << USER alice alice localhost :Alice >> PRIVMSG &bitlbee diff -Nru bitlbee-3.4.1/protocols/skype/t/login-bitlbee.mock bitlbee-3.4.2/protocols/skype/t/login-bitlbee.mock --- bitlbee-3.4.1/protocols/skype/t/login-bitlbee.mock 2015-06-16 22:30:40.000000000 +0000 +++ bitlbee-3.4.2/protocols/skype/t/login-bitlbee.mock 2016-03-19 23:37:33.000000000 +0000 @@ -1,4 +1,4 @@ ->> NOTICE AUTH +>> NOTICE * << NICK alice << USER alice alice localhost :Alice >> PRIVMSG &bitlbee diff -Nru bitlbee-3.4.1/protocols/skype/t/msg-bitlbee.mock bitlbee-3.4.2/protocols/skype/t/msg-bitlbee.mock --- bitlbee-3.4.1/protocols/skype/t/msg-bitlbee.mock 2015-06-16 22:30:40.000000000 +0000 +++ bitlbee-3.4.2/protocols/skype/t/msg-bitlbee.mock 2016-03-19 23:37:33.000000000 +0000 @@ -1,4 +1,4 @@ ->> NOTICE AUTH +>> NOTICE * << NICK alice << USER alice alice localhost :Alice >> PRIVMSG &bitlbee diff -Nru bitlbee-3.4.1/protocols/skype/t/set-mood-text-bitlbee.mock bitlbee-3.4.2/protocols/skype/t/set-mood-text-bitlbee.mock --- bitlbee-3.4.1/protocols/skype/t/set-mood-text-bitlbee.mock 2015-06-16 22:30:40.000000000 +0000 +++ bitlbee-3.4.2/protocols/skype/t/set-mood-text-bitlbee.mock 2016-03-19 23:37:33.000000000 +0000 @@ -1,4 +1,4 @@ ->> NOTICE AUTH +>> NOTICE * << NICK alice << USER alice alice localhost :Alice >> PRIVMSG &bitlbee diff -Nru bitlbee-3.4.1/protocols/twitter/twitter.c bitlbee-3.4.2/protocols/twitter/twitter.c --- bitlbee-3.4.1/protocols/twitter/twitter.c 2015-06-16 22:30:40.000000000 +0000 +++ bitlbee-3.4.2/protocols/twitter/twitter.c 2016-03-19 23:37:33.000000000 +0000 @@ -468,16 +468,11 @@ g_regex_match(regex, msg, 0, &match_info); while (g_match_info_matches(match_info)) { - gchar *s, *url; + gchar *url; url = g_match_info_fetch(match_info, 2); url_len_diff += target_len - g_utf8_strlen(url, -1); - /* Add another character for https://t.co/... URLs */ - if ((s = g_match_info_fetch(match_info, 3))) { - url_len_diff += 1; - g_free(s); - } g_free(url); g_match_info_next(match_info, NULL); } @@ -540,7 +535,7 @@ if (strcmp(acc->prpl->name, "twitter") == 0) { def_url = TWITTER_API_URL; - def_tul = "22"; + def_tul = "23"; def_mentions = "true"; } else { /* if( strcmp( acc->prpl->name, "identica" ) == 0 ) */ def_url = IDENTICA_API_URL; @@ -677,14 +672,14 @@ // Set the status to logged out. ic->flags &= ~OPT_LOGGED_IN; - // Remove the main_loop function from the function queue. - b_event_remove(td->main_loop_id); + if (td) { + // Remove the main_loop function from the function queue. + b_event_remove(td->main_loop_id); - if (td->timeline_gc) { - imcb_chat_free(td->timeline_gc); - } + if (td->timeline_gc) { + imcb_chat_free(td->timeline_gc); + } - if (td) { if (td->filter_update_id > 0) { b_event_remove(td->filter_update_id); } diff -Nru bitlbee-3.4.1/protocols/twitter/twitter_http.c bitlbee-3.4.2/protocols/twitter/twitter_http.c --- bitlbee-3.4.1/protocols/twitter/twitter_http.c 2015-06-16 22:30:40.000000000 +0000 +++ bitlbee-3.4.2/protocols/twitter/twitter_http.c 2016-03-19 23:37:33.000000000 +0000 @@ -23,7 +23,7 @@ /***************************************************************************\ * * -* Some funtions within this file have been copied from other files within * +* Some functions within this file have been copied from other files within * * BitlBee. * * * ****************************************************************************/ @@ -52,7 +52,7 @@ struct twitter_data *td = ic->proto_data; char *tmp; GString *request = g_string_new(""); - void *ret; + void *ret = NULL; char *url_arguments; url_t *base_url = NULL; @@ -71,8 +71,7 @@ if (strstr(url_string, "://")) { base_url = g_new0(url_t, 1); if (!url_set(base_url, url_string)) { - g_free(base_url); - return NULL; + goto error; } } @@ -131,6 +130,7 @@ ret = http_dorequest(td->url_host, td->url_port, td->url_ssl, request->str, func, data); } +error: g_free(url_arguments); g_string_free(request, TRUE); g_free(base_url); diff -Nru bitlbee-3.4.1/protocols/twitter/twitter_lib.c bitlbee-3.4.2/protocols/twitter/twitter_lib.c --- bitlbee-3.4.1/protocols/twitter/twitter_lib.c 2015-06-16 22:30:40.000000000 +0000 +++ bitlbee-3.4.2/protocols/twitter/twitter_lib.c 2016-03-19 23:37:33.000000000 +0000 @@ -311,14 +311,14 @@ td = ic->proto_data; - txl = g_new0(struct twitter_xml_list, 1); - txl->list = td->follow_ids; - // Parse the data. if (!(parsed = twitter_parse_response(ic, req))) { return; } + txl = g_new0(struct twitter_xml_list, 1); + txl->list = td->follow_ids; + twitter_xt_get_friends_id_list(parsed, txl); json_value_free(parsed); @@ -387,13 +387,14 @@ return; } - txl = g_new0(struct twitter_xml_list, 1); - txl->list = NULL; - // Get the user list from the parsed xml feed. if (!(parsed = twitter_parse_response(ic, req))) { return; } + + txl = g_new0(struct twitter_xml_list, 1); + txl->list = NULL; + twitter_xt_get_users(parsed, txl); json_value_free(parsed); @@ -1384,13 +1385,14 @@ td = ic->proto_data; - txl = g_new0(struct twitter_xml_list, 1); - txl->list = NULL; - // The root node should hold the list of statuses if (!(parsed = twitter_parse_response(ic, req))) { goto end; } + + txl = g_new0(struct twitter_xml_list, 1); + txl->list = NULL; + twitter_xt_get_status_list(ic, parsed, txl); json_value_free(parsed); @@ -1423,13 +1425,14 @@ td = ic->proto_data; - txl = g_new0(struct twitter_xml_list, 1); - txl->list = NULL; - // The root node should hold the list of statuses if (!(parsed = twitter_parse_response(ic, req))) { goto end; } + + txl = g_new0(struct twitter_xml_list, 1); + txl->list = NULL; + twitter_xt_get_status_list(ic, parsed, txl); json_value_free(parsed); diff -Nru bitlbee-3.4.1/protocols/yahoo/libyahoo2.c bitlbee-3.4.2/protocols/yahoo/libyahoo2.c --- bitlbee-3.4.1/protocols/yahoo/libyahoo2.c 2015-06-16 22:30:40.000000000 +0000 +++ bitlbee-3.4.2/protocols/yahoo/libyahoo2.c 2016-03-19 23:37:33.000000000 +0000 @@ -11,7 +11,7 @@ * gaim released under the GNU GPL. This code is also released under the * GNU GPL. * - * This code is derivitive of Gaim + * This code is derivative of Gaim * copyright (C) 1998-1999, Mark Spencer * 1998-1999, Adam Fritzler * 1998-2002, Rob Flynn @@ -1401,7 +1401,7 @@ } /* - * Status updates may be spread accross multiple packets and not + * Status updates may be spread across multiple packets and not * even on buddy boundaries, so keeping some state is important. * So, continue where we left off, and only add a user entry to * the list once it's complete (301-315 End buddy). diff -Nru bitlbee-3.4.1/protocols/yahoo/yahoo2_callbacks.h bitlbee-3.4.2/protocols/yahoo/yahoo2_callbacks.h --- bitlbee-3.4.1/protocols/yahoo/yahoo2_callbacks.h 2015-06-16 22:30:40.000000000 +0000 +++ bitlbee-3.4.2/protocols/yahoo/yahoo2_callbacks.h 2016-03-19 23:37:33.000000000 +0000 @@ -684,7 +684,7 @@ /* * Name: ext_yahoo_connect_async * Connect to a host:port asynchronously. This function should return - * immediately returing a tag used to identify the connection handler, + * immediately returning a tag used to identify the connection handler, * or a pre-connect error (eg: host name lookup failure). * Once the connect completes (successfully or unsuccessfully), callback * should be called (see the signature for yahoo_connect_callback). diff -Nru bitlbee-3.4.1/protocols/yahoo/yahoo.c bitlbee-3.4.2/protocols/yahoo/yahoo.c --- bitlbee-3.4.1/protocols/yahoo/yahoo.c 2015-06-16 22:30:40.000000000 +0000 +++ bitlbee-3.4.2/protocols/yahoo/yahoo.c 2016-03-19 23:37:33.000000000 +0000 @@ -439,8 +439,17 @@ void byahoo_connect_callback(gpointer data, gint source, b_input_condition cond) { struct byahoo_connect_callback_data *d = data; + struct im_connection *ic; - if (!byahoo_get_ic_by_id(d->id)) { + if (!(ic = byahoo_get_ic_by_id(d->id))) { + g_free(d); + return; + } + + if (source == -1) { + d->callback(NULL, 0, d->data); + imcb_error(ic, "Could not connect to server"); + imc_logout(ic, TRUE); g_free(d); return; } diff -Nru bitlbee-3.4.1/README.md bitlbee-3.4.2/README.md --- bitlbee-3.4.1/README.md 2015-06-16 22:30:40.000000000 +0000 +++ bitlbee-3.4.2/README.md 2016-03-19 23:37:33.000000000 +0000 @@ -15,30 +15,40 @@ License: GPLv2 -## Development +## Installation -Use github pull requests against the 'develop' branch to submit patches. +BitlBee is available in the package managers of most distros. -The 'master' branch should be stable enough to be usable by users of the APT repo, but only requires a few days of testing in the 'develop' branch. +For debian/ubuntu/etc you may use the nightly APT repository: http://code.bitlbee.org/debian/ -Building: +You can also use a public server (such as `im.bitlbee.org`) instead of installing it: http://bitlbee.org/main.php/servers.html -``` -./configure --debug=1 -# or, for a local install: -# ./configure --debug=1 --prefix=$HOME/bitlbee --config=$HOME/bitlbee --pidfile=$HOME/bitlbee/bitlbee.pid +## Compiling -# Also try --asan=1 for AddressSanitizer +If you wish to compile it yourself, ensure you have the following packages and their headers: -make +* glib 2.16 or newer (not to be confused with glibc) +* gnutls +* python 2 or 3 (for the user guide) -BITLBEE_DEBUG=1 ./bitlbee -Dnv -``` +Some optional features have additional dependencies, such as libpurple, libotr, libevent, etc. +NSS and OpenSSL are also available but not as well supported as GnuTLS. -See ./doc/README and ./doc/HACKING for more details. +Once you have the dependencies, building should be a matter of: -Mappings of bzr revisions to git commits (for historical purposes) are available in ./doc/git-bzr-rev-map + ./configure + make + sudo make install + +## Development tips + +* To enable debug symbols: `./configure --debug=1` +* To get some additional debug output for some protocols: `BITLBEE_DEBUG=1 ./bitlbee -Dnv` +* Use github pull requests against the 'develop' branch to submit patches. +* The coding style based on K&R with tabs and 120 columns. See `./doc/uncrustify.cfg` for the parameters used to reformat the code. +* Mappings of bzr revisions to git commits (for historical purposes) are available in `./doc/git-bzr-rev-map` +* See also `./doc/README` and `./doc/HACKING` ## Help? -Join **#BitlBee** on OFTC (**irc.oftc.net**) (OFTC, *not* FreeNode!) and flame us right in the face. :-) +Join **#BitlBee** on OFTC (**irc.oftc.net**) (OFTC, *not* FreeNode!) diff -Nru bitlbee-3.4.1/root_commands.c bitlbee-3.4.2/root_commands.c --- bitlbee-3.4.1/root_commands.c 2015-06-16 22:30:40.000000000 +0000 +++ bitlbee-3.4.2/root_commands.c 2016-03-19 23:37:33.000000000 +0000 @@ -162,6 +162,12 @@ irc->status |= USTATUS_IDENTIFIED; irc_umode_set(irc, "+R", 1); + if (irc->caps & CAP_SASL) { + irc_user_t *iu = irc->user; + irc_send_num(irc, 900, "%s!%s@%s %s :You are now logged in as %s", + iu->nick, iu->user, iu->host, iu->nick, iu->nick); + } + bitlbee_whatsnew(irc); /* The following code is a bit hairy now. With takeover @@ -238,6 +244,12 @@ irc->status |= USTATUS_IDENTIFIED; irc_umode_set(irc, "+R", 1); + if (irc->caps & CAP_SASL) { + irc_user_t *iu = irc->user; + irc_send_num(irc, 900, "%s!%s@%s %s :You are now logged in as %s", + iu->nick, iu->user, iu->host, iu->nick, iu->nick); + } + /* Set this var now, or anyone who logs in to his/her newly created account for the first time gets the whatsnew story. */ @@ -412,7 +424,11 @@ prpl = find_protocol(cmd[2]); if (prpl == NULL) { - irc_rootmsg(irc, "Unknown protocol"); + if (is_protocol_disabled(cmd[2])) { + irc_rootmsg(irc, "Protocol disabled in global config"); + } else { + irc_rootmsg(irc, "Unknown protocol"); + } return; } @@ -1221,6 +1237,7 @@ static void cmd_transfer(irc_t *irc, char **cmd) { GSList *files = irc->file_transfers; + GSList *next; enum { LIST, REJECT, CANCEL }; int subcmd = LIST; @@ -1238,7 +1255,8 @@ subcmd = CANCEL; } - for (; files; files = g_slist_next(files)) { + for (; files; files = next) { + next = files->next; file_transfer_t *file = files->data; switch (subcmd) { diff -Nru bitlbee-3.4.1/tests/check.c bitlbee-3.4.2/tests/check.c --- bitlbee-3.4.1/tests/check.c 2015-06-16 22:30:40.000000000 +0000 +++ bitlbee-3.4.2/tests/check.c 2016-03-19 23:37:33.000000000 +0000 @@ -43,6 +43,11 @@ return((double) time->tv_sec + (double) time->tv_usec / 1000000); } +void sighandler_shutdown_setup() +{ + /* no-op. originally defined in unix.c, needed by bitlbee.c */ +} + /* From check_util.c */ Suite *util_suite(void); diff -Nru bitlbee-3.4.1/tests/check_jabber_util.c bitlbee-3.4.2/tests/check_jabber_util.c --- bitlbee-3.4.1/tests/check_jabber_util.c 2015-06-16 22:30:40.000000000 +0000 +++ bitlbee-3.4.2/tests/check_jabber_util.c 2016-03-19 23:37:33.000000000 +0000 @@ -36,7 +36,7 @@ fail_unless(jabber_buddy_by_jid(ic, "WILMER@GAAST.NET/BitlBee", GET_BUDDY_EXACT) == budw1); fail_unless(jabber_buddy_by_jid(ic, "wilmer@GAAST.NET/BitlBee", GET_BUDDY_CREAT) == budw1); - fail_if(jabber_buddy_by_jid(ic, "wilmer@gaast.net", GET_BUDDY_EXACT)); + fail_unless(jabber_buddy_by_jid(ic, "wilmer@gaast.net", GET_BUDDY_EXACT)); fail_unless(jabber_buddy_by_jid(ic, "WILMER@gaast.net", 0) == budw3); /* Check O_FIRST and see if it's indeed the first item from the list. */ @@ -104,6 +104,25 @@ fail_if(jabber_compare_jid("", "bugtest@google.com/A")); } +static void check_hipchat_slug(int l) +{ + int i; + + const char *tests[] = { + "test !\"#$%&\'()*+,-./0123456789:;<=>?@ABC", "test_!#$%\()*+,-.0123456789;=?abc", + "test XYZ[\\]^_`abc", "test_xyz[\\]^_`abc", + "test {|}~¡¢£¤¥¦§¨©ª«¬\xad®¯°±²³´µ¶·¸¹º»¼½¾¿ÀÁÂÃÄÅÆ", "test_{|}~¡¢£¤¥¦§¨©ª«¬\xad®¯°±²³´µ¶·¸¹º»¼½¾¿àáâãäåæ", + "test IJ ij I ı I ı", "test_ij_ij_i_ı_i_ı", + NULL, + }; + + for (i = 0; tests[i]; i += 2) { + char *new = hipchat_make_channel_slug(tests[i]); + fail_unless(!strcmp(tests[i + 1], new)); + g_free(new); + } +} + Suite *jabber_util_suite(void) { Suite *s = suite_create("jabber/util"); @@ -120,5 +139,6 @@ suite_add_tcase(s, tc_core); tcase_add_test(tc_core, check_buddy_add); tcase_add_test(tc_core, check_compareJID); + tcase_add_test(tc_core, check_hipchat_slug); return s; } diff -Nru bitlbee-3.4.1/tests/check_user.c bitlbee-3.4.2/tests/check_user.c --- bitlbee-3.4.1/tests/check_user.c 2015-06-16 22:30:40.000000000 +0000 +++ bitlbee-3.4.2/tests/check_user.c 2016-03-19 23:37:33.000000000 +0000 @@ -45,7 +45,7 @@ fail_unless(user_find(irc, "foo") == NULL); END_TEST -START_TEST(test_user_del_nonexistant) +START_TEST(test_user_del_nonexistent) irc_t * irc = torture_irc(); fail_unless(user_del(irc, "foo") == 0); END_TEST @@ -70,7 +70,7 @@ tcase_add_test(tc_core, test_user_add_invalid); tcase_add_test(tc_core, test_user_add_exists); tcase_add_test(tc_core, test_user_del_invalid); - tcase_add_test(tc_core, test_user_del_nonexistant); + tcase_add_test(tc_core, test_user_del_nonexistent); tcase_add_test(tc_core, test_user_del); tcase_add_test(tc_core, test_user_rename); #endif diff -Nru bitlbee-3.4.1/tests/Makefile bitlbee-3.4.2/tests/Makefile --- bitlbee-3.4.1/tests/Makefile 2015-06-16 22:30:40.000000000 +0000 +++ bitlbee-3.4.2/tests/Makefile 2016-03-19 23:37:33.000000000 +0000 @@ -14,7 +14,7 @@ distclean: clean -main_objs = bitlbee.o conf.o dcc.o help.o ipc.o irc.o irc_channel.o irc_commands.o irc_im.o irc_send.o irc_user.o irc_util.o irc_commands.o log.o nick.o query.o root_commands.o set.o storage.o storage_xml.o +main_objs = bitlbee.o conf.o dcc.o help.o ipc.o irc.o irc_cap.o irc_channel.o irc_commands.o irc_im.o irc_send.o irc_user.o irc_util.o irc_commands.o log.o nick.o query.o root_commands.o set.o storage.o storage_xml.o test_objs = check.o check_util.o check_nick.o check_md5.o check_arc.o check_irc.o check_help.o check_user.o check_set.o check_jabber_sasl.o check_jabber_util.o diff -Nru bitlbee-3.4.1/.travis.yml bitlbee-3.4.2/.travis.yml --- bitlbee-3.4.1/.travis.yml 2015-06-16 22:30:40.000000000 +0000 +++ bitlbee-3.4.2/.travis.yml 2016-03-19 23:37:33.000000000 +0000 @@ -1,23 +1,33 @@ +sudo: false language: c script: - ./configure - make check - - BITLBEE_SKYPE=plugin dpkg-buildpackage -uc -us + - BITLBEE_SKYPE=plugin dpkg-buildpackage -uc -us -d +# ubuntu precise doesn't have libotr5, so extract a prebuilt version to ~/otr before_install: - - sudo apt-get update -qq - - sudo apt-get install --no-install-recommends -qq libevent-dev libpurple-dev check - - wget http://dump.dequis.org/indexed/bitlbee-travis-libs/libotr5{,-dev}_4.1.0-2~bpo70+1_amd64.deb - - sudo dpkg -i *.deb + - wget http://dump.dequis.org/indexed/bitlbee-travis-libs/libotr5_4.1.0_amd64_with_dev_for_travis.tar.gz -O /tmp/otr.tar.gz + - echo "8424feb28a2cff3ce603ddcdff9be788701ff7e4 /tmp/otr.tar.gz" | sha1sum -c - + - tar -C "$HOME" -xf /tmp/otr.tar.gz + - sed -i "s#/usr#$HOME/otr/usr/#" "$HOME/otr/usr/lib/pkgconfig/libotr.pc" + - echo "libotr 5 libotr" > debian/shlibs.local env: global: + - PKG_CONFIG_PATH=$HOME/otr/usr/lib/pkgconfig/ + - LD_LIBRARY_PATH=$HOME/otr/usr/lib/ # The next declaration is the encrypted COVERITY_SCAN_TOKEN, created # via the "travis encrypt" command using the project repo's public key - secure: "MO6hy2cRxR02A0nSenfQFyPFpepxorJ+XgNkq2JS7LtI6tcwYRR0alvunIPJXam1/OUKxoFsBJLS1nCJTvEUXFCOvoTSAoMiePTBUEg2zfzcTb5k+cqtcOUznCXHNmXAwrqriP4vkG+57ijO9Ojz2r9LijcvjtFDRFJQY9Rcs38=" addons: + apt: + packages: + - libevent-dev + - libpurple-dev + - check coverity_scan: project: name: "bitlbee/bitlbee" diff -Nru bitlbee-3.4.1/unix.c bitlbee-3.4.2/unix.c --- bitlbee-3.4.1/unix.c 2015-06-16 22:30:40.000000000 +0000 +++ bitlbee-3.4.2/unix.c 2016-03-19 23:37:33.000000000 +0000 @@ -47,7 +47,11 @@ global_t global; /* Against global namespace pollution */ -static int signal_shutdown_pipe[2] = { -1, -1 }; +static struct { + int fd[2]; + int tag; +} shutdown_pipe = {{-1 , -1}, 0}; + static void sighandler_shutdown(int signal); static void sighandler_crash(int signal); @@ -155,13 +159,11 @@ sig.sa_handler = sighandler_crash; sigaction(SIGSEGV, &sig, &old); - /* Use a pipe for SIGTERM/SIGINT so the actual signal handler doesn't do anything unsafe */ - if (pipe(signal_shutdown_pipe) == 0) { - b_input_add(signal_shutdown_pipe[0], B_EV_IO_READ, bitlbee_shutdown, NULL); - sig.sa_handler = sighandler_shutdown; - sigaction(SIGINT, &sig, &old); - sigaction(SIGTERM, &sig, &old); - } + sighandler_shutdown_setup(); + + sig.sa_handler = sighandler_shutdown; + sigaction(SIGINT, &sig, &old); + sigaction(SIGTERM, &sig, &old); if (!getuid() || !geteuid()) { log_message(LOGLVL_WARNING, "BitlBee is running with root privileges. Why?"); @@ -255,12 +257,27 @@ return 0; } +/* Set up a pipe for SIGTERM/SIGINT so the actual signal handler doesn't do anything unsafe */ +void sighandler_shutdown_setup() +{ + if (shutdown_pipe.fd[0] != -1) { + /* called again from a forked process, clean up to avoid propagating the signal */ + b_event_remove(shutdown_pipe.tag); + close(shutdown_pipe.fd[0]); + close(shutdown_pipe.fd[1]); + } + + if (pipe(shutdown_pipe.fd) == 0) { + shutdown_pipe.tag = b_input_add(shutdown_pipe.fd[0], B_EV_IO_READ, bitlbee_shutdown, NULL); + } +} + /* Signal handler for SIGTERM and SIGINT */ static void sighandler_shutdown(int signal) { /* Write a single null byte to the pipe, just to send a message to the main loop. * This gets handled by bitlbee_shutdown (the b_input_add callback for this pipe) */ - write(signal_shutdown_pipe[1], "", 1); + write(shutdown_pipe.fd[1], "", 1); } /* Signal handler for SIGSEGV @@ -274,6 +291,7 @@ for (l = irc_connection_list; l; l = l->next) { irc_t *irc = l->data; + sock_make_blocking(irc->fd); write(irc->fd, message, len); }