Changeset 3fbce97


Ignore:
Timestamp:
2016-09-24T20:14:34Z (8 years ago)
Author:
Wilmer van der Gaast <wilmer@…>
Children:
ba52ac5
Parents:
63cad66 (diff), 82cb190 (diff)
Note: this is a merge changeset, the changes displayed below correspond to the merge itself.
Use the (diff) links above to see all the changes relative to each parent.
Message:

Merge branch 'master' into parson

Files:
2 added
117 edited

Legend:

Unmodified
Added
Removed
  • .travis.yml

    r63cad66 r3fbce97  
     1sudo: false
    12language: c
    23
     
    45 - ./configure
    56 - make check
    6  - BITLBEE_SKYPE=plugin dpkg-buildpackage -uc -us
     7 - BITLBEE_SKYPE=plugin dpkg-buildpackage -uc -us -d
    78
     9# ubuntu precise doesn't have libotr5, so extract a prebuilt version to ~/otr
    810before_install:
    9  - sudo apt-get update -qq
    10  - sudo apt-get install --no-install-recommends -qq libevent-dev libpurple-dev check
    11  - wget http://dump.dequis.org/indexed/bitlbee-travis-libs/libotr5{,-dev}_4.1.0-2~bpo70+1_amd64.deb
    12  - sudo dpkg -i *.deb
     11 - 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
     12 - echo "8424feb28a2cff3ce603ddcdff9be788701ff7e4  /tmp/otr.tar.gz" | sha1sum -c -
     13 - tar -C "$HOME" -xf /tmp/otr.tar.gz
     14 - sed -i "s#/usr#$HOME/otr/usr/#" "$HOME/otr/usr/lib/pkgconfig/libotr.pc"
     15 - echo "libotr 5 libotr" > debian/shlibs.local
    1316
    1417env:
    1518  global:
     19   - PKG_CONFIG_PATH=$HOME/otr/usr/lib/pkgconfig/
     20   - LD_LIBRARY_PATH=$HOME/otr/usr/lib/
    1621   # The next declaration is the encrypted COVERITY_SCAN_TOKEN, created
    1722   #   via the "travis encrypt" command using the project repo's public key
     
    1924
    2025addons:
     26  apt:
     27    packages:
     28    - libevent-dev
     29    - libpurple-dev
     30    - check
    2131  coverity_scan:
    2232    project:
  • Makefile

    r63cad66 r3fbce97  
    1010
    1111# Program variables
    12 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
     12objects = 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
    1313headers = $(wildcard $(_SRCDIR_)*.h $(_SRCDIR_)lib/*.h $(_SRCDIR_)protocols/*.h)
    1414subdirs = lib protocols
     
    3030
    3131install: install-bin install-doc install-plugins
     32        @echo
     33        @echo Installed successfully
     34        @echo
    3235        @if ! [ -d $(DESTDIR)$(CONFIG) ]; then echo -e '\nThe configuration directory $(DESTDIR)$(CONFIG) does not exist yet, don'\''t forget to create it!'; fi
    3336        @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
     
    3538        @echo If you want to start BitlBee using systemd, try \"make install-systemd\".
    3639endif
    37         @echo
    38 
    39 .PHONY:   install   install-bin   install-etc   install-doc install-plugins install-systemd \
    40         uninstall uninstall-bin uninstall-etc uninstall-doc \
     40        @echo To be able to compile third party plugins, run \"make install-dev\"
     41        @echo
     42
     43.PHONY:   install   install-bin   install-etc   install-doc install-plugins install-systemd install-dev \
     44        uninstall uninstall-bin uninstall-etc uninstall-doc uninstall-etc \
    4145        all clean distclean tar $(subdirs) doc
    4246
  • README.md

    r63cad66 r3fbce97  
    1616License: GPLv2
    1717
    18 ## Development
     18## Installation
    1919
    20 Use github pull requests against the 'develop' branch to submit patches.
     20BitlBee is available in the package managers of most distros.
    2121
    22 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.
     22For debian/ubuntu/etc you may use the nightly APT repository: http://code.bitlbee.org/debian/
    2323
    24 Building:
     24You can also use a public server (such as `im.bitlbee.org`) instead of installing it: http://bitlbee.org/main.php/servers.html
    2525
    26 ```
    27 ./configure --debug=1
    28 # or, for a local install:
    29 # ./configure --debug=1 --prefix=$HOME/bitlbee --config=$HOME/bitlbee --pidfile=$HOME/bitlbee/bitlbee.pid
     26## Compiling
    3027
    31 # Also try --asan=1 for AddressSanitizer
     28If you wish to compile it yourself, ensure you have the following packages and their headers:
    3229
    33 make
     30* glib 2.16 or newer (not to be confused with glibc)
     31* gnutls
     32* python 2 or 3 (for the user guide)
    3433
    35 BITLBEE_DEBUG=1 ./bitlbee -Dnv
    36 ```
     34Some optional features have additional dependencies, such as libpurple, libotr, libevent, etc.
     35NSS and OpenSSL are also available but not as well supported as GnuTLS.
    3736
    38 See ./doc/README and ./doc/HACKING for more details.
     37Once you have the dependencies, building should be a matter of:
    3938
    40 Mappings of bzr revisions to git commits (for historical purposes) are available in ./doc/git-bzr-rev-map
     39    ./configure
     40    make
     41    sudo make install
     42
     43## Development tips
     44
     45* To enable debug symbols: `./configure --debug=1`
     46* To get some additional debug output for some protocols: `BITLBEE_DEBUG=1 ./bitlbee -Dnv`
     47* Use github pull requests against the 'develop' branch to submit patches.
     48* The coding style based on K&R with tabs and 120 columns. See `./doc/uncrustify.cfg` for the parameters used to reformat the code.
     49* Mappings of bzr revisions to git commits (for historical purposes) are available in `./doc/git-bzr-rev-map`
     50* See also `./doc/README` and `./doc/HACKING`
    4151
    4252## Help?
    4353
    44 Join **#BitlBee** on OFTC (**irc.oftc.net**) (OFTC, *not* FreeNode!) and flame us right in the face. :-)
     54Join **#BitlBee** on OFTC (**irc.oftc.net**) (OFTC, *not* FreeNode!)
  • bitlbee.c

    r63cad66 r3fbce97  
    311311                        b_event_remove(global.listen_watch_source_id);
    312312
     313                        /* Make a new pipe for the shutdown signal handler */
     314                        sighandler_shutdown_setup();
     315
    313316                        /* Make the connection. */
    314317                        irc = irc_new(new_socket);
  • bitlbee.h

    r63cad66 r3fbce97  
    158158} global_t;
    159159
     160void sighandler_shutdown_setup(void);
     161
    160162int bitlbee_daemon_init(void);
    161163int bitlbee_inetd_init(void);
  • conf.c

    r63cad66 r3fbce97  
    3737
    3838static int conf_loadini(conf_t *conf, char *file);
     39static void conf_free(conf_t *conf);
    3940
    4041conf_t *conf_load(int argc, char *argv[])
     
    7374        if (i == 0) {
    7475                fprintf(stderr, "Error: Syntax error in configuration file `%s'.\n", global.conf_file);
     76                conf_free(conf);
    7577                return NULL;
    7678        } else if (i == -1) {
     
    104106                                g_free(global.conf_file);
    105107                                global.conf_file = g_strdup(optarg);
    106                                 g_free(conf);
     108                                conf_free(conf);
    107109                                /* Re-evaluate arguments. Don't use this option twice,
    108110                                   you'll end up in an infinite loop! Hope this trick
     
    135137                               "  -h  Show this help page.\n"
    136138                               "  -V  Show version info.\n");
     139                        conf_free(conf);
    137140                        return NULL;
    138141                } else if (opt == 'V') {
    139                         printf("BitlBee %s\nAPI version %06x\n",
    140                                BITLBEE_VERSION, BITLBEE_VERSION_CODE);
     142                        printf("BitlBee %s\nAPI version %06x\nConfigure args: %s\n",
     143                               BITLBEE_VERSION, BITLBEE_VERSION_CODE, BITLBEE_CONFIGURE_ARGS);
     144                        conf_free(conf);
    141145                        return NULL;
    142146                } else if (opt == 'u') {
     
    162166                   they're secure when in fact they're not. */
    163167                fprintf(stderr, "Error: Could not read CA file %s: %s\n", conf->cafile, strerror(errno));
     168                conf_free(conf);
    164169                return NULL;
    165170        }
    166171
    167172        return conf;
     173}
     174
     175static void conf_free(conf_t *conf)
     176{
     177        /* Free software means users have the four essential freedoms:
     178           0. to run the program,
     179           2. to study and change the program in source code form,
     180           2. to redistribute exact copies, and
     181           3. to distribute modified versions
     182        */
     183        g_free(conf->auth_pass);
     184        g_free(conf->cafile);
     185        g_free(conf->configdir);
     186        g_free(conf->ft_listen);
     187        g_free(conf->hostname);
     188        g_free(conf->iface_in);
     189        g_free(conf->iface_out);
     190        g_free(conf->motdfile);
     191        g_free(conf->oper_pass);
     192        g_free(conf->pidfile);
     193        g_free(conf->plugindir);
     194        g_free(conf->port);
     195        g_free(conf->primary_storage);
     196        g_free(conf->user);
     197        g_strfreev(conf->migrate_storage);
     198        g_strfreev(conf->protocols);
     199        g_free(conf);
     200
    168201}
    169202
     
    219252                                g_free(conf->configdir);
    220253                                conf->configdir = g_strdup(ini->value);
     254                        } else if (g_strcasecmp(ini->key, "plugindir") == 0) {
     255                                g_free(conf->plugindir);
     256                                conf->plugindir = g_strdup(ini->value);
    221257                        } else if (g_strcasecmp(ini->key, "motdfile") == 0) {
    222258                                g_free(conf->motdfile);
     
    259295                                } else if (url->proto == PROTO_SOCKS5) {
    260296                                        proxytype = PROXY_SOCKS5;
     297                                } else if (url->proto == PROTO_SOCKS4A) {
     298                                        proxytype = PROXY_SOCKS4A;
    261299                                }
    262300
  • configure

    r63cad66 r3fbce97  
    2424pcdir='$prefix/lib/pkgconfig'
    2525systemlibdirs="/lib64 /usr/lib64 /usr/local/lib64 /lib /usr/lib /usr/local/lib"
     26sysroot=''
     27
     28configure_args="$@"
    2629
    2730# Set these to default-on to let it be overriden by either the user or purple
     
    5356pie=1
    5457
    55 arch=`uname -s`
    56 cpu=`uname -m`
     58arch=$(uname -s)
     59cpu=$(uname -m)
    5760
    5861GLIB_MIN_VERSION=2.16
     
    105108
    106109while [ -n "$1" ]; do
    107         e="`expr "X$1" : 'X--\(.*=.*\)'`"
     110        e="$(expr "X$1" : 'X--\(.*=.*\)')"
    108111        if [ -z "$e" ]; then
    109112                cat<<EOF
     
    153156
    154157--target=...    Cross compilation target                same as host
     158--sysroot=...   Cross compilation sysroot               $sysroot
    155159EOF
    156160                exit;
     
    161165
    162166# Expand $prefix and get rid of double slashes
    163 bindir=`eval echo "$bindir/" | sed 's/\/\{1,\}/\//g'`
    164 sbindir=`eval echo "$sbindir/" | sed 's/\/\{1,\}/\//g'`
    165 etcdir=`eval echo "$etcdir/" | sed 's/\/\{1,\}/\//g'`
    166 mandir=`eval echo "$mandir/" | sed 's/\/\{1,\}/\//g'`
    167 datadir=`eval echo "$datadir/" | sed 's/\/\{1,\}/\//g'`
    168 config=`eval echo "$config/" | sed 's/\/\{1,\}/\//g'`
    169 plugindir=`eval echo "$plugindir/" | sed 's/\/\{1,\}/\//g'`
     167bindir=$(eval echo "$bindir/" | sed 's/\/\{1,\}/\//g')
     168sbindir=$(eval echo "$sbindir/" | sed 's/\/\{1,\}/\//g')
     169etcdir=$(eval echo "$etcdir/" | sed 's/\/\{1,\}/\//g')
     170mandir=$(eval echo "$mandir/" | sed 's/\/\{1,\}/\//g')
     171datadir=$(eval echo "$datadir/" | sed 's/\/\{1,\}/\//g')
     172config=$(eval echo "$config/" | sed 's/\/\{1,\}/\//g')
     173plugindir=$(eval echo "$plugindir/" | sed 's/\/\{1,\}/\//g')
    170174rpcplugindir=$(eval echo "$rpcplugindir/" | sed 's/\/\{1,\}/\//g')
    171 includedir=`eval echo "$includedir"/ | sed 's/\/\{1,\}/\//g'`
    172 libevent=`eval echo "$libevent"/ | sed 's/\/\{1,\}/\//g'`
    173 
    174 pidfile=`eval echo "$pidfile" | sed 's/\/\{1,\}/\//g'`
    175 ipcsocket=`eval echo "$ipcsocket" | sed 's/\/\{1,\}/\//g'`
    176 pcdir=`eval echo "$pcdir" | sed 's/\/\{1,\}/\//g'`
     175includedir=$(eval echo "$includedir"/ | sed 's/\/\{1,\}/\//g')
     176libevent=$(eval echo "$libevent"/ | sed 's/\/\{1,\}/\//g')
     177
     178pidfile=$(eval echo "$pidfile" | sed 's/\/\{1,\}/\//g')
     179ipcsocket=$(eval echo "$ipcsocket" | sed 's/\/\{1,\}/\//g')
     180pcdir=$(eval echo "$pcdir" | sed 's/\/\{1,\}/\//g')
    177181
    178182protocols_mods=""
     
    180184cat <<EOF >Makefile.settings
    181185## BitlBee settings, generated by configure
     186
     187# ./configure $configure_args
     188
    182189PREFIX=$prefix
    183190BINDIR=$bindir
     
    233240   of them can/will be overridden at run-time */
    234241
     242#define BITLBEE_CONFIGURE_ARGS "$configure_args"
     243
    235244#define CONFIG "$config"
    236245#define ETCDIR "$etcdir"
     
    247256
    248257if [ -n "$target" ]; then
    249         PKG_CONFIG_LIBDIR=/usr/$target/lib/pkgconfig
    250         export PKG_CONFIG_LIBDIR
    251         PATH=/usr/$target/bin:$PATH
     258        # prepend sysroot to system lib dirs
     259
     260        systemlibdirs_cross=''
     261        for i in $systemlibdirs; do
     262                systemlibdirs_cross="$systemlibdirs_cross $sysroot$i"
     263        done
     264        systemlibdirs=$systemlibdirs_cross
     265        unset systemlibdirs_cross
     266
     267        # backward compatibility
     268
     269        if [ -z "$PKG_CONFIG_LIBDIR" ]; then
     270                PKG_CONFIG_LIBDIR=/usr/$target/lib/pkgconfig
     271                export PKG_CONFIG_LIBDIR
     272        fi
     273
     274        if [ -d /usr/$target/bin ]; then
     275                PATH=/usr/$target/bin:$PATH
     276        fi
     277
     278        if [ -d /usr/$target/lib ]; then
     279                systemlibdirs="$systemlibdirs /usr/$target/lib"
     280        fi
     281
    252282        CC=$target-cc
    253283        LD=$target-ld
    254         systemlibdirs="/usr/$target/lib"
     284        STRIP=$target-strip
    255285fi
    256286
     
    321351        if $PKG_CONFIG glib-2.0 --atleast-version=$GLIB_MIN_VERSION; then
    322352                cat<<EOF >>Makefile.settings
    323 EFLAGS+=`$PKG_CONFIG --libs glib-2.0 gmodule-2.0`
    324 CFLAGS+=`$PKG_CONFIG --cflags glib-2.0 gmodule-2.0`
     353EFLAGS+=$($PKG_CONFIG --libs glib-2.0 gmodule-2.0)
     354CFLAGS+=$($PKG_CONFIG --cflags glib-2.0 gmodule-2.0)
    325355EOF
    326356        else
    327357                echo
    328                 echo 'Found glib2 '`$PKG_CONFIG glib-2.0 --modversion`', but version '$GLIB_MIN_VERSION' or newer is required.'
     358                echo 'Found glib2 '$($PKG_CONFIG glib-2.0 --modversion)', but version '$GLIB_MIN_VERSION' or newer is required.'
    329359                exit 1
    330360        fi
     
    362392        if $PKG_CONFIG --exists gnutls; then
    363393                cat <<EOF >>Makefile.settings
    364 EFLAGS+=`$PKG_CONFIG --libs gnutls` `libgcrypt-config --libs`
    365 CFLAGS+=`$PKG_CONFIG --cflags gnutls` `libgcrypt-config --cflags`
     394EFLAGS+=$($PKG_CONFIG --libs gnutls) $(libgcrypt-config --libs)
     395CFLAGS+=$($PKG_CONFIG --cflags gnutls) $(libgcrypt-config --cflags)
    366396EOF
    367397                ssl=gnutls
    368                 if ! pkg-config gnutls --atleast-version=2.8; then
     398                if ! $PKG_CONFIG gnutls --atleast-version=2.8; then
    369399                        echo
    370400                        echo 'Warning: With GnuTLS versions <2.8, certificate expire dates are not verified.'
     
    373403        elif libgnutls-config --version > /dev/null 2> /dev/null; then
    374404                cat <<EOF >>Makefile.settings
    375 EFLAGS+=`libgnutls-config --libs` `libgcrypt-config --libs`
    376 CFLAGS+=`libgnutls-config --cflags` `libgcrypt-config --cflags`
     405EFLAGS+=$(libgnutls-config --libs) $(libgcrypt-config --libs)
     406CFLAGS+=$(libgnutls-config --cflags) $(libgcrypt-config --cflags)
    377407EOF
    378408               
     
    388418        if $PKG_CONFIG --version > /dev/null 2>/dev/null && $PKG_CONFIG nss; then
    389419                cat<<EOF >>Makefile.settings
    390 EFLAGS+=`$PKG_CONFIG --libs nss`
    391 CFLAGS+=`$PKG_CONFIG --cflags nss`
     420EFLAGS+=$($PKG_CONFIG --libs nss)
     421CFLAGS+=$($PKG_CONFIG --cflags nss)
    392422EOF
    393423               
     
    634664if [ -z "$systemdsystemunitdir" ]; then
    635665        if $PKG_CONFIG --exists systemd; then
    636                 systemdsystemunitdir=`$PKG_CONFIG --variable=systemdsystemunitdir systemd`
     666                systemdsystemunitdir=$($PKG_CONFIG --variable=systemdsystemunitdir systemd)
    637667        fi
    638668fi
     
    662692
    663693otrprefix=""
    664 for i in / /usr /usr/local; do
    665         if [ -f ${i}/lib/libotr.a ]; then
    666                 otrprefix=${i}
    667                 break
    668         fi
    669 done
    670694if [ "$otr" = "auto" ]; then
    671         if [ -n "$otrprefix" ]; then
    672                 otr=1
    673         else
    674                 otr=0
    675         fi
    676 fi
     695        ! $PKG_CONFIG --exists libotr
     696        otr=$?
     697fi
     698
     699if [ "$otr" != 0 ] && ! $PKG_CONFIG --atleast-version=4.0 --print-errors libotr; then
     700        exit 1
     701fi
     702
    677703if [ "$otr" = 1 ]; then
    678704        # BI == built-in
    679705        echo '#define OTR_BI' >> config.h
    680         echo "EFLAGS+=-L${otrprefix}/lib -lotr -lgcrypt" >> Makefile.settings
    681         echo "CFLAGS+=-I${otrprefix}/include" >> Makefile.settings
     706        echo "EFLAGS+=$($PKG_CONFIG --libs libotr) $(libgcrypt-config --libs)" >> Makefile.settings
     707        echo "CFLAGS+=$($PKG_CONFIG --cflags libotr) $(libgcrypt-config --cflags)" >> Makefile.settings
    682708        echo 'OTR_BI=otr.o' >> Makefile.settings
    683709elif [ "$otr" = "plugin" ]; then
     710        # for some mysterious reason beyond the comprehension of my mortal mind,
     711        # the libgcrypt flags aren't needed when building as plugin. add them anyway.
    684712        echo '#define OTR_PI' >> config.h
    685         echo "OTRFLAGS=-L${otrprefix}/lib -lotr" >> Makefile.settings
    686         echo "CFLAGS+=-I${otrprefix}/include" >> Makefile.settings
     713        echo "OTRFLAGS=$($PKG_CONFIG --libs libotr) $(libgcrypt-config --libs)" >> Makefile.settings
     714        echo "CFLAGS+=$($PKG_CONFIG --cflags libotr) $(libgcrypt-config --cflags)" >> Makefile.settings
    687715        echo 'OTR_PI=otr.so' >> Makefile.settings
    688 fi
    689 if [ "$otr" != 0 ] && ! pkg-config libotr --atleast-version=4.0; then
    690         echo
    691         echo 'WARNING: Your libotr seems to be old. BitlBee now needs at least libotr 4.0.'
    692         # Not hard-failing because the code above doesn't use pkg-config, so who knows
    693         # what's true at this point...
    694716fi
    695717
  • dcc.c

    r63cad66 r3fbce97  
    265265                }
    266266
    267                 /* How likely is it that a 32-bit integer gets split accross
     267                /* How likely is it that a 32-bit integer gets split across
    268268                   packet boundaries? Chances are rarely 0 so let's be sure. */
    269269                if ((df->acked_len = (df->acked_len + ret) % 4) > 0) {
  • debian/changelog

    r63cad66 r3fbce97  
    1 bitlbee (3.2.2-2) UNRELEASED; urgency=medium
     1bitlbee (3.4.1-1) unstable; urgency=medium
    22
    33  * Fix copyright info (Closes: #764181)
    4 
    5  -- Wilmer van der Gaast <wilmer@gaast.net>  Fri, 17 Oct 2014 23:25:33 +0100
     4  * "New" upstream release. Apologies for the delay.
     5  * Exclude .git not .bzr when building the source package.
     6
     7 -- Wilmer van der Gaast <wilmer@gaast.net>  Mon, 03 Aug 2015 22:33:25 +0100
    68
    79bitlbee (3.2.2-1) unstable; urgency=medium
  • debian/source/options

    r63cad66 r3fbce97  
    1 --diff-ignore='(^|/)\.bzr'
     1--diff-ignore='(^|/)\.git'
  • doc/CHANGES

    r63cad66 r3fbce97  
    429429  officially be treated as case sensitive.
    430430- Fully stripping spaces from AIM screennames, this didn't happen completely
    431   which severly breaks the IRC protocol.
     431  which severely breaks the IRC protocol.
    432432- Removed all the yellow tape around daemon mode, it's pretty mature by now:
    433433  testing.bitlbee.org serves all (~30) SSL users from one daemon mode
     
    629629  some situations.
    630630- Outgoing MSN typing notifications are now understood correctly by the
    631   orignal MS Mac/Windows clients (again).
     631  original MS Mac/Windows clients (again).
    632632- Added "account add $protocol" to the documentation, got rid of a lot
    633633  of over-markup (i.e. overuse of bold-tags), reviewed some other parts.
  • doc/HACKING

    r63cad66 r3fbce97  
    1414calls and arguments where that seemed useful, etc.
    1515
    16 However, up to late in the 1.2 series, the IRC core was still spread accross
     16However, up to late in the 1.2 series, the IRC core was still spread across
    1717several files, mostly irc.c + irc_commands.c and pieces and bits in
    1818nogaim.c. If you're looking for a textbook example of layer violation, start
  • doc/README

    r63cad66 r3fbce97  
    88to set up the build system. If configure succeeds, run make to build BitlBee.
    99make install will move all the files to the right places.
     10
     11RUN MODES
     12=========
    1013
    1114--- (Fork)Daemon mode
     
    8992==================
    9093
    91 Cygwin NOTE: You'll need a glib installation to run BitlBee. However, Cygwin
    92 doesn't provide a glib package. You can download a binary tar.gz from:
    93 <http://my.dreamwiz.com/jbdoll/>. When you installed it, BitlBee should work
    94 fine. You'll probably like bitlbeed or xinetd to get it running on the
    95 network.
    96 
    97 On some non-Linux systems the program still suffers from some random bugs.
    98 Please do report them, we might be able to fix them if they're not too
    99 mysterious.
    100 
    101 Also, the configure script is known to not work very well with non-Bash
    102 shells, so if you experience problems, make sure you use bash to run the
    103 script. Same for the Makefile, it only works well with GNU make. (gmake on
    104 most BSD systems)
     94The configure script is may not work very well with some non-bash shells (but
     95dash is supported), so if you experience problems, make sure you use bash to
     96run the script. Same for the Makefile, it only works well with GNU make. (gmake
     97on most BSD systems)
    10598
    10699If someone can tell us how to write Makefiles that work with both/all
     
    153146=============================
    154147
    155 There used to be a note here about the simple obfuscation method used to
    156 make the passwords in the configuration files unreadable. However, BitlBee
    157 now uses a better format (and real encryption (salted MD5 and RC4)) to store
    158 the passwords. This means that people who somehow get their hands on your
    159 configuration files can't easily extract your passwords from them anymore.
     148BitlBee currently uses salted MD5 and RC4 to store the passwords. This means
     149that people who somehow get their hands on your configuration files can't
     150easily extract your passwords from them anymore.
    160151
    161152However, once you log into the BitlBee server and send your password, an
    162153intruder with tcpdump can still read your passwords. This can't really be
    163 avoided, of course. The new format is a lot more reliable (because it can't
    164 be cracked with just very basic crypto analysis anymore), but you still have
    165 to be careful. The main extra protection offered by the new format is that
    166 the files can only be cracked with some help from the user (by sending the
    167 password at login time).
    168 
    169 So if you run a public server, it's most important that you don't give root
    170 access to people who like to play with tcpdump. Also, it's a good idea to
    171 delete all *.nicks/*.accounts files as soon as BitlBee converted them to the
    172 new format (which happens as soon as the user logs in, it can't be done
    173 automatically because it needs the password for that account). You won't
    174 need them anymore (unless you want to switch back to an older BitlBee
    175 version) and they only make it easier for others to crack your passwords.
    176 
     154avoided, of course. So if you run a public server, it's most important that you
     155don't give root access to people who like to play with tcpdump.
    177156
    178157LEGAL
     
    181160BitlBee is distributed under the GPL (GNU General Public License). See the
    182161file COPYING for this license.
    183 
    184 The MD5 algorithm code is licensed under the Aladdin license. This license
    185 can be found in the files, to which this applies. The SHA1 algorithm code
    186 is licensed under the Mozilla Public License, see http://www.mozilla.org/MPL/
    187 for details.
    188162
    189163The Yahoo! library used by BitlBee is libyahoo2 <http://libyahoo2.sf.net/>,
  • doc/user-guide/Installation.xml

    r63cad66 r3fbce97  
    7373<para>
    7474By default, BitlBee runs as the user nobody. You might want
    75 to run it as a seperate user (some computers run named or apache as nobody).
     75to run it as a separate user (some computers run named or apache as nobody).
    7676</para>
    7777
  • doc/user-guide/commands.xml

    r63cad66 r3fbce97  
    182182                               
    183183                                <para>
    184                                         For more infomation about a setting, see <emphasis>help set &lt;setting&gt;</emphasis>.
     184                                        For more information about a setting, see <emphasis>help set &lt;setting&gt;</emphasis>.
    185185                                </para>
    186186                               
     
    240240                               
    241241                                <para>
    242                                         For more infomation about a setting, see <emphasis>help set &lt;setting&gt;</emphasis>.
     242                                        For more information about a setting, see <emphasis>help set &lt;setting&gt;</emphasis>.
    243243                                </para>
    244244                               
     
    785785                        <para>
    786786                                Keep two things in mind: When not using Twitter, you <emphasis>must</emphasis> also disable the <emphasis>oauth</emphasis> setting as it currently only works with Twitter. If you're still having issues, make sure there is <emphasis>no</emphasis> slash at the end of the URL you enter here.
     787                        </para>
     788                </description>
     789        </bitlbee-setting>
     790
     791        <bitlbee-setting name="carbons" type="boolean" scope="account">
     792                <default>true</default>
     793
     794                <description>
     795                        <para>
     796                                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.
     797                        </para>
     798                        <para>
     799                                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.
     800                        </para>
     801                        <para>
     802                                See also the <emphasis>self_messages</emphasis> setting.
    787803                        </para>
    788804                </description>
     
    10931109                       
    10941110                        <para>
    1095                                 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.
     1111                                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).
    10961112                        </para>
    10971113                       
     
    11581174                <description>
    11591175                        <para>
    1160                                 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.
     1176                                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.
    11611177                        </para>
    11621178
     
    11721188                <description>
    11731189                        <para>
    1174                                 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.
     1190                                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.
    11751191                        </para>
    11761192
     
    13241340        </bitlbee-setting>
    13251341
     1342        <bitlbee-setting name="proxy" type="string" scope="account">
     1343                <default>&lt;local&gt;&lt;auto&gt;</default>
     1344
     1345                <description>
     1346                        <para>
     1347                                A list of <emphasis>file transfer proxies</emphasis> for jabber. This isn't the connection proxy. Sorry, look in bitlbee.conf for those.
     1348                        </para>
     1349
     1350                        <para>
     1351                                It's a semicolon-separated list of items that can be either <emphasis>JID,HOST,PORT</emphasis> or two special values, <emphasis>&lt;local&gt;</emphasis> (to try a direct connection first) and <emphasis>&lt;auto&gt;</emphasis> (to try to discover a proxy). For example, "&lt;local&gt;;proxy.somewhere.org,123.123.123.123,7777".
     1352                        </para>
     1353                        <para>
     1354                                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.
     1355                        </para>
     1356                        <para>
     1357                                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.
     1358                        </para>
     1359                </description>
     1360        </bitlbee-setting>
     1361
    13261362        <bitlbee-setting name="query_order" type="string" scope="global">
    13271363                <default>lifo</default>
     
    13841420        </bitlbee-setting>
    13851421
     1422        <bitlbee-setting name="self_messages" type="string" scope="global">
     1423                <default>true</default>
     1424                <possible-values>true, false, prefix, prefix_notice</possible-values>
     1425
     1426                <description>
     1427                        <para>
     1428                                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.
     1429                        </para>
     1430
     1431                        <para>
     1432                                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.
     1433                        </para>
     1434                       
     1435                        <para>
     1436                                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.
     1437                        </para>
     1438
     1439                        <para>
     1440                                You can also set it to "false" to disable these messages completely.
     1441                        </para>
     1442
     1443                        <para>
     1444                                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.
     1445                        </para>
     1446
     1447                        <para>
     1448                                More information: <emphasis>https://wiki.bitlbee.org/SelfMessages</emphasis>
     1449                        </para>
     1450                </description>
     1451        </bitlbee-setting>
     1452
    13861453        <bitlbee-setting name="server" type="string" scope="account">
    13871454                <description>
     
    14171484
    14181485        <bitlbee-setting name="show_users" type="string" scope="channel">
    1419                 <default>online+,away</default>
     1486                <default>online+,special%,away</default>
    14201487
    14211488                <description>
     
    14241491                                and any modes they should have. The following statuses are currently
    14251492                                recognised: <emphasis>online</emphasis> (i.e. available, not
    1426                                 away), <emphasis>away</emphasis>, and <emphasis>offline</emphasis>.
     1493                                away), <emphasis>special</emphasis>, <emphasis>away</emphasis>,
     1494                                and <emphasis>offline</emphasis>.
    14271495                        </para>
    14281496                       
     
    14301498                                If a status is followed by a valid channel mode character
    14311499                                (@, % or +), it will be given to users with that status.
    1432                                 For example, <emphasis>online@,away+,offline</emphasis> will
    1433                                 show all users in the channel. Online people will
     1500                                For example, <emphasis>online@,special%,away+,offline</emphasis>
     1501                                will show all users in the channel. Online people will
    14341502                                have +o, people who are online but away will have +v,
    14351503                                and others will have no special modes.
     
    18911959
    18921960                        <description>
    1893                                 <para>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?</para>
     1961                                <para>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?</para>
    18941962                        </description>
    18951963
  • doc/user-guide/misc.xml

    r63cad66 r3fbce97  
    9090
    9191<para>
    92 Away states have different names accross different protocols. BitlBee will try to pick the best available option for every connection:
     92Away states have different names across different protocols. BitlBee will try to pick the best available option for every connection:
    9393</para>
    9494
     
    146146
    147147<para>
    148 Control channels are where you see your contacts. By default, you will have one control channel called &amp;bitlbee, containing all your contacts. But you can create more, if you want, and divide your contact list accross several channels.
     148Control channels are where you see your contacts. By default, you will have one control channel called &amp;bitlbee, containing all your contacts. But you can create more, if you want, and divide your contact list across several channels.
    149149</para>
    150150
  • doc/user-guide/quickstart.xml

    r63cad66 r3fbce97  
    1111
    1212<para>
    13 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 <emphasis>register</emphasis> command: <emphasis>register &lt;password&gt;</emphasis> (without the brackets!).
     13You 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 <emphasis>register</emphasis> command: <emphasis>register &lt;password&gt;</emphasis> (without the brackets!).
    1414</para>
    1515
  • help.c

    r63cad66 r3fbce97  
    105105        h = *help;
    106106        while (h) {
    107                 if (h->fd != last_fd) {
     107                if (h->fd == -1) {
     108                        g_free(h->offset.mem_offset);
     109                } else if (h->fd != last_fd) {
    108110                        close(h->fd);
    109111                        last_fd = h->fd;
     
    146148                        if (lseek(h->fd, h->offset.file_offset, SEEK_SET) == -1 ||
    147149                            read(h->fd, s, h->length) != h->length) {
     150                                g_free(s);
    148151                                return NULL;
    149152                        }
  • ipc.c

    r63cad66 r3fbce97  
    590590                                }
    591591
    592                                 *recv_fd = *(int *) CMSG_DATA(cmsg);
     592                                memcpy(recv_fd, CMSG_DATA(cmsg), sizeof(int));
    593593                                /*
    594594                                fprintf( stderr, "pid %d received fd %d\n", (int) getpid(), *recv_fd );
     
    758758        cmsg->cmsg_type = SCM_RIGHTS;
    759759        cmsg->cmsg_len = CMSG_LEN(sizeof(send_fd));
    760         *(int *) CMSG_DATA(cmsg) = send_fd;
     760        memcpy(CMSG_DATA(cmsg), &send_fd, sizeof(int));
    761761        msg.msg_controllen = cmsg->cmsg_len;
    762762#endif
  • irc.c

    r63cad66 r3fbce97  
    129129        s = set_add(&b->set, "show_offline", "false", set_eval_bw_compat, irc);
    130130        s->flags |= SET_HIDDEN;
     131        s = set_add(&b->set, "self_messages", "true", set_eval_self_messages, irc);
    131132        s = set_add(&b->set, "simulate_netsplit", "true", set_eval_bool, irc);
    132133        s = set_add(&b->set, "timezone", "local", set_eval_timezone, irc);
     
    153154        set_eval_charset(set_find(&b->set, "charset"), set_getstr(&b->set, "charset"));
    154155
    155         irc_write(irc, ":%s NOTICE AUTH :%s", irc->root->host, "BitlBee-IRCd initialized, please go on");
     156        irc_write(irc, ":%s NOTICE * :%s", irc->root->host, "BitlBee-IRCd initialized, please go on");
    156157        if (isatty(irc->fd)) {
    157                 irc_write(irc, ":%s NOTICE AUTH :%s", irc->root->host,
     158                irc_write(irc, ":%s NOTICE * :%s", irc->root->host,
    158159                          "If you read this, you most likely accidentally "
    159160                          "started BitlBee in inetd mode on the command line. "
     
    380381                                                conv = NULL;
    381382                                        } else {
    382                                                 irc_write(irc, ":%s NOTICE AUTH :%s", irc->root->host,
     383                                                irc_write(irc, ":%s NOTICE * :%s", irc->root->host,
    383384                                                          "Warning: invalid characters received at login time.");
    384385
     
    727728int irc_check_login(irc_t *irc)
    728729{
    729         if (irc->user->user && irc->user->nick) {
     730        if (irc->user->user && irc->user->nick && !(irc->status & USTATUS_CAP_PENDING)) {
    730731                if (global.conf->authmode == AUTHMODE_CLOSED && !(irc->status & USTATUS_AUTHORIZED)) {
    731732                        irc_send_num(irc, 464, ":This server is password-protected.");
  • irc.h

    r63cad66 r3fbce97  
    4949        USTATUS_SHUTDOWN = 8,   /* Now used to indicate we're shutting down.
    5050                                   Currently just blocks irc_vawrite(). */
     51        USTATUS_CAP_PENDING = 16,
     52        USTATUS_SASL_PLAIN_PENDING = 32,
    5153
    5254        /* Not really status stuff, but other kinds of flags: For slightly
     
    6567} irc_status_t;
    6668
     69typedef enum {
     70        CAP_SASL = (1 << 0),
     71        CAP_MULTI_PREFIX = (1 << 1),
     72        CAP_EXTENDED_JOIN = (1 << 2),
     73        CAP_AWAY_NOTIFY = (1 << 3),
     74        CAP_USERHOST_IN_NAMES = (1 << 4),
     75} irc_cap_flag_t;
     76
    6777struct irc_user;
    6878
     
    102112
    103113        struct bee *b;
     114        guint32 caps;
    104115} irc_t;
    105116
     
    302313gboolean irc_channel_name_hint(irc_channel_t *ic, const char *name);
    303314void irc_channel_update_ops(irc_channel_t *ic, char *value);
     315char irc_channel_user_get_prefix(irc_channel_user_t *icu);
    304316char *set_eval_irc_channel_ops(struct set *set, char *value);
    305317gboolean irc_channel_wants_user(irc_channel_t *ic, irc_user_t *iu);
     
    331343                                     irc_channel_user_flags_t old_flags, irc_channel_user_flags_t new_flags);
    332344void irc_send_invite(irc_user_t *iu, irc_channel_t *ic);
     345void irc_send_cap(irc_t *irc, char *subcommand, char *body);
     346void irc_send_away_notify(irc_user_t *iu);
    333347
    334348/* irc_user.c */
     
    344358char *set_eval_timezone(struct set *set, char *value);
    345359char *irc_format_timestamp(irc_t *irc, time_t msg_ts);
     360char *set_eval_self_messages(struct set *set, char *value);
    346361
    347362/* irc_im.c */
     
    349364void bee_irc_user_nick_reset(irc_user_t *iu);
    350365
     366/* irc_cap.c */
     367void irc_cmd_cap(irc_t *irc, char **cmd);
     368
    351369#endif
  • irc_channel.c

    r63cad66 r3fbce97  
    213213        }
    214214
     215        /* Skip the free/init if nothing is being changed */
     216        if (ic->f == new) {
     217                return value;
     218        }
     219
    215220        /* TODO: Return values. */
    216221        if (ic->f && ic->f->_free) {
     
    255260
    256261        if (!(icu = irc_channel_has_user(ic, iu))) {
     262                if (iu == ic->irc->user && type == IRC_CDU_KICK) {
     263                        /* an error happened before joining, inform the client with a numeric */
     264                        irc_send_num(ic->irc, 403, "%s :Error joining channel (check control channel?)", ic->name);
     265                }
    257266                return 0;
    258267        }
     
    427436                          changes);
    428437        }
     438}
     439
     440char irc_channel_user_get_prefix(irc_channel_user_t *icu)
     441{
     442        if (icu->flags & IRC_CHANNEL_USER_OP) {
     443                return '@';
     444        } else if (icu->flags & IRC_CHANNEL_USER_HALFOP) {
     445                return '%';
     446        } else if (icu->flags & IRC_CHANNEL_USER_VOICE) {
     447                return '+';
     448        }
     449        return 0;
    429450}
    430451
     
    593614
    594615        translit_name = g_convert_with_fallback(hint, -1, "ASCII//TRANSLIT", "UTF-8", "", NULL, &bytes_written, NULL);
     616
     617        if (!translit_name) {
     618                /* Same thing as in nick_gen() in nick.c, try again without //TRANSLIT */
     619                translit_name = g_convert_with_fallback(hint, -1, "ASCII", "UTF-8", "", NULL, &bytes_written, NULL);
     620        }
     621
     622        if (!translit_name) {
     623                return NULL;
     624        }
     625
    595626        if (bytes_written > MAX_NICK_LENGTH) {
    596627                translit_name[MAX_NICK_LENGTH] = '\0';
  • irc_commands.c

    r63cad66 r3fbce97  
    2828#include "help.h"
    2929#include "ipc.h"
     30#include "base64.h"
    3031
    3132static void irc_cmd_pass(irc_t *irc, char **cmd)
     
    5556                irc_setpass(irc, cmd[1]);
    5657                irc_check_login(irc);
     58        }
     59}
     60
     61static gboolean irc_sasl_plain_parse(char *input, char **user, char **pass)
     62{
     63        int i, part, len;
     64        guint8 *decoded;
     65        char *parts[3];
     66
     67        /* bitlbee's base64_decode wrapper adds an extra null terminator at the end */
     68        len = base64_decode(input, &decoded);
     69
     70        /* this loop splits the decoded string into the parts array, like this:
     71           "username\0username\0password" -> {"username", "username", "password"} */
     72
     73        for (i = 0, part = 0; i < len && part < 3; part++) {
     74                /* set each of parts[] to point to the beginning of a string */
     75                parts[part] = (char *) decoded + i;
     76
     77                /* move the cursor forward to the next null terminator*/
     78                i += strlen(parts[part]) + 1;
     79        }
     80
     81        /* sanity checks */
     82        if (part != 3 || i != (len + 1) || (parts[0][0] && strcmp(parts[0], parts[1]) != 0)) {
     83                g_free(decoded);
     84                return FALSE;
     85        } else {
     86                *user = g_strdup(parts[1]);
     87                *pass = g_strdup(parts[2]);
     88                g_free(decoded);
     89                return TRUE;
     90        }
     91}
     92
     93static gboolean irc_sasl_check_pass(irc_t *irc, char *user, char *pass)
     94{
     95        storage_status_t status;
     96
     97        /* just check the password here to be able to reply with useful numerics
     98         * the actual identification will be handled later */
     99        status = storage_check_pass(user, pass);
     100
     101        if (status == STORAGE_OK) {
     102                if (!irc->user->nick) {
     103                        /* set the nick here so we have it for the following numeric */
     104                        irc->user->nick = g_strdup(user);
     105                }
     106                irc_send_num(irc, 903, ":Password accepted");
     107                return TRUE;
     108
     109        } else if (status == STORAGE_INVALID_PASSWORD) {
     110                irc_send_num(irc, 904, ":Incorrect password");
     111        } else if (status == STORAGE_NO_SUCH_USER) {
     112                irc_send_num(irc, 904, ":The nick is (probably) not registered");
     113        } else {
     114                irc_send_num(irc, 904, ":Unknown SASL authentication error");
     115        }
     116
     117        return FALSE;
     118}
     119
     120static void irc_cmd_authenticate(irc_t *irc, char **cmd)
     121{
     122        /* require the CAP to be enabled, and don't allow authentication before server password */
     123        if (!(irc->caps & CAP_SASL) ||
     124            (global.conf->authmode == AUTHMODE_CLOSED && !(irc->status & USTATUS_AUTHORIZED))) {
     125                return;
     126        }
     127
     128        if (irc->status & USTATUS_SASL_PLAIN_PENDING) {
     129                char *user, *pass;
     130
     131                irc->status &= ~USTATUS_SASL_PLAIN_PENDING;
     132
     133                if (!irc_sasl_plain_parse(cmd[1], &user, &pass)) {
     134                        irc_send_num(irc, 904, ":SASL authentication failed");
     135                        return;
     136                }
     137
     138                /* let's not support the nick != user case
     139                 * if NICK is received after SASL, it will just fail after registration */
     140                if (user && irc->user->nick && strcmp(user, irc->user->nick) != 0) {
     141                        irc_send_num(irc, 902, ":Your SASL username does not match your nickname");
     142
     143                } else if (irc_sasl_check_pass(irc, user, pass)) {
     144                        /* and here we do the same thing as the PASS command*/
     145                        if (irc->status & USTATUS_LOGGED_IN) {
     146                                char *send_cmd[] = { "identify", pass, NULL };
     147                                root_command(irc, send_cmd);
     148                        } else {
     149                                /* no check_login here - wait for CAP END */
     150                                irc_setpass(irc, pass);
     151                        }
     152                }
     153
     154                g_free(user);
     155                g_free(pass);
     156
     157        } else if (irc->status & USTATUS_IDENTIFIED) {
     158                irc_send_num(irc, 907, ":You have already authenticated");
     159
     160        } else if (strcmp(cmd[1], "*") == 0) {
     161                irc_send_num(irc, 906, ":SASL authentication aborted");
     162                irc->status &= ~USTATUS_SASL_PLAIN_PENDING;
     163
     164        } else if (g_strcasecmp(cmd[1], "PLAIN") == 0) {
     165                irc_write(irc, "AUTHENTICATE +");
     166                irc->status |= USTATUS_SASL_PLAIN_PENDING;
     167
     168        } else {
     169                irc_send_num(irc, 908, "PLAIN :is the available SASL mechanism");
     170                irc_send_num(irc, 904, ":SASL authentication failed");
     171                irc->status &= ~USTATUS_SASL_PLAIN_PENDING;
    57172        }
    58173}
     
    83198                        irc->status &= ~USTATUS_IDENTIFIED;
    84199                        irc_umode_set(irc, "-R", 1);
     200
     201                        if (irc->caps & CAP_SASL) {
     202                                irc_send_num(irc, 901, "%s!%s@%s :You are now logged out",
     203                                        irc->user->nick, irc->user->user, irc->user->host);
     204                        }
     205
    85206                        irc_rootmsg(irc, "Changing nicks resets your identify status. "
    86207                                    "Re-identify or register a new account if you want "
     
    685806
    686807static const command_t irc_commands[] = {
     808        { "cap",         1, irc_cmd_cap,         0 },
    687809        { "pass",        1, irc_cmd_pass,        0 },
    688810        { "user",        4, irc_cmd_user,        IRC_CMD_PRE_LOGIN },
     
    721843        { "restart",     0, NULL,                IRC_CMD_OPER_ONLY | IRC_CMD_TO_MASTER },
    722844        { "kill",        2, NULL,                IRC_CMD_OPER_ONLY | IRC_CMD_TO_MASTER },
     845        { "authenticate", 1, irc_cmd_authenticate, 0 },
    723846        { NULL }
    724847};
  • irc_im.c

    r63cad66 r3fbce97  
    8484        }
    8585
    86         while ((s = strchr(iu->user, ' '))) {
    87                 *s = '_';
    88         }
     86        /* Sanitize */
     87        str_reject_chars(iu->user, " ", '_');
     88        str_reject_chars(iu->host, " ", '_');
    8989
    9090        if (bu->flags & BEE_USER_LOCAL) {
     
    118118        if (bu->flags & BEE_USER_AWAY || !(bu->flags & BEE_USER_ONLINE)) {
    119119                iu->flags |= IRC_USER_AWAY;
     120        }
     121
     122        if ((irc->caps & CAP_AWAY_NOTIFY) &&
     123            ((bu->flags & BEE_USER_AWAY) != (old->flags & BEE_USER_AWAY) ||
     124             (bu->flags & BEE_USER_ONLINE) != (old->flags & BEE_USER_ONLINE))) {
     125                irc_send_away_notify(iu);
    120126        }
    121127
     
    201207}
    202208
    203 static gboolean bee_irc_user_msg(bee_t *bee, bee_user_t *bu, const char *msg_, time_t sent_at)
     209static gboolean bee_irc_user_msg(bee_t *bee, bee_user_t *bu, const char *msg_, guint32 flags, time_t sent_at)
    204210{
    205211        irc_t *irc = bee->ui_data;
    206212        irc_user_t *iu = (irc_user_t *) bu->ui_data;
     213        irc_user_t *src_iu = iu;
     214        irc_user_t *dst_iu = irc->user;
    207215        const char *dst;
    208216        char *prefix = NULL;
    209217        char *wrapped, *ts = NULL;
    210218        char *msg = g_strdup(msg_);
     219        char *message_type = "PRIVMSG";
    211220        GSList *l;
    212221
     
    216225
    217226        dst = irc_user_msgdest(iu);
    218         if (dst != irc->user->nick) {
    219                 /* if not messaging directly, call user by name */
    220                 prefix = g_strdup_printf("%s%s%s", irc->user->nick, set_getstr(&bee->set, "to_char"), ts ? : "");
     227
     228        if (flags & OPT_SELFMESSAGE) {
     229                char *setting = set_getstr(&irc->b->set, "self_messages");
     230
     231                if (is_bool(setting)) {
     232                        if (bool2int(setting)) {
     233                                /* set to true, send it with src/dst flipped */
     234                               
     235                                dst_iu = iu;
     236                                src_iu = irc->user;
     237
     238                                if (dst == irc->user->nick) {
     239                                        dst = dst_iu->nick;
     240                                }
     241                        } else {
     242                                /* set to false, skip the message completely */
     243                                goto cleanup;
     244                        }
     245                } else if (g_strncasecmp(setting, "prefix", 6) == 0) {
     246                        /* third state, prefix, loosely imitates the znc privmsg_prefix module */
     247
     248                        g_free(msg);
     249                        if (g_strncasecmp(msg_, "/me ", 4) == 0) {
     250                                msg = g_strdup_printf("/me -> %s", msg_ + 4);
     251                        } else {
     252                                msg = g_strdup_printf("-> %s", msg_);
     253                        }
     254
     255                        if (g_strcasecmp(setting, "prefix_notice") == 0) {
     256                                message_type = "NOTICE";
     257                        }
     258                }
     259
     260        }
     261
     262        if (dst != dst_iu->nick) {
     263                /* if not messaging directly (control channel), call user by name */
     264                prefix = g_strdup_printf("%s%s%s", dst_iu->nick, set_getstr(&bee->set, "to_char"), ts ? : "");
    221265        } else {
    222266                prefix = ts;
     
    249293
    250294        wrapped = word_wrap(msg, 425);
    251         irc_send_msg(iu, "PRIVMSG", dst, wrapped, prefix);
     295        irc_send_msg(src_iu, message_type, dst, wrapped, prefix);
    252296        g_free(wrapped);
    253297
     
    260304}
    261305
    262 static gboolean bee_irc_user_typing(bee_t *bee, bee_user_t *bu, uint32_t flags)
     306static gboolean bee_irc_user_typing(bee_t *bee, bee_user_t *bu, guint32 flags)
    263307{
    264308        irc_t *irc = (irc_t *) bee->ui_data;
     
    617661}
    618662
    619 static gboolean bee_irc_chat_msg(bee_t *bee, struct groupchat *c, bee_user_t *bu, const char *msg, time_t sent_at)
     663static gboolean bee_irc_chat_msg(bee_t *bee, struct groupchat *c, bee_user_t *bu, const char *msg, guint32 flags, time_t sent_at)
    620664{
    621665        irc_t *irc = bee->ui_data;
    622         irc_user_t *iu = bu->ui_data;
     666        irc_user_t *iu = flags & OPT_SELFMESSAGE ? irc->user : bu->ui_data;
    623667        irc_channel_t *ic = c->ui_data;
    624668        char *wrapped, *ts = NULL;
     
    654698}
    655699
    656 static gboolean bee_irc_chat_remove_user(bee_t *bee, struct groupchat *c, bee_user_t *bu)
     700static gboolean bee_irc_chat_remove_user(bee_t *bee, struct groupchat *c, bee_user_t *bu, const char *reason)
    657701{
    658702        irc_t *irc = bee->ui_data;
     
    666710           using imcb_chat_free() and the channel was IRC_CHANNEL_TEMP, we get into
    667711           a broken state around here. */
    668         irc_channel_del_user(ic, bu == bee->user ? irc->user : bu->ui_data, IRC_CDU_PART, NULL);
     712        irc_channel_del_user(ic, bu == bee->user ? irc->user : bu->ui_data, IRC_CDU_PART, reason);
    669713
    670714        return TRUE;
     
    839883            acc->prpl->chat_join) {
    840884                char *nick;
     885                struct groupchat *gc;
    841886
    842887                if (!(nick = set_getstr(&ic->set, "nick"))) {
     
    845890
    846891                ic->flags |= IRC_CHANNEL_CHAT_PICKME;
    847                 acc->prpl->chat_join(acc->ic, room, nick, NULL, &ic->set);
     892                gc = acc->prpl->chat_join(acc->ic, room, nick, NULL, &ic->set);
    848893                ic->flags &= ~IRC_CHANNEL_CHAT_PICKME;
     894
     895                if (!gc) {
     896                        irc_send_num(ic->irc, 403, "%s :Error joining channel (check control channel?)", ic->name);
     897                }
    849898
    850899                return FALSE;
     
    863912        }
    864913
    865         /* Remove the reference. We don't need it anymore. */
    866         ic->data = NULL;
     914        if (!(ic->flags & IRC_CHANNEL_TEMP)) {
     915                /* Remove the reference.
     916                 * We only need it for temp channels that are being freed */
     917                ic->data = NULL;
     918        }
    867919
    868920        return TRUE;
     
    10551107                df->proto_finished = TRUE;
    10561108        }
     1109}
     1110
     1111static void bee_irc_log(bee_t *bee, const char *tag, const char *msg)
     1112{
     1113        irc_t *irc = (irc_t *) bee->ui_data;
     1114
     1115        irc_rootmsg(irc, "%s - %s", tag, msg);
    10571116}
    10581117
     
    10851144        bee_irc_ft_close,
    10861145        bee_irc_ft_finished,
     1146
     1147        bee_irc_log,
    10871148};
  • irc_send.c

    r63cad66 r3fbce97  
    172172        irc_t *irc = ic->irc;
    173173
    174         irc_write(irc, ":%s!%s@%s JOIN :%s", iu->nick, iu->user, iu->host, ic->name);
     174        if (irc->caps & CAP_EXTENDED_JOIN) {
     175                irc_write(irc, ":%s!%s@%s JOIN %s * :%s", iu->nick, iu->user, iu->host, ic->name, iu->fullname);
     176        } else {
     177                irc_write(irc, ":%s!%s@%s JOIN :%s", iu->nick, iu->user, iu->host, ic->name);
     178        }
    175179
    176180        if (iu == irc->user) {
     
    198202}
    199203
     204#define IRC_NAMES_LEN 385
     205
    200206void irc_send_names(irc_channel_t *ic)
    201207{
    202208        GSList *l;
    203         char namelist[385] = "";
     209        GString *namelist = g_string_sized_new(IRC_NAMES_LEN);
     210        gboolean uhnames = (ic->irc->caps & CAP_USERHOST_IN_NAMES);
    204211
    205212        /* RFCs say there is no error reply allowed on NAMES, so when the
     
    208215                irc_channel_user_t *icu = l->data;
    209216                irc_user_t *iu = icu->iu;
    210 
    211                 if (strlen(namelist) + strlen(iu->nick) > sizeof(namelist) - 4) {
    212                         irc_send_num(ic->irc, 353, "= %s :%s", ic->name, namelist);
    213                         *namelist = 0;
    214                 }
    215 
    216                 if (icu->flags & IRC_CHANNEL_USER_OP) {
    217                         strcat(namelist, "@");
    218                 } else if (icu->flags & IRC_CHANNEL_USER_HALFOP) {
    219                         strcat(namelist, "%");
    220                 } else if (icu->flags & IRC_CHANNEL_USER_VOICE) {
    221                         strcat(namelist, "+");
    222                 }
    223 
    224                 strcat(namelist, iu->nick);
    225                 strcat(namelist, " ");
    226         }
    227 
    228         if (*namelist) {
    229                 irc_send_num(ic->irc, 353, "= %s :%s", ic->name, namelist);
     217                size_t extra_len = strlen(iu->nick);
     218                char prefix;
     219
     220                if (uhnames) {
     221                        extra_len += strlen(iu->user) + strlen(iu->host) + 2;
     222                }
     223
     224                if (namelist->len + extra_len > IRC_NAMES_LEN - 4) {
     225                        irc_send_num(ic->irc, 353, "= %s :%s", ic->name, namelist->str);
     226                        g_string_truncate(namelist, 0);
     227                }
     228
     229                if ((prefix = irc_channel_user_get_prefix(icu))) {
     230                        g_string_append_c(namelist, prefix);
     231                }
     232
     233                if (uhnames) {
     234                        g_string_append_printf(namelist, "%s!%s@%s ", iu->nick, iu->user, iu->host);
     235                } else {
     236                        g_string_append(namelist, iu->nick);
     237                        g_string_append_c(namelist, ' ');
     238                }
     239        }
     240
     241        if (namelist->len) {
     242                irc_send_num(ic->irc, 353, "= %s :%s", ic->name, namelist->str);
    230243        }
    231244
    232245        irc_send_num(ic->irc, 366, "%s :End of /NAMES list", ic->name);
     246
     247        g_string_free(namelist, TRUE);
    233248}
    234249
     
    249264}
    250265
     266/* msg1 and msg2 are output parameters. If msg2 is non-null, msg1 is guaranteed to be non-null too.
     267   The idea is to defer the formatting of "$msg1 ($msg2)" to later calls to avoid a g_strdup_printf() here. */
     268static void get_status_message(bee_user_t *bu, char **msg1, char **msg2)
     269{
     270        *msg1 = NULL;
     271        *msg2 = NULL;
     272
     273        if (!(bu->flags & BEE_USER_ONLINE)) {
     274                *msg1 = "User is offline";
     275
     276        } else if ((bu->status && *bu->status) ||
     277                   (bu->status_msg && *bu->status_msg)) {
     278
     279                if (bu->status && bu->status_msg) {
     280                        *msg1 = bu->status;
     281                        *msg2 = bu->status_msg;
     282                } else {
     283                        *msg1 = bu->status ? : bu->status_msg;
     284                }
     285        }
     286
     287        if (*msg1 && !**msg1) {
     288                *msg1 = (bu->flags & BEE_USER_AWAY) ? "Away" : NULL;
     289        }
     290}
     291
    251292void irc_send_whois(irc_user_t *iu)
    252293{
     
    258299        if (iu->bu) {
    259300                bee_user_t *bu = iu->bu;
     301                char *msg1, *msg2;
     302                int num;
    260303
    261304                irc_send_num(irc, 312, "%s %s.%s :%s network", iu->nick, bu->ic->acc->user,
     
    263306                             bu->ic->acc->prpl->name);
    264307
    265                 if ((bu->status && *bu->status) ||
    266                     (bu->status_msg && *bu->status_msg)) {
    267                         int num = bu->flags & BEE_USER_AWAY ? 301 : 320;
    268 
    269                         if (bu->status && bu->status_msg) {
    270                                 irc_send_num(irc, num, "%s :%s (%s)", iu->nick, bu->status, bu->status_msg);
    271                         } else {
    272                                 irc_send_num(irc, num, "%s :%s", iu->nick, bu->status ? : bu->status_msg);
    273                         }
    274                 } else if (!(bu->flags & BEE_USER_ONLINE)) {
    275                         irc_send_num(irc, 301, "%s :%s", iu->nick, "User is offline");
     308                num = (bu->flags & BEE_USER_AWAY || !(bu->flags & BEE_USER_ONLINE)) ? 301 : 320;
     309
     310                get_status_message(bu, &msg1, &msg2);
     311
     312                if (msg1 && msg2) {
     313                        irc_send_num(irc, num, "%s :%s (%s)", iu->nick, msg1, msg2);
     314                } else if (msg1) {
     315                        irc_send_num(irc, num, "%s :%s", iu->nick, msg1);
    276316                }
    277317
     
    294334
    295335        while (l) {
    296                 irc_user_t *iu = l->data;
     336                irc_user_t *iu;
     337
     338                /* Null terminated string with three chars, respectively:
     339                 * { <H|G>, <@|%|+|\0>, \0 } */
     340                char status_prefix[3] = {0};
     341
    297342                if (is_channel) {
    298                         iu = ((irc_channel_user_t *) iu)->iu;
    299                 }
    300                 /* TODO(wilmer): Restore away/channel information here */
    301                 irc_send_num(irc, 352, "%s %s %s %s %s %c :0 %s",
     343                        irc_channel_user_t *icu = l->data;
     344                        status_prefix[1] = irc_channel_user_get_prefix(icu);
     345                        iu = icu->iu;
     346                } else {
     347                        iu = l->data;
     348                }
     349
     350                /* rfc1459 doesn't mention this: G means gone, H means here */
     351                status_prefix[0] = iu->flags & IRC_USER_AWAY ? 'G' : 'H';
     352
     353                irc_send_num(irc, 352, "%s %s %s %s %s %s :0 %s",
    302354                             is_channel ? channel : "*", iu->user, iu->host, irc->root->host,
    303                              iu->nick, iu->flags & IRC_USER_AWAY ? 'G' : 'H',
    304                              iu->fullname);
     355                             iu->nick, status_prefix, iu->fullname);
    305356                l = l->next;
    306357        }
     
    428479                  iu->nick, iu->user, iu->host, irc->user->nick, ic->name);
    429480}
     481
     482void irc_send_cap(irc_t *irc, char *subcommand, char *body)
     483{
     484        char *nick = irc->user->nick ? : "*";
     485
     486        irc_write(irc, ":%s CAP %s %s :%s", irc->root->host, nick, subcommand, body);
     487}
     488
     489void irc_send_away_notify(irc_user_t *iu)
     490{
     491        bee_user_t *bu = iu->bu;
     492
     493        if (!bu) {
     494                return;
     495        }
     496
     497        if (bu->flags & BEE_USER_AWAY || !(bu->flags & BEE_USER_ONLINE)) {
     498                char *msg1, *msg2;
     499
     500                get_status_message(bu, &msg1, &msg2);
     501
     502                if (msg2) {
     503                        irc_write(iu->irc, ":%s!%s@%s AWAY :%s (%s)", iu->nick, iu->user, iu->host, msg1, msg2);
     504                } else {
     505                        irc_write(iu->irc, ":%s!%s@%s AWAY :%s", iu->nick, iu->user, iu->host, msg1);
     506                }
     507        } else {
     508                irc_write(iu->irc, ":%s!%s@%s AWAY", iu->nick, iu->user, iu->host);
     509        }
     510}
     511
  • irc_util.c

    r63cad66 r3fbce97  
    119119        }
    120120}
     121
     122
     123char *set_eval_self_messages(set_t *set, char *value)
     124{
     125        if (is_bool(value) ||
     126            g_strcasecmp(value, "prefix") == 0 ||
     127            g_strcasecmp(value, "prefix_notice") == 0) {
     128                return value;
     129        } else {
     130                return SET_INVALID;
     131        }
     132}
  • lib/http_client.c

    r63cad66 r3fbce97  
    162162        }
    163163
    164         req->func(req);
     164        if (req->func != NULL) {
     165                req->func(req);
     166        }
    165167        http_free(req);
    166168        return FALSE;
     
    299301        }
    300302
    301         req->func(req);
     303        if (req->func != NULL) {
     304                req->func(req);
     305        }
    302306        http_free(req);
    303307        return FALSE;
     
    412416        }
    413417
    414         if ((req->flags & HTTPC_STREAMING) && req->reply_body) {
     418        if ((req->flags & HTTPC_STREAMING) && req->reply_body && req->func != NULL) {
    415419                req->func(req);
    416420        }
     
    696700                ssl_disconnect(req->ssl);
    697701        } else {
    698                 closesocket(req->fd);
     702                proxy_disconnect(req->fd);
    699703        }
    700704
  • lib/ini.c

    r63cad66 r3fbce97  
    2828ini_t *ini_open(char *file)
    2929{
    30         int fd;
     30        int fd = -1;
    3131        ini_t *ini = NULL;
    3232        struct stat fi;
    3333
    34         if ((fd = open(file, O_RDONLY)) != -1 &&
     34        if (file && (fd = open(file, O_RDONLY)) != -1 &&
    3535            fstat(fd, &fi) == 0 &&
    3636            fi.st_size <= 16384 &&
  • lib/misc.c

    r63cad66 r3fbce97  
    187187                                } else if (g_strncasecmp(cs + 1, "br", taglen) == 0) {
    188188                                        *(s++) = '\n';
     189                                } else if (g_strncasecmp(cs + 1, "br/", taglen) == 0) {
     190                                        *(s++) = '\n';
     191                                } else if (g_strncasecmp(cs + 1, "br /", taglen) == 0) {
     192                                        *(s++) = '\n';
    189193                                }
    190194                                in++;
     
    295299
    296300/* Warning: This one explodes the string. Worst-cases can make the string 3x its original size! */
    297 /* This fuction is safe, but make sure you call it safely as well! */
     301/* This function is safe, but make sure you call it safely as well! */
    298302void http_encode(char *s)
    299303{
     
    767771}
    768772
     773/* Filters all the characters in 'blacklist' replacing them with 'replacement'.
     774 * Modifies the string in-place and returns the string itself.
     775 * For the opposite, use g_strcanon() */
     776char *str_reject_chars(char *string, const char *reject, char replacement)
     777{
     778        char *c = string;
     779
     780        while (*c) {
     781                c += strcspn(c, reject);
     782                if (*c) {
     783                        *c = replacement;
     784                }
     785        }
     786
     787        return string;
     788}
  • lib/misc.h

    r63cad66 r3fbce97  
    150150G_MODULE_EXPORT int truncate_utf8(char *string, int maxlen);
    151151G_MODULE_EXPORT gboolean parse_int64(char *string, int base, guint64 *number);
     152G_MODULE_EXPORT char *str_reject_chars(char *string, const char *reject, char replacement);
    152153
    153154#endif
  • lib/proxy.c

    r63cad66 r3fbce97  
    5151#endif
    5252
     53static GHashTable *phb_hash = NULL;
     54
    5355struct PHB {
    5456        b_event_handler func, proxy_func;
     
    6163};
    6264
     65typedef int (*proxy_connect_func)(const char *host, unsigned short port_, struct PHB *phb);
     66
    6367static int proxy_connect_none(const char *host, unsigned short port_, struct PHB *phb);
    6468
    65 static gboolean phb_close(struct PHB *phb)
    66 {
    67         close(phb->fd);
    68         phb->func(phb->data, -1, B_EV_IO_READ);
     69static gboolean phb_free(struct PHB *phb, gboolean success)
     70{
     71        g_hash_table_remove(phb_hash, &phb->fd);
     72
     73        if (!success) {
     74                if (phb->fd > 0) {
     75                        closesocket(phb->fd);
     76                }
     77                if (phb->func) {
     78                        phb->func(phb->data, -1, B_EV_IO_READ);
     79                }
     80        }
     81        if (phb->gai) {
     82                freeaddrinfo(phb->gai);
     83        }
    6984        g_free(phb->host);
    7085        g_free(phb);
     
    89104                                dup2(new_fd, source);
    90105                                closesocket(new_fd);
     106                                phb->fd = source;
    91107                                phb->inpa = b_input_add(source, B_EV_IO_WRITE, proxy_connected, phb);
    92108                                return FALSE;
     
    101117
    102118        freeaddrinfo(phb->gai);
     119        phb->gai = NULL;
     120
    103121        b_event_remove(phb->inpa);
    104122        phb->inpa = 0;
     123
    105124        if (phb->proxy_func) {
    106125                phb->proxy_func(phb->proxy_data, source, B_EV_IO_READ);
    107126        } else {
    108127                phb->func(phb->data, source, B_EV_IO_READ);
    109                 g_free(phb);
     128                phb_free(phb, TRUE);
    110129        }
    111130
     
    171190
    172191        if (fd < 0 && host) {
    173                 g_free(phb);
     192                phb_free(phb, TRUE);
    174193        }
    175194
     
    204223            (memcmp(HTTP_GOODSTRING2, inputline, strlen(HTTP_GOODSTRING2)) == 0)) {
    205224                phb->func(phb->data, source, B_EV_IO_READ);
    206                 g_free(phb->host);
    207                 g_free(phb);
    208                 return FALSE;
    209         }
    210 
    211         return phb_close(phb);
     225                return phb_free(phb, TRUE);
     226        }
     227
     228        return phb_free(phb, FALSE);
    212229}
    213230
     
    224241        len = sizeof(error);
    225242        if (getsockopt(source, SOL_SOCKET, SO_ERROR, &error, &len) < 0) {
    226                 return phb_close(phb);
     243                return phb_free(phb, FALSE);
    227244        }
    228245        sock_make_blocking(source);
     
    231248                   phb->host, phb->port);
    232249        if (send(source, cmd, strlen(cmd), 0) < 0) {
    233                 return phb_close(phb);
     250                return phb_free(phb, FALSE);
    234251        }
    235252
     
    242259                g_free(t2);
    243260                if (send(source, cmd, strlen(cmd), 0) < 0) {
    244                         return phb_close(phb);
     261                        return phb_free(phb, FALSE);
    245262                }
    246263        }
     
    248265        g_snprintf(cmd, sizeof(cmd), "\r\n");
    249266        if (send(source, cmd, strlen(cmd), 0) < 0) {
    250                 return phb_close(phb);
     267                return phb_free(phb, FALSE);
    251268        }
    252269
     
    279296        if (read(source, packet, 9) >= 4 && packet[1] == 90) {
    280297                phb->func(phb->data, source, B_EV_IO_READ);
    281                 g_free(phb->host);
    282                 g_free(phb);
    283                 return FALSE;
    284         }
    285 
    286         return phb_close(phb);
     298                return phb_free(phb, TRUE);
     299        }
     300
     301        return phb_free(phb, FALSE);
    287302}
    288303
     
    294309        socklen_t len;
    295310        int error = ETIMEDOUT;
     311        gboolean is_socks4a = (proxytype == PROXY_SOCKS4A);
    296312
    297313        if (phb->inpa > 0) {
     
    300316        len = sizeof(error);
    301317        if (getsockopt(source, SOL_SOCKET, SO_ERROR, &error, &len) < 0) {
    302                 return phb_close(phb);
     318                return phb_free(phb, FALSE);
    303319        }
    304320        sock_make_blocking(source);
    305321
    306         /* XXX does socks4 not support host name lookups by the proxy? */
    307         if (!(hp = gethostbyname(phb->host))) {
    308                 return phb_close(phb);
     322        if (!is_socks4a && !(hp = gethostbyname(phb->host))) {
     323                return phb_free(phb, FALSE);
    309324        }
    310325
     
    313328        packet[2] = phb->port >> 8;
    314329        packet[3] = phb->port & 0xff;
    315         packet[4] = (unsigned char) (hp->h_addr_list[0])[0];
    316         packet[5] = (unsigned char) (hp->h_addr_list[0])[1];
    317         packet[6] = (unsigned char) (hp->h_addr_list[0])[2];
    318         packet[7] = (unsigned char) (hp->h_addr_list[0])[3];
     330        if (is_socks4a) {
     331                packet[4] = 0;
     332                packet[5] = 0;
     333                packet[6] = 0;
     334                packet[7] = 1;
     335        } else {
     336                packet[4] = (unsigned char) (hp->h_addr_list[0])[0];
     337                packet[5] = (unsigned char) (hp->h_addr_list[0])[1];
     338                packet[6] = (unsigned char) (hp->h_addr_list[0])[2];
     339                packet[7] = (unsigned char) (hp->h_addr_list[0])[3];
     340        }
    319341        packet[8] = 0;
    320342        if (write(source, packet, 9) != 9) {
    321                 return phb_close(phb);
     343                return phb_free(phb, FALSE);
     344        }
     345
     346        if (is_socks4a) {
     347                size_t host_len = strlen(phb->host) + 1; /* include the \0 */
     348
     349                if (write(source, phb->host, host_len) != host_len) {
     350                        return phb_free(phb, FALSE);
     351                }
    322352        }
    323353
     
    348378
    349379        if (read(source, buf, 10) < 10) {
    350                 return phb_close(phb);
     380                return phb_free(phb, FALSE);
    351381        }
    352382        if ((buf[0] != 0x05) || (buf[1] != 0x00)) {
    353                 return phb_close(phb);
     383                return phb_free(phb, FALSE);
    354384        }
    355385
    356386        phb->func(phb->data, source, B_EV_IO_READ);
    357         g_free(phb->host);
    358         g_free(phb);
    359 
    360         return FALSE;
     387        return phb_free(phb, TRUE);
    361388}
    362389
     
    377404
    378405        if (write(source, buf, (5 + strlen(phb->host) + 2)) < (5 + strlen(phb->host) + 2)) {
    379                 phb_close(phb);
     406                phb_free(phb, FALSE);
    380407                return;
    381408        }
     
    392419
    393420        if (read(source, buf, 2) < 2) {
    394                 return phb_close(phb);
     421                return phb_free(phb, FALSE);
    395422        }
    396423
    397424        if ((buf[0] != 0x01) || (buf[1] != 0x00)) {
    398                 return phb_close(phb);
     425                return phb_free(phb, FALSE);
    399426        }
    400427
     
    412439
    413440        if (read(source, buf, 2) < 2) {
    414                 return phb_close(phb);
     441                return phb_free(phb, FALSE);
    415442        }
    416443
    417444        if ((buf[0] != 0x05) || (buf[1] == 0xff)) {
    418                 return phb_close(phb);
     445                return phb_free(phb, FALSE);
    419446        }
    420447
     
    427454                memcpy(buf + 2 + i + 1, proxypass, j);
    428455                if (write(source, buf, 3 + i + j) < 3 + i + j) {
    429                         return phb_close(phb);
     456                        return phb_free(phb, FALSE);
    430457                }
    431458
     
    451478        len = sizeof(error);
    452479        if (getsockopt(source, SOL_SOCKET, SO_ERROR, &error, &len) < 0) {
    453                 return phb_close(phb);
     480                return phb_free(phb, FALSE);
    454481        }
    455482        sock_make_blocking(source);
     
    469496
    470497        if (write(source, buf, i) < i) {
    471                 return phb_close(phb);
     498                return phb_free(phb, FALSE);
    472499        }
    473500
     
    487514}
    488515
     516static const proxy_connect_func proxy_connect_funcs_array[] = {
     517        proxy_connect_none,   /* PROXY_NONE */
     518        proxy_connect_http,   /* PROXY_HTTP */
     519        proxy_connect_socks4, /* PROXY_SOCKS4 */
     520        proxy_connect_socks5, /* PROXY_SOCKS5 */
     521        proxy_connect_socks4, /* PROXY_SOCKS4A */
     522};
    489523
    490524/* Export functions */
     
    493527{
    494528        struct PHB *phb;
     529        proxy_connect_func fun;
     530        int fd;
     531
     532        if (!phb_hash) {
     533                phb_hash = g_hash_table_new(g_int_hash, g_int_equal);
     534        }
    495535
    496536        if (!host || port <= 0 || !func || strlen(host) > 128) {
     
    502542        phb->data = data;
    503543
    504         if (proxytype == PROXY_NONE || !proxyhost[0] || proxyport <= 0) {
    505                 return proxy_connect_none(host, port, phb);
    506         } else if (proxytype == PROXY_HTTP) {
    507                 return proxy_connect_http(host, port, phb);
    508         } else if (proxytype == PROXY_SOCKS4) {
    509                 return proxy_connect_socks4(host, port, phb);
    510         } else if (proxytype == PROXY_SOCKS5) {
    511                 return proxy_connect_socks5(host, port, phb);
    512         }
    513 
    514         g_free(phb);
    515         return -1;
    516 }
     544        if (proxyhost[0] && proxyport > 0 && proxytype >= 0 && proxytype < G_N_ELEMENTS(proxy_connect_funcs_array)) {
     545                fun = proxy_connect_funcs_array[proxytype];
     546        } else {
     547                fun = proxy_connect_none;
     548        }
     549
     550        fd = fun(host, port, phb);
     551
     552        if (fd != -1) {
     553                g_hash_table_insert(phb_hash, &phb->fd, phb);
     554        }
     555
     556        return fd;
     557}
     558
     559void proxy_disconnect(int fd)
     560{
     561        struct PHB *phb = g_hash_table_lookup(phb_hash, &fd);
     562
     563        if (!phb) {
     564                /* not in the early part of the connection - just close the fd */
     565                closesocket(fd);
     566                return;
     567        }
     568
     569        if (phb->inpa) {
     570                b_event_remove(phb->inpa);
     571                phb->inpa = 0;
     572        }
     573
     574        /* avoid calling the callback, which might result in double-free */
     575        phb->func = NULL;
     576
     577        /* close and free */
     578        phb_free(phb, FALSE);
     579}
  • lib/proxy.h

    r63cad66 r3fbce97  
    4040#define PROXY_SOCKS4 2
    4141#define PROXY_SOCKS5 3
     42#define PROXY_SOCKS4A 4
    4243
    4344extern char proxyhost[128];
     
    4849
    4950G_MODULE_EXPORT int proxy_connect(const char *host, int port, b_event_handler func, gpointer data);
     51G_MODULE_EXPORT void proxy_disconnect(int fd);
    5052
    5153#endif /* _PROXY_H_ */
  • lib/ssl_gnutls.c

    r63cad66 r3fbce97  
    455455        }
    456456
    457         closesocket(conn->fd);
     457        proxy_disconnect(conn->fd);
    458458
    459459        if (conn->session) {
  • lib/ssl_nss.c

    r63cad66 r3fbce97  
    226226        if (conn->prfd) {
    227227                PR_Close(conn->prfd);
    228         }
    229         if (source >= 0) {
     228        } else if (source >= 0) {
     229                /* proxy_disconnect() would be redundant here */
    230230                closesocket(source);
    231231        }
     
    305305        if (conn->prfd) {
    306306                PR_Close(conn->prfd);
     307        } else if (conn->fd) {
     308                proxy_disconnect(conn->fd);
    307309        }
    308310
  • lib/ssl_openssl.c

    r63cad66 r3fbce97  
    131131                conn->func(conn->data, 1, NULL, cond);
    132132                if (source >= 0) {
    133                         closesocket(source);
     133                        proxy_disconnect(source);
    134134                }
    135135                ssl_conn_free(conn);
     
    276276        }
    277277
    278         closesocket(conn->fd);
     278        proxy_disconnect(conn->fd);
    279279
    280280        ssl_conn_free(conn);
  • lib/url.c

    r63cad66 r3fbce97  
    4848                } else if (g_strncasecmp(set_url, "socks5", i - set_url) == 0) {
    4949                        url->proto = PROTO_SOCKS5;
     50                } else if (g_strncasecmp(set_url, "socks4a", i - set_url) == 0) {
     51                        url->proto = PROTO_SOCKS4A;
    5052                } else {
    5153                        return 0;
  • lib/url.h

    r63cad66 r3fbce97  
    3030#define PROTO_SOCKS4    3
    3131#define PROTO_SOCKS5    4
     32#define PROTO_SOCKS4A   5
    3233#define PROTO_DEFAULT   PROTO_HTTP
    3334
  • log.c

    r63cad66 r3fbce97  
    5151void log_link(int level, int output)
    5252{
    53         /* I know it's ugly, but it works and I didn't feel like messing with pointer to function pointers */
     53        void (*output_function)(int level, const char *logmessage) = &log_null;
     54
     55        if (output == LOGOUTPUT_NULL) {
     56                output_function = &log_null;
     57        } else if (output == LOGOUTPUT_IRC) {
     58                output_function = &log_irc;
     59        } else if (output == LOGOUTPUT_SYSLOG) {
     60                output_function = &log_syslog;
     61        } else if (output == LOGOUTPUT_CONSOLE) {
     62                output_function = &log_console;
     63        }
    5464
    5565        if (level == LOGLVL_INFO) {
    56                 if (output == LOGOUTPUT_NULL) {
    57                         logoutput.informational = &log_null;
    58                 } else if (output == LOGOUTPUT_IRC) {
    59                         logoutput.informational = &log_irc;
    60                 } else if (output == LOGOUTPUT_SYSLOG) {
    61                         logoutput.informational = &log_syslog;
    62                 } else if (output == LOGOUTPUT_CONSOLE) {
    63                         logoutput.informational = &log_console;
    64                 }
     66                logoutput.informational = output_function;
    6567        } else if (level == LOGLVL_WARNING) {
    66                 if (output == LOGOUTPUT_NULL) {
    67                         logoutput.warning = &log_null;
    68                 } else if (output == LOGOUTPUT_IRC) {
    69                         logoutput.warning = &log_irc;
    70                 } else if (output == LOGOUTPUT_SYSLOG) {
    71                         logoutput.warning = &log_syslog;
    72                 } else if (output == LOGOUTPUT_CONSOLE) {
    73                         logoutput.warning = &log_console;
    74                 }
     68                logoutput.warning = output_function;
    7569        } else if (level == LOGLVL_ERROR) {
    76                 if (output == LOGOUTPUT_NULL) {
    77                         logoutput.error = &log_null;
    78                 } else if (output == LOGOUTPUT_IRC) {
    79                         logoutput.error = &log_irc;
    80                 } else if (output == LOGOUTPUT_SYSLOG) {
    81                         logoutput.error = &log_syslog;
    82                 } else if (output == LOGOUTPUT_CONSOLE) {
    83                         logoutput.error = &log_console;
    84                 }
     70                logoutput.error = output_function;
    8571        }
    8672#ifdef DEBUG
    8773        else if (level == LOGLVL_DEBUG) {
    88                 if (output == LOGOUTPUT_NULL) {
    89                         logoutput.debug = &log_null;
    90                 } else if (output == LOGOUTPUT_IRC) {
    91                         logoutput.debug = &log_irc;
    92                 } else if (output == LOGOUTPUT_SYSLOG) {
    93                         logoutput.debug = &log_syslog;
    94                 } else if (output == LOGOUTPUT_CONSOLE) {
    95                         logoutput.debug = &log_console;
    96                 }
     74                logoutput.debug = output_function;
    9775        }
    9876#endif
  • otr.c

    r63cad66 r3fbce97  
    187187/* update op/voice flag of given user according to encryption state and settings
    188188   returns 0 if neither op_buddies nor voice_buddies is set to "encrypted",
    189    i.e. msgstate should be announced seperately */
     189   i.e. msgstate should be announced separately */
    190190int otr_update_modeflags(irc_t *irc, irc_user_t *u);
    191191
     
    216216void otr_disconnect_all(irc_t *irc);
    217217
     218/* modifies string in-place, replacing \x03 with '?',
     219   as a quick way to prevent remote users from messing with irc colors */
     220static char *otr_filter_colors(char *msg);
     221
    218222/* functions to be called for certain events */
    219223static const struct irc_plugin otr_plugin;
     224
     225#define OTR_COLOR_TRUSTED "03"     /* green */
     226#define OTR_COLOR_UNTRUSTED "05"   /* red */
    220227
    221228/*** routines declared in otr.h: ***/
     
    434441
    435442        /* don't do OTR on certain (not classic IM) protocols, e.g. twitter */
    436         if (ic->acc->prpl->options & OPT_NOOTR) {
     443        if (ic->acc->prpl->options & OPT_NOOTR ||
     444            iu->bu->flags & BEE_USER_NOOTR) {
    437445                return msg;
    438446        }
     
    451459        } else if (!newmsg) {
    452460                /* this was a non-OTR message */
    453                 return msg;
     461                return otr_filter_colors(msg);
    454462        } else {
    455463                /* we're done with the original msg, which will be caller-freed. */
     
    472480
    473481        /* don't do OTR on certain (not classic IM) protocols, e.g. twitter */
    474         if (ic->acc->prpl->options & OPT_NOOTR) {
     482        if (ic->acc->prpl->options & OPT_NOOTR ||
     483            iu->bu->flags & BEE_USER_NOOTR) {
    475484                return msg;
    476485        }
     
    742751}
    743752
     753static char *otr_filter_colors(char *msg)
     754{
     755        return str_reject_chars(msg, "\x02\x03", '?');
     756}
     757
     758/* returns newly allocated string */
     759static char *otr_color_encrypted(char *msg, char *color, gboolean is_query) {
     760        char **lines;
     761        GString *out;
     762        int i;
     763
     764        lines = g_strsplit(msg, "\n", -1);
     765
     766        /* up to 4 extra chars per line (e.g., '\x03' + ("03"|"05") + ' ') */
     767        out = g_string_sized_new(strlen(msg) + g_strv_length(lines) * 4);
     768       
     769        for (i = 0; lines[i]; i++) {
     770                char *line = lines[i];
     771
     772                if (i != 0) {
     773                        g_string_append_c(out, '\n');
     774
     775                } else if (is_query && g_strncasecmp(line, "/me ", 4) == 0) {
     776                        /* in a query window, keep "/me " uncolored at the beginning */
     777                        line += 4;
     778                        g_string_append(out, "/me ");
     779                }
     780
     781                g_string_append_c(out, '\x03');
     782                g_string_append(out, color);
     783
     784                /* comma in first place could mess with the color code */
     785                if (line[0] == ',') {
     786                        /* insert a space between color spec and message */
     787                        g_string_append_c(out, ' ');
     788                }
     789
     790                g_string_append(out, otr_filter_colors(line));
     791        }
     792
     793        g_strfreev(lines);
     794
     795        return g_string_free(out, FALSE);
     796}
     797
    744798void op_convert_msg(void *opdata, ConnContext *ctx, OtrlConvertType typ,
    745799                    char **dst, const char *src)
     
    752806        if (typ == OTRL_CONVERT_RECEIVING) {
    753807                char *msg = g_strdup(src);
    754                 char *buf = msg;
    755808
    756809                /* HTML decoding */
     
    759812                    set_getbool(&ic->bee->set, "strip_html")) {
    760813                        strip_html(msg);
     814
     815                        /* msg is borrowed by *dst (unless the next if decides to color it) */
    761816                        *dst = msg;
    762817                }
     
    764819                /* coloring */
    765820                if (set_getbool(&ic->bee->set, "otr_color_encrypted")) {
    766                         int color;                /* color according to f'print trust */
    767                         char *pre = "", *sep = "";    /* optional parts */
    768821                        const char *trust = ctx->active_fingerprint->trust;
    769 
    770                         if (trust && trust[0] != '\0') {
    771                                 color = 3;   /* green */
    772                         } else {
    773                                 color = 5;   /* red */
    774 
    775                         }
    776                         /* in a query window, keep "/me " uncolored at the beginning */
    777                         if (g_strncasecmp(msg, "/me ", 4) == 0
    778                             && irc_user_msgdest(iu) == irc->user->nick) {
    779                                 msg += 4;  /* skip */
    780                                 pre = "/me ";
    781                         }
    782 
    783                         /* comma in first place could mess with the color code */
    784                         if (msg[0] == ',') {
    785                                 /* insert a space between color spec and message */
    786                                 sep = " ";
    787                         }
    788 
    789                         *dst = g_strdup_printf("%s\x03%.2d%s%s\x0F", pre,
    790                                                color, sep, msg);
    791                         g_free(buf);
     822                        char *color = (trust && *trust) ? OTR_COLOR_TRUSTED : OTR_COLOR_UNTRUSTED;
     823                        gboolean is_query = (irc_user_msgdest(iu) == irc->user->nick);
     824
     825                        /* the return value of otr_color_encrypted() is borrowed by *dst */
     826                        *dst = otr_color_encrypted(msg, color, is_query);
     827
     828                        /* this branch doesn't need msg */
     829                        g_free(msg);
    792830                }
    793831        } else {
     
    13571395
    13581396        if (u) {
    1359                 /* display as a notice from this particular user */
    1360                 irc_usernotice(u, "%s", msg);
     1397                /* just show this as a regular message */
     1398                irc_usermsg(u, "<<\002OTR\002>> %s", msg);
    13611399        } else {
    13621400                irc_rootmsg(irc, "[otr] %s", msg);
     
    16951733        *p = '\0';
    16961734
     1735        /* remove trailing whitespace */
     1736        g_strchomp(prefix);
     1737
    16971738        /* find first key which matches the given prefix */
    16981739        n = strlen(prefix);
  • protocols/account.c

    r63cad66 r3fbce97  
    8484                    strstr(a->user, "@googlemail.com")) {
    8585                        strcpy(tag, "gtalk");
    86                 } else if (strstr(a->user, "@chat.facebook.com")) {
    87                         strcpy(tag, "fb");
    8886                }
    8987        }
  • protocols/bee.h

    r63cad66 r3fbce97  
    6262        BEE_USER_LOCAL = 256,   /* Locally-added contacts (not in real contact list) */
    6363        BEE_USER_SPECIAL = 512, /* Denotes a user as being special */
     64        BEE_USER_NOOTR = 4096,  /* Per-user version of OPT_NOOTR */
    6465} bee_user_flags_t;
    6566
     
    104105        gboolean (*user_status)(bee_t *bee, struct bee_user *bu, struct bee_user *old);
    105106        /* On every incoming message. sent_at = 0 means unknown. */
    106         gboolean (*user_msg)(bee_t *bee, bee_user_t *bu, const char *msg, time_t sent_at);
     107        gboolean (*user_msg)(bee_t *bee, bee_user_t *bu, const char *msg, guint32 flags, time_t sent_at);
    107108        /* Flags currently defined (OPT_TYPING/THINKING) in nogaim.h. */
    108109        gboolean (*user_typing)(bee_t *bee, bee_user_t *bu, guint32 flags);
     
    117118        /* System messages of any kind. */
    118119        gboolean (*chat_log)(bee_t *bee, struct groupchat *c, const char *text);
    119         gboolean (*chat_msg)(bee_t *bee, struct groupchat *c, bee_user_t *bu, const char *msg, time_t sent_at);
     120        gboolean (*chat_msg)(bee_t *bee, struct groupchat *c, bee_user_t *bu, const char *msg, guint32 flags, time_t sent_at);
    120121        gboolean (*chat_add_user)(bee_t *bee, struct groupchat *c, bee_user_t *bu);
    121         gboolean (*chat_remove_user)(bee_t *bee, struct groupchat *c, bee_user_t *bu);
     122        gboolean (*chat_remove_user)(bee_t *bee, struct groupchat *c, bee_user_t *bu, const char *reason);
    122123        gboolean (*chat_topic)(bee_t *bee, struct groupchat *c, const char *new_topic, bee_user_t *bu);
    123124        gboolean (*chat_name_hint)(bee_t *bee, struct groupchat *c, const char *name);
     
    128129        void (*ft_close)(struct im_connection *ic, struct file_transfer *ft);
    129130        void (*ft_finished)(struct im_connection *ic, struct file_transfer *ft);
     131
     132        void (*log)(bee_t *bee, const char *tag, const char *msg);
    130133} bee_ui_funcs_t;
    131134
  • protocols/bee_chat.c

    r63cad66 r3fbce97  
    9595}
    9696
    97 void imcb_chat_msg(struct groupchat *c, const char *who, char *msg, uint32_t flags, time_t sent_at)
     97void imcb_chat_msg(struct groupchat *c, const char *who, char *msg, guint32 flags, time_t sent_at)
    9898{
    9999        struct im_connection *ic = c->ic;
    100100        bee_t *bee = ic->bee;
    101101        bee_user_t *bu;
    102         gboolean temp;
     102        gboolean temp = FALSE;
    103103        char *s;
    104104
    105         /* Gaim sends own messages through this too. IRC doesn't want this, so kill them */
    106         if (handle_is_self(ic, who)) {
     105        if (handle_is_self(ic, who) && !(flags & OPT_SELFMESSAGE)) {
    107106                return;
    108107        }
     
    122121
    123122        if (bee->ui->chat_msg) {
    124                 bee->ui->chat_msg(bee, c, bu, msg, sent_at);
     123                bee->ui->chat_msg(bee, c, bu, msg, flags, sent_at);
    125124        }
    126125
     
    230229
    231230        if (bee->ui->chat_remove_user && bu) {
    232                 bee->ui->chat_remove_user(bee, c, bu);
     231                bee->ui->chat_remove_user(bee, c, bu, reason);
    233232        }
    234233}
  • protocols/bee_user.c

    r63cad66 r3fbce97  
    247247}
    248248
    249 void imcb_buddy_msg(struct im_connection *ic, const char *handle, const char *msg, uint32_t flags, time_t sent_at)
     249void imcb_buddy_msg(struct im_connection *ic, const char *handle, const char *msg, guint32 flags, time_t sent_at)
    250250{
    251251        bee_t *bee = ic->bee;
     
    265265
    266266        if (bee->ui->user_msg && bu) {
    267                 bee->ui->user_msg(bee, bu, msg, sent_at);
     267                bee->ui->user_msg(bee, bu, msg, flags, sent_at);
    268268        } else {
    269269                imcb_log(ic, "Message from unknown handle %s:\n%s", handle, msg);
     
    297297}
    298298
    299 void imcb_buddy_typing(struct im_connection *ic, const char *handle, uint32_t flags)
     299void imcb_buddy_typing(struct im_connection *ic, const char *handle, guint32 flags)
    300300{
    301301        bee_user_t *bu;
  • protocols/ft.h

    r63cad66 r3fbce97  
    5858 *                                    | accept
    5959 *                                    V
    60  *                     /------ /-------------\                    /------------------------\
    61  *         out_of_data |       | TRANSFERING | -----------------> | TRANSFERING | CANCELED |
    62  *                     \-----> \-------------/  [canceled,]free   \------------------------/
     60 *                     /------ /-------------\                    /--------------------------\
     61 *         out_of_data |       | TRANSFERRING | -----------------> | TRANSFERRING | CANCELED |
     62 *                     \-----> \-------------/  [canceled,]free   \--------------------------/
    6363 *                                    |
    6464 *                                    | finished,free
    6565 *                                    V
    66  *                       /------------------------\
    67  *                       | TRANSFERING | FINISHED |
    68  *                       \------------------------/
     66 *                       /-------------------------\
     67 *                       | TRANSFERRING | FINISHED |
     68 *                       \-------------------------/
    6969 */
    7070typedef struct file_transfer {
     
    115115
    116116        /*
    117          * If set, called after succesful connection setup.
     117         * If set, called after successful connection setup.
    118118         */
    119119        void (*accept)(struct file_transfer *file);
  • protocols/jabber/conference.c

    r63cad66 r3fbce97  
    2626
    2727static xt_status jabber_chat_join_failed(struct im_connection *ic, struct xt_node *node, struct xt_node *orig);
     28static xt_status jabber_chat_self_message(struct im_connection *ic, struct xt_node *node, struct xt_node *orig);
    2829
    2930struct groupchat *jabber_chat_join(struct im_connection *ic, const char *room, const char *nick, const char *password)
     
    121122        }
    122123        if (bud) {
    123                 jabber_chat_free(jabber_chat_by_jid(ic, bud->bare_jid));
     124                struct groupchat *c = jabber_chat_by_jid(ic, bud->bare_jid);
     125                if (c) {
     126                        jabber_chat_free(c);
     127                }
    124128        }
    125129
    126130        return XT_HANDLED;
     131}
     132
     133static xt_status jabber_chat_self_message(struct im_connection *ic, struct xt_node *node, struct xt_node *orig)
     134{
     135        /* This is a self message sent by this bitlbee - just drop it */
     136        return XT_ABORT;
    127137}
    128138
     
    171181        node = jabber_make_packet("message", "groupchat", jc->name, node);
    172182
    173         if (!jabber_write_packet(ic, node)) {
    174                 xt_free_node(node);
    175                 return 0;
    176         }
    177         xt_free_node(node);
    178 
    179         return 1;
     183        jabber_cache_add(ic, node, jabber_chat_self_message);
     184
     185        return !jabber_write_packet(ic, node);
    180186}
    181187
     
    299305                                        }
    300306                                }
    301 
    302                                 /* Some program-specific restrictions. */
    303                                 imcb_clean_handle(ic, bud->ext_jid);
    304307                        }
    305308                        bud->flags |= JBFLAG_IS_ANONYMOUS;
     
    331334        } else if (type) { /* type can only be NULL or "unavailable" in this function */
    332335                if ((bud->flags & JBFLAG_IS_CHATROOM) && bud->ext_jid) {
     336                        char *reason = NULL;
     337                        char *status = NULL;
     338                        char *status_text = NULL;
     339                       
     340                        if ((c = xt_find_node_by_attr(node->children, "x", "xmlns", XMLNS_MUC_USER))) {
     341                                struct xt_node *c2 = c->children;
     342
     343                                while ((c2 = xt_find_node(c2, "status"))) {
     344                                        char *code = xt_find_attr(c2, "code");
     345                                        if (g_strcmp0(code, "301") == 0) {
     346                                                status = "Banned";
     347                                                break;
     348                                        } else if (g_strcmp0(code, "303") == 0) {
     349                                                /* This could be handled in a cleverer way,
     350                                                 * but let's just show a literal part/join for now */
     351                                                status = "Changing nicks";
     352                                                break;
     353                                        } else if (g_strcmp0(code, "307") == 0) {
     354                                                status = "Kicked";
     355                                                break;
     356                                        }
     357                                        c2 = c2->next;
     358                                }
     359
     360                                /* Sometimes the status message is in presence/x/item/reason */
     361                                if ((c2 = xt_find_path(c, "item/reason")) && c2->text && c2->text_len) {
     362                                        status_text = c2->text;
     363                                }
     364                        }
     365
     366                        /* Sometimes the status message is right inside <presence> */
     367                        if ((c = xt_find_node(node->children, "status")) && c->text && c->text_len) {
     368                                status_text = c->text;
     369                        }
     370
     371                        if (status_text && status) {
     372                                reason = g_strdup_printf("%s: %s", status, status_text);
     373                        } else {
     374                                reason = g_strdup(status_text ? : status);
     375                        }
     376
    333377                        s = strchr(bud->ext_jid, '/');
    334378                        if (s) {
    335379                                *s = 0;
    336380                        }
    337                         imcb_chat_remove_buddy(chat, bud->ext_jid, NULL);
     381                        imcb_chat_remove_buddy(chat, bud->ext_jid, reason);
    338382                        if (bud != jc->me && bud->flags & JBFLAG_IS_ANONYMOUS) {
    339                                 imcb_remove_buddy(ic, bud->ext_jid, NULL);
     383                                imcb_remove_buddy(ic, bud->ext_jid, reason);
    340384                        }
    341385                        if (s) {
    342386                                *s = '/';
    343387                        }
     388
     389                        g_free(reason);
    344390                }
    345391
     
    360406        char *final_from = NULL;
    361407        char *bare_jid = NULL;
     408        guint32 flags = 0;
    362409
    363410        from = (bud) ? bud->full_jid : xt_find_attr(node, "from");
     
    396443
    397444        if (subject && chat) {
    398                 char *subject_text = subject->text_len > 0 ? subject->text : NULL;
     445                char *subject_text = subject->text_len > 0 ? subject->text : "";
    399446                if (g_strcmp0(chat->topic, subject_text) != 0) {
    400447                        bare_jid = (bud) ? jabber_get_bare_jid(bud->ext_jid) : NULL;
     
    402449                                        jabber_get_timestamp(node));
    403450                        g_free(bare_jid);
     451                        bare_jid = NULL;
    404452                }
    405453        }
     
    422470                imcb_chat_log(chat, "From conference server: %s", body->text);
    423471                return;
    424         } else if (jc && jc->flags & JCFLAG_MESSAGE_SENT && bud == jc->me) {
    425                 /* exclude self-messages since they would get filtered out
    426                  * but not the ones in the backlog */
     472        } else if (jc && jc->flags & JCFLAG_MESSAGE_SENT && bud == jc->me &&
     473                   (jabber_cache_handle_packet(ic, node) == XT_ABORT)) {
     474                /* Self message marked by this bitlbee, don't show it */
    427475                return;
    428476        }
    429477
    430         if (bud && jc && bud != jc->me) {
     478        if (bud) {
    431479                bare_jid = jabber_get_bare_jid(bud->ext_jid ? bud->ext_jid : bud->full_jid);
    432480                final_from = bare_jid;
     481                flags = (bud == jc->me) ? OPT_SELFMESSAGE : 0;
    433482        } else {
    434483                final_from = nick;
    435484        }
    436485
    437         imcb_chat_msg(chat, final_from, body->text, 0, jabber_get_timestamp(node));
     486        imcb_chat_msg(chat, final_from, body->text, flags, jabber_get_timestamp(node));
    438487
    439488        g_free(bare_jid);
  • protocols/jabber/hipchat.c

    r63cad66 r3fbce97  
    4242                *sep = '/';
    4343        }
     44
     45        jd->muc_host = g_strdup(xt_find_attr(node, "muc_host"));
    4446
    4547        /* Hipchat's auth doesn't expect a restart here */
     
    9294
    9395}
     96
     97/* Returns a newly allocated string that tries to match the "slug" part of the JID using an
     98 * approximation of the method used by the server. This might fail in some rare conditions
     99 * (old JIDs generated a different way, locale settings unicode, etc) */
     100char *hipchat_make_channel_slug(const char *name)
     101{
     102        char *lower;
     103        char *new = g_malloc(strlen(name) + 1);
     104        int i = 0;
     105
     106        do {
     107                if (*name == ' ') {
     108                        new[i++] = '_';
     109                } else if (*name && !strchr("\"&'/:<>@", *name)) {
     110                        new[i++] = *name;
     111                }
     112        } while (*(name++));
     113
     114        new[i] = '\0';
     115
     116        lower = g_utf8_strdown(new, -1);
     117        g_free(new);
     118
     119        return lower;
     120}
     121
     122char *hipchat_guess_channel_name(struct im_connection *ic, const char *name)
     123{
     124        struct jabber_data *jd = ic->proto_data;
     125        char *slug, *retval, *underscore;
     126       
     127        if (!(underscore = strchr(jd->username, '_')) || !jd->muc_host) {
     128                return NULL;
     129        }
     130
     131        slug = hipchat_make_channel_slug(name);
     132
     133        /* Get the organization ID from the username, before the underscore */
     134        *underscore = '\0';
     135
     136        retval = g_strdup_printf("%s_%s@%s", jd->username, slug, jd->muc_host);
     137
     138        *underscore = '_';
     139
     140        g_free(slug);
     141
     142        return retval;
     143}
  • protocols/jabber/io.c

    r63cad66 r3fbce97  
    147147}
    148148
    149 static gboolean jabber_read_callback(gpointer data, gint fd, b_input_condition cond)
    150 {
    151         struct im_connection *ic = data;
    152         struct jabber_data *jd = ic->proto_data;
    153         char buf[512];
    154         int st;
    155 
    156         if (jd->fd == -1) {
    157                 return FALSE;
    158         }
    159 
    160         if (jd->ssl) {
    161                 st = ssl_read(jd->ssl, buf, sizeof(buf));
    162         } else {
    163                 st = read(jd->fd, buf, sizeof(buf));
    164         }
    165 
    166         if (st > 0) {
    167                 /* Parse. */
    168                 if (xt_feed(jd->xt, buf, st) < 0) {
     149static gboolean jabber_feed_input(struct im_connection *ic, char *buf, int size)
     150{
     151        struct jabber_data *jd = ic->proto_data;
     152
     153        /* Allow not passing a size for debugging purposes.
     154         * This never happens when reading from the socket */
     155        if (size == -1) {
     156                size = strlen(buf);
     157        }
     158
     159        /* Parse. */
     160        if (xt_feed(jd->xt, buf, size) < 0) {
     161                imcb_error(ic, "XML stream error");
     162                imc_logout(ic, TRUE);
     163                return FALSE;
     164        }
     165
     166        /* Execute all handlers. */
     167        if (!xt_handle(jd->xt, NULL, 1)) {
     168                /* Don't do anything, the handlers should have
     169                   aborted the connection already. */
     170                return FALSE;
     171        }
     172
     173        if (jd->flags & JFLAG_STREAM_RESTART) {
     174                jd->flags &= ~JFLAG_STREAM_RESTART;
     175                jabber_start_stream(ic);
     176        }
     177
     178        /* Garbage collection. */
     179        xt_cleanup(jd->xt, NULL, 1);
     180
     181        /* This is a bit hackish, unfortunately. Although xmltree
     182           has nifty event handler stuff, it only calls handlers
     183           when nodes are complete. Since the server should only
     184           send an opening <stream:stream> tag, we have to check
     185           this by hand. :-( */
     186        if (!(jd->flags & JFLAG_STREAM_STARTED) && jd->xt && jd->xt->root) {
     187                if (g_strcasecmp(jd->xt->root->name, "stream:stream") == 0) {
     188                        jd->flags |= JFLAG_STREAM_STARTED;
     189
     190                        /* If there's no version attribute, assume
     191                           this is an old server that can't do SASL
     192                           authentication. */
     193                        if (!set_getbool(&ic->acc->set, "sasl") || !sasl_supported(ic)) {
     194                                /* If there's no version= tag, we suppose
     195                                   this server does NOT implement: XMPP 1.0,
     196                                   SASL and TLS. */
     197                                if (set_getbool(&ic->acc->set, "tls")) {
     198                                        imcb_error(ic, "TLS is turned on for this "
     199                                                   "account, but is not supported by this server");
     200                                        imc_logout(ic, FALSE);
     201                                        return FALSE;
     202                                } else {
     203                                        if (!jabber_init_iq_auth(ic)) {
     204                                                return FALSE;
     205                                        }
     206                                }
     207                        }
     208                } else {
    169209                        imcb_error(ic, "XML stream error");
    170210                        imc_logout(ic, TRUE);
    171211                        return FALSE;
    172212                }
    173 
    174                 /* Execute all handlers. */
    175                 if (!xt_handle(jd->xt, NULL, 1)) {
    176                         /* Don't do anything, the handlers should have
    177                            aborted the connection already. */
     213        }
     214
     215        return TRUE;
     216}
     217
     218
     219static gboolean jabber_read_callback(gpointer data, gint fd, b_input_condition cond)
     220{
     221        struct im_connection *ic = data;
     222        struct jabber_data *jd = ic->proto_data;
     223        char buf[512];
     224        int st;
     225
     226        if (jd->fd == -1) {
     227                return FALSE;
     228        }
     229
     230        if (jd->ssl) {
     231                st = ssl_read(jd->ssl, buf, sizeof(buf));
     232        } else {
     233                st = read(jd->fd, buf, sizeof(buf));
     234        }
     235
     236        if (st > 0) {
     237                if (!jabber_feed_input(ic, buf, st)) {
    178238                        return FALSE;
    179                 }
    180 
    181                 if (jd->flags & JFLAG_STREAM_RESTART) {
    182                         jd->flags &= ~JFLAG_STREAM_RESTART;
    183                         jabber_start_stream(ic);
    184                 }
    185 
    186                 /* Garbage collection. */
    187                 xt_cleanup(jd->xt, NULL, 1);
    188 
    189                 /* This is a bit hackish, unfortunately. Although xmltree
    190                    has nifty event handler stuff, it only calls handlers
    191                    when nodes are complete. Since the server should only
    192                    send an opening <stream:stream> tag, we have to check
    193                    this by hand. :-( */
    194                 if (!(jd->flags & JFLAG_STREAM_STARTED) && jd->xt && jd->xt->root) {
    195                         if (g_strcasecmp(jd->xt->root->name, "stream:stream") == 0) {
    196                                 jd->flags |= JFLAG_STREAM_STARTED;
    197 
    198                                 /* If there's no version attribute, assume
    199                                    this is an old server that can't do SASL
    200                                    authentication. */
    201                                 if (!set_getbool(&ic->acc->set, "sasl") || !sasl_supported(ic)) {
    202                                         /* If there's no version= tag, we suppose
    203                                            this server does NOT implement: XMPP 1.0,
    204                                            SASL and TLS. */
    205                                         if (set_getbool(&ic->acc->set, "tls")) {
    206                                                 imcb_error(ic, "TLS is turned on for this "
    207                                                            "account, but is not supported by this server");
    208                                                 imc_logout(ic, FALSE);
    209                                                 return FALSE;
    210                                         } else {
    211                                                 return jabber_init_iq_auth(ic);
    212                                         }
    213                                 }
    214                         } else {
    215                                 imcb_error(ic, "XML stream error");
    216                                 imc_logout(ic, TRUE);
    217                                 return FALSE;
    218                         }
    219239                }
    220240        } else if (st == 0 || (st < 0 && !ssl_sockerr_again(jd->ssl))) {
  • protocols/jabber/iq.c

    r63cad66 r3fbce97  
    2828static xt_status jabber_iq_display_vcard(struct im_connection *ic, struct xt_node *node, struct xt_node *orig);
    2929static xt_status jabber_gmail_handle_new(struct im_connection *ic, struct xt_node *node);
     30static xt_status jabber_iq_carbons_response(struct im_connection *ic, struct xt_node *node, struct xt_node *orig);
    3031
    3132xt_status jabber_pkt_iq(struct xt_node *node, gpointer data)
     
    5253                      (c = xt_find_node(node->children, "time"))) ||
    5354                    !(s = xt_find_attr(c, "xmlns"))) {
    54                         /* Sigh. Who decided to suddenly invent new elements
    55                            instead of just sticking with <query/>? */
    56                         return XT_HANDLED;
     55
     56                        reply = jabber_make_error_packet(node, "service-unavailable", "cancel", NULL);
     57                        st = jabber_write_packet(ic, reply);
     58                        xt_free_node(reply);
     59                        return st;
    5760                }
    5861
     
    118121                                                   XMLNS_BYTESTREAMS,
    119122                                                   XMLNS_FILETRANSFER,
     123                                                   XMLNS_CARBONS,
    120124                                                   NULL };
    121125                        const char **f;
     
    134138                } else {
    135139                        xt_free_node(reply);
    136                         reply = jabber_make_error_packet(node, "feature-not-implemented", "cancel", NULL);
     140                        reply = jabber_make_error_packet(node, "service-unavailable", "cancel", NULL);
    137141                        pack = 0;
    138142                }
     
    385389        struct xt_node *response;
    386390        struct jabber_data *jd = ic->proto_data;
    387 
    388         response = jabber_make_packet("iq", "result", g_strdup_printf("%s@%s", jd->username, jd->server), NULL);
     391       
     392        response = jabber_make_packet("iq", "result", jd->me, NULL);
    389393
    390394        jabber_cache_add(ic, response, NULL);
     
    10041008{
    10051009        struct jabber_data *jd = ic->proto_data;
    1006         struct xt_node *id;
    1007 
    1008         if ((id = xt_find_path(node, "query/identity"))) {
     1010        struct xt_node *query, *id;
     1011
     1012        if (!(query = xt_find_node(node->children, "query"))) {
     1013                return XT_HANDLED;
     1014        }
     1015
     1016        if (xt_find_node_by_attr(query->children, "feature", "var", XMLNS_CARBONS) &&
     1017            set_getbool(&ic->acc->set, "carbons")) {
     1018
     1019                struct xt_node *enable, *iq;
     1020
     1021                enable = xt_new_node("enable", NULL, NULL);
     1022                xt_add_attr(enable, "xmlns", XMLNS_CARBONS);
     1023                iq = jabber_make_packet("iq", "set", NULL, enable);
     1024
     1025                jabber_cache_add(ic, iq, jabber_iq_carbons_response);
     1026                jabber_write_packet(ic, iq);
     1027        }
     1028
     1029        if ((id = xt_find_node(query->children, "identity"))) {
    10091030                char *cat, *type, *name;
    10101031
     
    10231044        return XT_HANDLED;
    10241045}
     1046
     1047static xt_status jabber_iq_carbons_response(struct im_connection *ic,
     1048                                            struct xt_node *node, struct xt_node *orig)
     1049{
     1050        struct jabber_error *err;
     1051
     1052        if ((err = jabber_error_parse(xt_find_node(node->children, "error"), XMLNS_STANZA_ERROR))) {
     1053                imcb_error(ic, "Error enabling carbons: %s%s%s",
     1054                           err->code, err->text ? ": " : "", err->text ? err->text : "");
     1055                jabber_error_free(err);
     1056        } else {
     1057                imcb_log(ic, "Carbons enabled");
     1058        }
     1059
     1060        return XT_HANDLED;
     1061}
  • protocols/jabber/jabber.c

    r63cad66 r3fbce97  
    114114        s->flags |= ACC_SET_OFFLINE_ONLY | SET_NULL_OK;
    115115
     116        s = set_add(&acc->set, "carbons", "true", set_eval_bool, acc);
     117        s->flags |= ACC_SET_OFFLINE_ONLY;
     118
    116119        acc->flags |= ACC_FLAG_AWAY_MESSAGE | ACC_FLAG_STATUS_MESSAGE |
    117120                      ACC_FLAG_HANDLE_DOMAINS;
     
    148151        }
    149152
     153        if (strstr(jd->server, ".facebook.com")) {
     154                imcb_error(ic, "Facebook's XMPP service is gone. Try this instead: https://wiki.bitlbee.org/HowtoFacebookMQTT");
     155                imc_logout(ic, FALSE);
     156                return;
     157        }
     158
    150159        if ((s = strchr(jd->server, '/'))) {
    151160                *s = 0;
     
    167176                jd->fd = jd->r_inpa = jd->w_inpa = -1;
    168177
    169                 if (strstr(jd->server, ".facebook.com")) {
    170                         jd->oauth2_service = &oauth2_service_facebook;
    171                 } else {
    172                         jd->oauth2_service = &oauth2_service_google;
    173                 }
     178                /* There are no other options atm, so assume google for everything
     179                   Facebook and MSN XMPP used to be here. RIP. */
     180                jd->oauth2_service = &oauth2_service_google;
    174181
    175182                oauth_params_parse(&p_in, ic->acc->pass);
     
    197204        } else {
    198205                jabber_connect(ic);
     206        }
     207}
     208
     209static void jabber_xmlconsole_enable(struct im_connection *ic)
     210{
     211        struct jabber_data *jd = ic->proto_data;
     212        const char *handle = JABBER_XMLCONSOLE_HANDLE;
     213        bee_user_t *bu;
     214       
     215        jd->flags |= JFLAG_XMLCONSOLE;
     216
     217        if (!(bu = bee_user_by_handle(ic->bee, ic, handle))) {
     218                bu = bee_user_new(ic->bee, ic, handle, 0);
     219                bu->flags |= BEE_USER_NOOTR;
    199220        }
    200221}
     
    266287
    267288        if (set_getbool(&acc->set, "xmlconsole")) {
    268                 jd->flags |= JFLAG_XMLCONSOLE;
    269                 /* Shouldn't really do this at this stage already, maybe. But
    270                    I think this shouldn't break anything. */
    271                 imcb_add_buddy(ic, JABBER_XMLCONSOLE_HANDLE, NULL);
     289                jabber_xmlconsole_enable(ic);
    272290        }
    273291
     
    334352        }
    335353        if (jd->fd >= 0) {
    336                 closesocket(jd->fd);
     354                proxy_disconnect(jd->fd);
    337355        }
    338356
     
    345363        }
    346364
    347         jabber_buddy_remove_all(ic);
     365        if (jd->buddies) {
     366                jabber_buddy_remove_all(ic);
     367        }
    348368
    349369        xt_free(jd->xt);
     
    355375        g_free(jd->internal_jid);
    356376        g_free(jd->gmail_tid);
     377        g_free(jd->muc_host);
    357378        g_free(jd->username);
    358379        g_free(jd->me);
     
    413434        }
    414435
     436        /* XEP-0364 suggests we add message processing hints (XEP-0334) to OTR messages,
     437           mostly to avoid carbons (XEP-0280) and server-side message archiving.
     438           OTR messages are roughly like this: /^\?OTR(.*\?| Error:|:)/
     439           But I'm going to simplify it to messages starting with "?OTR". */
     440        if (g_str_has_prefix(message, "?OTR")) {
     441                int i;
     442                char *hints[] = {
     443                        "no-copy", XMLNS_HINTS,
     444                        "no-permanent-store", XMLNS_HINTS,
     445                        "private", XMLNS_CARBONS,
     446                        NULL
     447                };
     448                       
     449                for (i = 0; hints[i]; i += 2) {
     450                        struct xt_node *hint;
     451                        hint = xt_new_node(hints[i], NULL, NULL);
     452                        xt_add_attr(hint, "xmlns", hints[i + 1]);
     453                        xt_add_child(node, hint);
     454                }
     455        }
     456
    415457        st = jabber_write_packet(ic, node);
    416458        xt_free_node(node);
     
    472514static void jabber_add_buddy(struct im_connection *ic, char *who, char *group)
    473515{
    474         struct jabber_data *jd = ic->proto_data;
    475 
    476516        if (g_strcasecmp(who, JABBER_XMLCONSOLE_HANDLE) == 0) {
    477                 jd->flags |= JFLAG_XMLCONSOLE;
    478                 imcb_add_buddy(ic, JABBER_XMLCONSOLE_HANDLE, NULL);
     517                jabber_xmlconsole_enable(ic);
    479518                return;
    480519        }
     
    520559        }
    521560
     561        if (jd->flags & JFLAG_HIPCHAT && jd->muc_host && !g_str_has_suffix(room, jd->muc_host)) {
     562                char *guessed_name = hipchat_guess_channel_name(ic, room);
     563                if (guessed_name) {
     564                        set_setstr(sets, "room", guessed_name);
     565                        g_free(guessed_name);
     566
     567                        /* call this same function again with the fixed name */
     568                        return jabber_chat_join_(ic, set_getstr(sets, "room"), nick, password, sets);
     569                }
     570        }
     571
    522572        if (strchr(room, '@') == NULL) {
    523573                imcb_error(ic, "%s is not a valid Jabber room name. Maybe you mean %s@conference.%s?",
     
    526576                imcb_error(ic, "Already present in chat `%s'", room);
    527577        } else {
     578                /* jabber_chat_join without the underscore is the conference.c one */
    528579                return jabber_chat_join(ic, room, final_nick, set_getstr(sets, "password"));
    529580        }
     
    590641{
    591642        struct jabber_data *jd = ic->proto_data;
    592         struct jabber_buddy *bud;
     643        struct jabber_buddy *bud, *bare;
    593644
    594645        /* Enable typing notification related code from now. */
    595646        jd->flags |= JFLAG_WANT_TYPING;
    596647
    597         if ((bud = jabber_buddy_by_jid(ic, who, 0)) == NULL) {
     648        if ((bud = jabber_buddy_by_jid(ic, who, 0)) == NULL ||
     649            (bare = jabber_buddy_by_jid(ic, who, GET_BUDDY_BARE)) == NULL) {
    598650                /* Sending typing notifications to unknown buddies is
    599651                   unsupported for now. Shouldn't be a problem, I think. */
     
    601653        }
    602654
    603         if (bud->flags & JBFLAG_DOES_XEP85) {
     655
     656        if (bud->flags & JBFLAG_DOES_XEP85 || bare->flags & JBFLAG_DOES_XEP85) {
    604657                /* We're only allowed to send this stuff if we know the other
    605                    side supports it. */
     658                   side supports it. If the bare JID has the flag, all other
     659                   resources get it, too (That is the case in gtalk) */
    606660
    607661                struct xt_node *node;
  • protocols/jabber/jabber.h

    r63cad66 r3fbce97  
    5050        JFLAG_GTALK =  0x100000,        /* Is Google Talk, as confirmed by iq discovery */
    5151        JFLAG_HIPCHAT = 0x200000,       /* Is hipchat, because prpl->name says so */
    52 
    53         JFLAG_SASL_FB = 0x10000,        /* Trying Facebook authentication. */
    5452} jabber_flags_t;
    5553
     
    113111        GSList *streamhosts;
    114112        int have_streamhosts;
     113
     114        char *muc_host;
    115115};
    116116
     
    228228#define XMLNS_XDATA        "jabber:x:data"                                       /* XEP-0004 */
    229229#define XMLNS_GMAILNOTIFY  "google:mail:notify"                                  /* Not a XEP */
     230#define XMLNS_CARBONS      "urn:xmpp:carbons:2"                                  /* XEP-0280 */
     231#define XMLNS_FORWARDING   "urn:xmpp:forward:0"                                  /* XEP-0297 */
     232#define XMLNS_HINTS        "urn:xmpp:hints"                                      /* XEP-0334 */
    230233#define XMLNS_CHATSTATES   "http://jabber.org/protocol/chatstates"               /* XEP-0085 */
    231234#define XMLNS_DISCO_INFO   "http://jabber.org/protocol/disco#info"               /* XEP-0030 */
     
    338341
    339342extern const struct oauth2_service oauth2_service_google;
    340 extern const struct oauth2_service oauth2_service_facebook;
    341343
    342344/* conference.c */
     
    356358xt_status jabber_parse_hipchat_profile(struct im_connection *ic, struct xt_node *node, struct xt_node *orig);
    357359xt_status hipchat_handle_success(struct im_connection *ic, struct xt_node *node);
     360char *hipchat_make_channel_slug(const char *name);
     361char *hipchat_guess_channel_name(struct im_connection *ic, const char *name);
    358362
    359363#endif
  • protocols/jabber/jabber_util.c

    r63cad66 r3fbce97  
    514514                } else if (bud->resource && (flags & GET_BUDDY_EXACT)) {
    515515                        /* We want an exact match, so in thise case there shouldn't be a /resource. */
    516                         return NULL;
     516                        if (head != bud && head->resource == NULL) {
     517                                return head;
     518                        } else {
     519                                return NULL;
     520                        }
    517521                } else if (bud->resource == NULL || bud->next == NULL) {
    518522                        /* No need for selection if there's only one option. */
  • protocols/jabber/message.c

    r63cad66 r3fbce97  
    2424#include "jabber.h"
    2525
    26 xt_status jabber_pkt_message(struct xt_node *node, gpointer data)
     26static xt_status jabber_pkt_message_normal(struct xt_node *node, gpointer data, gboolean carbons_sent)
    2727{
    2828        struct im_connection *ic = data;
    29         char *from = xt_find_attr(node, "from");
     29        struct jabber_data *jd = ic->proto_data;
     30        char *from = xt_find_attr(node, carbons_sent ? "to" : "from");
    3031        char *type = xt_find_attr(node, "type");
    3132        char *id = xt_find_attr(node, "id");
     
    3738        if (!from) {
    3839                return XT_HANDLED; /* Consider this packet corrupted. */
    39 
    40         }
    41         if (request && id) {
     40        }
     41
     42        /* try to detect hipchat's own version of self-messages */
     43        if (jd->flags & JFLAG_HIPCHAT) {
     44                struct xt_node *c;
     45
     46                if ((c = xt_find_node_by_attr(node->children, "delay", "xmlns", XMLNS_DELAY)) &&
     47                    (s = xt_find_attr(c, "from_jid")) &&
     48                    jabber_compare_jid(s, jd->me)) {
     49                        carbons_sent = TRUE;
     50                }
     51        }
     52
     53        if (request && id && g_strcmp0(type, "groupchat") != 0 && !carbons_sent) {
    4254                /* Send a message receipt (XEP-0184), looking like this:
    43                  * <message
    44                  *  from='kingrichard@royalty.england.lit/throne'
    45                  *  id='bi29sg183b4v'
    46                  *  to='northumberland@shakespeare.lit/westminster'>
     55                 * <message from='...' id='...' to='...'>
    4756                 *  <received xmlns='urn:xmpp:receipts' id='richard2-4.1.247'/>
    48                  * </message> */
     57                 * </message>
     58                 *
     59                 * MUC messages are excluded, since receipts aren't supposed to be sent over MUCs
     60                 * (XEP-0184 section 5.3) and replying to those may result in 'forbidden' errors.
     61                 */
    4962                struct xt_node *received, *receipt;
    5063
     
    127140                if (fullmsg->len > 0) {
    128141                        imcb_buddy_msg(ic, from, fullmsg->str,
    129                                        0, jabber_get_timestamp(node));
     142                                       carbons_sent ? OPT_SELFMESSAGE : 0, jabber_get_timestamp(node));
    130143                }
    131144                if (room) {
     
    136149
    137150                /* Handling of incoming typing notifications. */
    138                 if (bud == NULL) {
    139                         /* Can't handle these for unknown buddies. */
     151                if (bud == NULL || carbons_sent) {
     152                        /* Can't handle these for unknown buddies.
     153                           And ignore them if it's just carbons */
    140154                } else if (xt_find_node(node->children, "composing")) {
    141155                        bud->flags |= JBFLAG_DOES_XEP85;
    142156                        imcb_buddy_typing(ic, from, OPT_TYPING);
    143157                }
    144                 /* No need to send a "stopped typing" signal when there's a message. */
    145                 else if (xt_find_node(node->children, "active") && (body == NULL)) {
     158                else if (xt_find_node(node->children, "active")) {
    146159                        bud->flags |= JBFLAG_DOES_XEP85;
    147                         imcb_buddy_typing(ic, from, 0);
     160
     161                        /* No need to send a "stopped typing" signal when there's a message. */
     162                        if (body == NULL) {
     163                                imcb_buddy_typing(ic, from, 0);
     164                        }
    148165                } else if (xt_find_node(node->children, "paused")) {
    149166                        bud->flags |= JBFLAG_DOES_XEP85;
     
    158175        return XT_HANDLED;
    159176}
     177
     178static xt_status jabber_carbons_message(struct xt_node *node, gpointer data)
     179{
     180        struct im_connection *ic = data;
     181        struct xt_node *wrap, *fwd, *msg;
     182        gboolean carbons_sent;
     183
     184        if ((wrap = xt_find_node(node->children, "received"))) {
     185                carbons_sent = FALSE;
     186        } else if ((wrap = xt_find_node(node->children, "sent"))) {
     187                carbons_sent = TRUE;
     188        }
     189
     190        if (wrap == NULL || g_strcmp0(xt_find_attr(wrap, "xmlns"), XMLNS_CARBONS) != 0) {
     191                return XT_NEXT;
     192        }
     193
     194        if (!(fwd = xt_find_node(wrap->children, "forwarded")) ||
     195             (g_strcmp0(xt_find_attr(fwd, "xmlns"), XMLNS_FORWARDING) != 0) ||
     196            !(msg = xt_find_node(fwd->children, "message"))) {
     197                imcb_log(ic, "Error: Invalid carbons message received");
     198                return XT_ABORT;
     199        }
     200
     201        return jabber_pkt_message_normal(msg, data, carbons_sent);
     202}
     203
     204xt_status jabber_pkt_message(struct xt_node *node, gpointer data)
     205{
     206        struct im_connection *ic = data;
     207        struct jabber_data *jd = ic->proto_data;
     208        char *from = xt_find_attr(node, "from");
     209
     210        if (jabber_compare_jid(jd->me, from)) {    /* Probably a Carbons message */
     211                xt_status st = jabber_carbons_message(node, data);
     212                if (st == XT_HANDLED || st == XT_ABORT) {
     213                        return st;
     214                }
     215        }
     216        return jabber_pkt_message_normal(node, data, FALSE);
     217}
  • protocols/jabber/presence.c

    r63cad66 r3fbce97  
    186186        char *prio = set_getstr(&ic->acc->set, "priority");
    187187
    188         if (jd->away_state->code != NULL) {
     188        if (jd->away_state && jd->away_state->full_name != NULL) {
    189189                int new_prio = (atoi(prio) - 5);
    190190                if (new_prio < 0) {
     
    222222        cap = xt_new_node("c", NULL, NULL);
    223223        xt_add_attr(cap, "xmlns", XMLNS_CAPS);
    224         xt_add_attr(cap, "node", "http://bitlbee.org/xmpp/caps");
     224
     225        if (jd->flags & JFLAG_HIPCHAT) {
     226                /* hipchat specific node, whitelisted by request to receive self-messages */
     227                xt_add_attr(cap, "node", "http://bitlbee.org/xmpp/caps/hipchat");
     228        } else {
     229                xt_add_attr(cap, "node", "http://bitlbee.org/xmpp/caps");
     230        }
    225231        xt_add_attr(cap, "ver", BITLBEE_VERSION);   /* The XEP wants this hashed, but nobody's doing that. */
    226232        xt_add_child(node, cap);
  • protocols/jabber/s5bytestream.c

    r63cad66 r3fbce97  
    536536                 * that sends atyp=0 addrlen=0 and only 6 bytes (one less than one would expect).
    537537                 * Therefore I removed the wait for more bytes. Since we don't care about what else the proxy
    538                  * is sending, it shouldnt matter */
     538                 * is sending, it should not matter */
    539539
    540540                if (bt->tf->ft->sending) {
     
    559559 * An intelligent sender would probably specify himself as the first streamhost and
    560560 * a proxy as the second (Kopete and PSI are examples here). That way, a (potentially)
    561  * slow proxy is only used if neccessary. This of course also means, that the timeout
     561 * slow proxy is only used if necessary. This of course also means, that the timeout
    562562 * per streamhost should be kept short. If one or two firewalled adresses are specified,
    563563 * they have to timeout first before a proxy is tried.
  • protocols/jabber/sasl.c

    r63cad66 r3fbce97  
    3838        "6C-Zgf7Tr7gEQTPlBhMUgo7R",
    3939};
    40 const struct oauth2_service oauth2_service_facebook =
    41 {
    42         "https://www.facebook.com/dialog/oauth",
    43         "https://graph.facebook.com/oauth/access_token",
    44         "https://www.bitlbee.org/main.php/Facebook/oauth2.html",
    45         "offline_access,xmpp_login",
    46         "126828914005625",
    47         "4b100f0f244d620bf3f15f8b217d4c32",
    48 };
    4940
    5041xt_status sasl_pkt_mechanisms(struct xt_node *node, gpointer data)
     
    5445        struct xt_node *c, *reply;
    5546        char *s;
    56         int sup_plain = 0, sup_digest = 0, sup_gtalk = 0, sup_fb = 0, sup_anonymous = 0;
     47        int sup_plain = 0, sup_digest = 0, sup_gtalk = 0, sup_anonymous = 0;
    5748        int want_oauth = FALSE, want_hipchat = FALSE, want_anonymous = FALSE;
    5849        GString *mechs;
     
    8980                } else if (c->text && g_strcasecmp(c->text, "X-OAUTH2") == 0) {
    9081                        sup_gtalk = 1;
    91                 } else if (c->text && g_strcasecmp(c->text, "X-FACEBOOK-PLATFORM") == 0) {
    92                         sup_fb = 1;
    9382                }
    9483
     
    10190
    10291        if (!want_oauth && !sup_plain && !sup_digest) {
    103                 if (!sup_gtalk && !sup_fb) {
     92                if (!sup_gtalk) {
    10493                        imcb_error(ic, "This server requires OAuth "
    10594                                   "(supported schemes:%s)", mechs->str);
     
    137126                reply->text_len = strlen(reply->text);
    138127                g_free(s);
    139         } else if (sup_fb && want_oauth) {
    140                 xt_add_attr(reply, "mechanism", "X-FACEBOOK-PLATFORM");
    141                 jd->flags |= JFLAG_SASL_FB;
    142128        } else if (want_oauth) {
    143129                imcb_error(ic, "OAuth requested, but not supported by server");
     
    154140                xt_free_node(reply);
    155141                return XT_ABORT;
    156         } else if (sup_digest) {
     142        } else if (sup_digest && !(jd->ssl && sup_plain)) {
     143                /* Only try DIGEST-MD5 if there's no SSL/TLS or if PLAIN isn't supported.
     144                 * Which in practice means "don't bother with DIGEST-MD5 most of the time".
     145                 * It's weak, pointless over TLS, and often breaks with some servers (hi openfire) */
     146
    157147                xt_add_attr(reply, "mechanism", "DIGEST-MD5");
    158148
     
    295285        dec = frombase64(node->text);
    296286
    297         if (jd->flags & JFLAG_SASL_FB) {
    298                 /* New-style Facebook OAauth2 support. Instead of sending a refresh
    299                    token, they just send an access token that should never expire. */
    300                 GSList *p_in = NULL, *p_out = NULL;
    301                 char time[33];
    302 
    303                 oauth_params_parse(&p_in, dec);
    304                 oauth_params_add(&p_out, "nonce", oauth_params_get(&p_in, "nonce"));
    305                 oauth_params_add(&p_out, "method", oauth_params_get(&p_in, "method"));
    306                 oauth_params_free(&p_in);
    307 
    308                 g_snprintf(time, sizeof(time), "%lld", (long long) (gettime() * 1000));
    309                 oauth_params_add(&p_out, "call_id", time);
    310                 oauth_params_add(&p_out, "api_key", oauth2_service_facebook.consumer_key);
    311                 oauth_params_add(&p_out, "v", "1.0");
    312                 oauth_params_add(&p_out, "format", "XML");
    313                 oauth_params_add(&p_out, "access_token", jd->oauth2_access_token);
    314 
    315                 reply = oauth_params_string(p_out);
    316                 oauth_params_free(&p_out);
    317         } else if (!(s = sasl_get_part(dec, "rspauth"))) {
     287        if (!(s = sasl_get_part(dec, "rspauth"))) {
    318288                /* See RFC 2831 for for information. */
    319289                md5_state_t A1, A2, H;
  • protocols/jabber/si.c

    r63cad66 r3fbce97  
    186186        jd->filetransfers = g_slist_prepend(jd->filetransfers, tf);
    187187
    188         /* query buddy's features and server's streaming proxies if neccessary */
     188        /* query buddy's features and server's streaming proxies if necessary */
    189189
    190190        if (!tf->bud->features) {
     
    283283
    284284        if (requestok) {
    285                 /* Figure out who the transfer should come frome... */
     285                /* Figure out who the transfer should come from... */
    286286
    287287                ext_jid = ini_jid;
     
    403403         * <iq from=... to=... id=...>
    404404         *      <si xmlns=si>
    405          *      [       <file xmlns=ft/>    ] <-- not neccessary
     405         *      [       <file xmlns=ft/>    ] <-- not necessary
    406406         *              <feature xmlns=feature>
    407407         *                      <x xmlns=xdata type=submit>
  • protocols/msn/gw.c

    r63cad66 r3fbce97  
    2222        gw->ssl = (GATEWAY_PORT == 443);
    2323        gw->poll_timeout = -1;
     24        gw->write_timeout = -1;
    2425        gw->ic = ic;
    2526        gw->md = ic->proto_data;
     
    3435                b_event_remove(gw->poll_timeout);
    3536        }
     37
     38        if (gw->write_timeout != -1) {
     39                b_event_remove(gw->write_timeout);
     40        }
     41
    3642        g_byte_array_free(gw->in, TRUE);
    3743        g_byte_array_free(gw->out, TRUE);
     
    189195}
    190196
    191 void msn_gw_write(struct msn_gw *gw, char *buf, size_t len)
    192 {
    193         g_byte_array_append(gw->out, (const guint8 *) buf, len);
     197static gboolean msn_gw_write_cb(gpointer data, gint source, b_input_condition cond)
     198{
     199        struct msn_gw *gw;
     200       
     201        if (!(gw = msn_gw_from_ic(data))) {
     202                return FALSE;
     203        }
     204
    194205        if (!gw->open) {
    195206                msn_gw_open(gw);
     
    197208                msn_gw_dorequest(gw, NULL);
    198209        }
    199 }
     210
     211        gw->write_timeout = -1;
     212        return FALSE;
     213}
     214
     215void msn_gw_write(struct msn_gw *gw, char *buf, size_t len)
     216{
     217        g_byte_array_append(gw->out, (const guint8 *) buf, len);
     218
     219        /* do a bit of buffering here to send several commands with a single request */
     220        if (gw->write_timeout == -1) {
     221                gw->write_timeout = b_timeout_add(1, msn_gw_write_cb, gw->ic);
     222        }
     223}
  • protocols/msn/msn.h

    r63cad66 r3fbce97  
    112112
    113113        int poll_timeout;
     114        int write_timeout;
    114115
    115116        b_event_handler callback;
  • protocols/msn/msn_util.c

    r63cad66 r3fbce97  
    4141        }
    4242
    43         return g_markup_printf_escaped("<ml><d n=\"%s\"><c n=\"%s\" l=\"%d\" t=\"1\"/></d></ml>",
     43        return g_markup_printf_escaped("<ml><d n=\"%s\"><c n=\"%s\" t=\"1\"><s n=\"IM\" l=\"%d\" /></c></d></ml>",
    4444                                       domain, handle, list);
    4545}
  • protocols/msn/ns.c

    r63cad66 r3fbce97  
    3939static void msn_ns_send_adl(struct im_connection *ic);
    4040static void msn_ns_structured_message(struct msn_data *md, char *msg, int msglen, char **cmd);
    41 static void msn_ns_sdg(struct msn_data *md, char *who, char **parts, char *action);
     41static void msn_ns_sdg(struct msn_data *md, char *who, char **parts, char *action, gboolean selfmessage);
    4242static void msn_ns_nfy(struct msn_data *md, char *who, char **parts, char *action, gboolean is_put);
    4343
     
    110110        struct im_connection *ic = md->ic;
    111111
     112        /* this should be taken from XFR, but hardcoding it for now. it also prevents more redirects. */
     113        const char *redir_data = "VmVyc2lvbjogMQ0KWGZyQ291bnQ6IDINCklzR2VvWGZyOiB0cnVlDQo=";
     114
    112115        if (source == -1 && !md->is_http) {
    113116                imcb_error(ic, "Could not connect to server");
     
    135138        }
    136139
    137         if (msn_ns_write(ic, source, "VER %d %s CVR0\r\n", ++md->trId, MSNP_VER)) {
     140        /* Having to handle potential errors in each write sure makes these ifs awkward...*/
     141
     142        if (msn_ns_write(ic, source, "VER %d %s CVR0\r\n", ++md->trId, MSNP_VER) &&
     143            msn_ns_write(ic, source, "CVR %d 0x0409 mac 10.2.0 ppc macmsgs 3.5.1 macmsgs %s %s\r\n",
     144                         ++md->trId, ic->acc->user, redir_data) &&
     145            msn_ns_write(ic, md->fd, "USR %d SSO I %s\r\n", ++md->trId, ic->acc->user)) {
     146
    138147                if (!md->is_http) {
    139148                        md->inpa = b_input_add(md->fd, B_EV_IO_READ, msn_ns_callback, md);
     
    208217                }
    209218
    210                 return(msn_ns_write(ic, md->fd, "CVR %d 0x0409 mac 10.2.0 ppc macmsgs 3.5.1 macmsgs %s VmVyc2lvbjogMQ0KWGZyQ291bnQ6IDINClhmclNlbnRVVENUaW1lOiA2MzU2MTQ3OTU5NzgzOTAwMDANCklzR2VvWGZyOiB0cnVlDQo=\r\n",
    211                                     ++md->trId, ic->acc->user));
    212219        } else if (strcmp(cmd[0], "CVR") == 0) {
    213220                /* We don't give a damn about the information we just received */
    214                 return msn_ns_write(ic, md->fd, "USR %d SSO I %s\r\n", ++md->trId, ic->acc->user);
    215221        } else if (strcmp(cmd[0], "XFR") == 0) {
    216222                char *server;
     
    280286                        md->msglen = atoi(cmd[2]);
    281287                }
     288        } else if (strcmp(cmd[0], "RML") == 0) {
     289                /* Move along, nothing to see here */
    282290        } else if (strcmp(cmd[0], "CHL") == 0) {
    283291                char *resp;
     
    475483}
    476484
     485/* returns newly allocated string */
     486static char *msn_ns_parse_header_address(struct msn_data *md, char *headers, char *header_name)
     487{
     488        char *semicolon = NULL;
     489        char *header = NULL;
     490        char *address = NULL;
     491
     492        if (!(header = get_rfc822_header(headers, header_name, 0))) {
     493                return NULL;
     494        }
     495
     496        /* either the semicolon or the end of the string */
     497        semicolon = strchr(header, ';') ? : (header + strlen(header));
     498
     499        address = g_strndup(header + 2, semicolon - header - 2);
     500
     501        g_free(header);
     502        return address;
     503}
     504
    477505static void msn_ns_structured_message(struct msn_data *md, char *msg, int msglen, char **cmd)
    478506{
    479507        char **parts = NULL;
    480         char *semicolon = NULL;
    481508        char *action = NULL;
    482         char *from = NULL;
    483509        char *who = NULL;
     510        gboolean selfmessage = FALSE;
    484511
    485512        parts = g_strsplit(msg, "\r\n\r\n", 4);
    486513
    487         if (!(from = get_rfc822_header(parts[0], "From", 0))) {
     514        if (!(who = msn_ns_parse_header_address(md, parts[0], "From"))) {
    488515                goto cleanup;
    489516        }
    490517
    491         /* either the semicolon or the end of the string */
    492         semicolon = strchr(from, ';') ? : (from + strlen(from));
    493 
    494         who = g_strndup(from + 2, semicolon - from - 2);
     518        if (strcmp(who, md->ic->acc->user) == 0) {
     519                selfmessage = TRUE;
     520                g_free(who);
     521                if (!(who = msn_ns_parse_header_address(md, parts[0], "To"))) {
     522                        goto cleanup;
     523                }
     524        }
    495525
    496526        if ((strcmp(cmd[0], "SDG") == 0) && (action = get_rfc822_header(parts[2], "Message-Type", 0))) {
    497                 msn_ns_sdg(md, who, parts, action);
     527                msn_ns_sdg(md, who, parts, action, selfmessage);
    498528
    499529        } else if ((strcmp(cmd[0], "NFY") == 0) && (action = get_rfc822_header(parts[2], "Uri", 0))) {
     
    505535        g_strfreev(parts);
    506536        g_free(action);
    507         g_free(from);
    508537        g_free(who);
    509538}
    510539
    511 static void msn_ns_sdg(struct msn_data *md, char *who, char **parts, char *action)
     540static void msn_ns_sdg(struct msn_data *md, char *who, char **parts, char *action, gboolean selfmessage)
    512541{
    513542        struct im_connection *ic = md->ic;
    514543
    515         if (strcmp(action, "Control/Typing") == 0) {
     544        if (strcmp(action, "Control/Typing") == 0 && !selfmessage) {
    516545                imcb_buddy_typing(ic, who, OPT_TYPING);
    517546        } else if (strcmp(action, "Text") == 0) {
    518                 imcb_buddy_msg(ic, who, parts[3], 0, 0);
     547                imcb_buddy_msg(ic, who, parts[3], selfmessage ? OPT_SELFMESSAGE : 0, 0);
    519548        }
    520549}
     
    595624        } else {
    596625                imcb_error(ic, "Error during Passport authentication: %s", error);
    597                 imc_logout(ic, TRUE);
     626
     627                /* don't reconnect with auth errors */
     628                if (error && g_str_has_prefix(error, "wsse:FailedAuthentication")) {
     629                        imc_logout(ic, FALSE);
     630                } else {
     631                        imc_logout(ic, TRUE);
     632                }
    598633        }
    599634}
  • protocols/msn/soap.c

    r63cad66 r3fbce97  
    270270        struct im_connection *ic = soap_req->ic;
    271271        struct msn_data *md = ic->proto_data;
    272         char pass[MAX_PASSPORT_PWLEN + 1];
    273272
    274273        if (sd->redirect) {
     
    286285        }
    287286
    288         strncpy(pass, ic->acc->pass, MAX_PASSPORT_PWLEN);
    289         pass[MAX_PASSPORT_PWLEN] = '\0';
    290287        soap_req->payload = g_markup_printf_escaped(SOAP_PASSPORT_SSO_PAYLOAD,
    291                                                     ic->acc->user, pass, md->pp_policy);
     288                                                    ic->acc->user, ic->acc->pass, md->pp_policy);
    292289
    293290        return MSN_SOAP_OK;
     
    327324        struct xt_node *code = xt_find_node(node->children, "faultcode");
    328325        struct xt_node *string = xt_find_node(node->children, "faultstring");
     326        struct xt_node *reqstatus = xt_find_path(node, "psf:pp/psf:reqstatus");
    329327        struct xt_node *url;
    330328
     
    335333                   url->text_len > 0) {
    336334                sd->redirect = g_strdup(url->text);
     335        } else if (reqstatus && strcmp(reqstatus->text, "0x800488fe") == 0) {
     336                char *msg = "Location blocked. Log in to live.com, go to recent activity and click 'this was me'";
     337                sd->error = g_strdup_printf("%s (%s)", code->text, msg);
    337338        } else {
    338339                sd->error = g_strdup_printf("%s (%s)", code->text, string && string->text_len ?
     
    346347        { "wsse:BinarySecurityToken", "wst:RequestedSecurityToken", msn_soap_passport_sso_token },
    347348        { "S:Fault", "S:Envelope", msn_soap_passport_failure },
     349        { "S:Fault", "wst:RequestSecurityTokenResponse", msn_soap_passport_failure },
    348350        { NULL, NULL, NULL }
    349351};
     
    774776                imcb_log(soap_req->ic, "Warning: %d contacts were in both your "
    775777                         "block and your allow list. Assuming they're all "
    776                          "allowed. Use the official WLM client once to fix "
    777                          "this.", wtf);
     778                         "allowed.", wtf);
    778779        }
    779780
  • protocols/msn/soap.h

    r63cad66 r3fbce97  
    6161#define SOAP_PASSPORT_SSO_URL "https://login.live.com/RST.srf"
    6262#define SOAP_PASSPORT_SSO_URL_MSN "https://msnia.login.live.com/pp900/RST.srf"
    63 #define MAX_PASSPORT_PWLEN 16
    6463
    6564#define SOAP_PASSPORT_SSO_PAYLOAD \
  • protocols/nogaim.c

    r63cad66 r3fbce97  
    9090
    9191GList *protocols = NULL;
     92GList *disabled_protocols = NULL;
    9293
    9394void register_protocol(struct prpl *p)
     
    103104
    104105        if (refused) {
    105                 log_message(LOGLVL_WARNING, "Protocol %s disabled\n", p->name);
     106                disabled_protocols = g_list_append(disabled_protocols, p);
    106107        } else {
    107108                protocols = g_list_append(protocols, p);
     
    109110}
    110111
     112static int proto_name_cmp(const void *proto_, const void *name)
     113{
     114        const struct prpl *proto = proto_;
     115        return g_strcasecmp(proto->name, name);
     116}
     117
    111118struct prpl *find_protocol(const char *name)
    112119{
    113         GList *gl;
    114 
    115         for (gl = protocols; gl; gl = gl->next) {
    116                 struct prpl *proto = gl->data;
    117 
    118                 if (g_strcasecmp(proto->name, name) == 0) {
    119                         return proto;
    120                 }
    121         }
    122 
    123         return NULL;
     120        GList *gl = g_list_find_custom(protocols, name, proto_name_cmp);
     121        return gl ? gl->data: NULL;
     122}
     123
     124gboolean is_protocol_disabled(const char *name)
     125{
     126        return g_list_find_custom(disabled_protocols, name, proto_name_cmp) != NULL;
    124127}
    125128
     
    209212        account_t *a;
    210213
     214        if (!ic->bee->ui->log) {
     215                return;
     216        }
     217
    211218        va_start(params, format);
    212219        text = g_strdup_vprintf(format, params);
     
    227234        /* If we found one, include the screenname in the message. */
    228235        if (a) {
    229                 /* FIXME(wilmer): ui_log callback or so */
    230                 irc_rootmsg(ic->bee->ui_data, "%s - %s", ic->acc->tag, text);
     236                ic->bee->ui->log(ic->bee, ic->acc->tag, text);
    231237        } else {
    232                 irc_rootmsg(ic->bee->ui_data, "%s - %s", ic->acc->prpl->name, text);
     238                ic->bee->ui->log(ic->bee, ic->acc->prpl->name, text);
    233239        }
    234240
     
    662668                GList *m = ic->acc->prpl->away_states(ic);
    663669                msg = ic->acc->flags & ACC_FLAG_AWAY_MESSAGE ? away : NULL;
    664                 away = imc_away_state_find(m, away, &msg) ? : m->data;
     670                away = imc_away_state_find(m, away, &msg) ? :
     671                       (imc_away_state_find(m, "away", &msg) ? : m->data);
    665672        } else if (ic->acc->flags & ACC_FLAG_STATUS_MESSAGE) {
    666673                away = NULL;
     
    781788}
    782789
     790/* Deprecated: using this function resulted in merging several handles accidentally
     791 * Also the irc layer handles this decently nowadays */
    783792void imcb_clean_handle(struct im_connection *ic, char *handle)
    784793{
    785         /* Accepts a handle and does whatever is necessary to make it
    786            BitlBee-friendly. Currently this means removing everything
    787            outside 33-127 (ASCII printable excl spaces), @ (only one
    788            is allowed) and ! and : */
    789         char out[strlen(handle) + 1];
    790         int s, d;
    791 
    792         s = d = 0;
    793         while (handle[s]) {
    794                 if (handle[s] > ' ' && handle[s] != '!' && handle[s] != ':' &&
    795                     (handle[s] & 0x80) == 0) {
    796                         if (handle[s] == '@') {
    797                                 /* See if we got an @ already? */
    798                                 out[d] = 0;
    799                                 if (strchr(out, '@')) {
    800                                         continue;
    801                                 }
    802                         }
    803 
    804                         out[d++] = handle[s];
    805                 }
    806                 s++;
    807         }
    808         out[d] = handle[s];
    809 
    810         strcpy(handle, out);
    811 }
     794}
  • protocols/nogaim.h

    r63cad66 r3fbce97  
    7676#define OPT_PONGED      0x00020000 /* Received a keep-alive during last interval */
    7777#define OPT_LOCAL_CONTACTS_SENT 0x00040000 /* Protocol already requested local contact list, so don't send it after finishing login. */
     78#define OPT_SELFMESSAGE 0x00080000 /* A message sent by self from another location */
    7879
    7980/* ok. now the fun begins. first we create a connection structure */
     
    280281G_MODULE_EXPORT GSList *get_connections();
    281282G_MODULE_EXPORT struct prpl *find_protocol(const char *name);
     283G_MODULE_EXPORT gboolean is_protocol_disabled(const char *name);
    282284/* When registering a new protocol, you should allocate space for a new prpl
    283285 * struct, initialize it (set the function pointers to point to your
     
    326328G_MODULE_EXPORT void imcb_add_buddy(struct im_connection *ic, const char *handle, const char *group);
    327329G_MODULE_EXPORT void imcb_remove_buddy(struct im_connection *ic, const char *handle, char *group);
    328 G_MODULE_EXPORT struct buddy *imcb_find_buddy(struct im_connection *ic, char *handle);
    329330G_MODULE_EXPORT void imcb_rename_buddy(struct im_connection *ic, const char *handle, const char *realname);
    330331G_MODULE_EXPORT void imcb_buddy_nick_hint(struct im_connection *ic, const char *handle, const char *nick);
     
    332333G_MODULE_EXPORT GSList *imcb_get_local_contacts(struct im_connection *ic);
    333334
    334 G_MODULE_EXPORT void imcb_buddy_typing(struct im_connection *ic, const char *handle, uint32_t flags);
     335G_MODULE_EXPORT void imcb_buddy_typing(struct im_connection *ic, const char *handle, guint32 flags);
    335336G_MODULE_EXPORT struct bee_user *imcb_buddy_by_handle(struct im_connection *ic, const char *handle);
    336 G_MODULE_EXPORT void imcb_clean_handle(struct im_connection *ic, char *handle);
     337
     338G_GNUC_DEPRECATED G_MODULE_EXPORT void imcb_clean_handle(struct im_connection *ic, char *handle);
    337339
    338340/* Actions, or whatever. */
  • protocols/oscar/aim.h

    r63cad66 r3fbce97  
    2121
    2222#include "bitlbee.h"
     23
     24#ifdef WITH_PURPLE
     25/* For compatibility with builds that include both purple and this oscar module */
     26#include "aim_prefixes.h"
     27#endif
    2328
    2429/* XXX adjust these based on autoconf-detected platform */
     
    871876 * SNAC Family: Internal Messages
    872877 *
    873  * This isn't truely a SNAC family either, but using
     878 * This isn't truly a SNAC family either, but using
    874879 * these, we can integrated non-SNAC services into
    875880 * the SNAC-centered libfaim callback structure.
  • protocols/oscar/auth.c

    r63cad66 r3fbce97  
    4141 * meaning you generally never call this.
    4242 *
    43  * But there are times when something might want it seperate. Specifically,
     43 * But there are times when something might want it separate. Specifically,
    4444 * libfaim sends this internally when doing SNAC login.
    4545 *
  • protocols/oscar/conn.c

    r63cad66 r3fbce97  
    1515 * In OSCAR, every connection has a set of SNAC groups associated
    1616 * with it.  These are the groups that you can send over this connection
    17  * without being guarenteed a "Not supported" SNAC error.
     17 * without being guaranteed a "Not supported" SNAC error.
    1818 *
    1919 * The grand theory of things says that these associations transcend
     
    3636 *
    3737 * Here comes the good bit.  Without even letting anyone know, particularly
    38  * the module that decided to send this SNAC, and definitly not that twit
     38 * the module that decided to send this SNAC, and definitely not that twit
    3939 * in Greenland, you send out a service request.  In this request, you have
    4040 * marked the need for a connection supporting group 0x000e.  A few seconds
     
    317317
    318318        if (deadconn->fd >= 3) {
    319                 closesocket(deadconn->fd);
     319                proxy_disconnect(deadconn->fd);
    320320        }
    321321        deadconn->fd = -1;
  • protocols/oscar/im.c

    r63cad66 r3fbce97  
    5050 * encoding for your message.  In UNICODE mode, _all_ characters must
    5151 * occupy 16bits, including ones that are not special.  (Remember that
    52  * the first 128 UNICODE symbols are equivelent to ASCII7, however they
     52 * the first 128 UNICODE symbols are equivalent to ASCII7, however they
    5353 * must be prefixed with a zero high order byte.)
    5454 *
     
    6464 * in all of libfaim, it is written with performance in mind.  As such,
    6565 * it is not as clear as it could be in respect to how this message is
    66  * supposed to be layed out. Most obviously, tlvlists should be used
     66 * supposed to be laid out. Most obviously, tlvlists should be used
    6767 * instead of writing out the bytes manually.
    6868 *
     
    476476 * examples of how to do this.
    477477 *
    478  * I would definitly recommend avoiding this feature unless you really
     478 * I would definitely recommend avoiding this feature unless you really
    479479 * know what you are doing, and/or you have something neat to do with it.
    480480 *
     
    638638                        }
    639639#if 0
    640                         /* XXX this isn't really necesary... */
     640                        /* XXX this isn't really necessary... */
    641641                        if (((args.flag1 != 0x0000) &&
    642642                             (args.flag1 != 0x0002) &&
     
    11611161         *
    11621162         * Channel 0x0001 is the message channel.  There are
    1163          * other channels for things called "rendevous"
     1163         * other channels for things called "rendezvous"
    11641164         * which represent chat and some of the other new
    11651165         * features of AIM2/3/3.5.
    11661166         *
    1167          * Channel 0x0002 is the Rendevous channel, which
     1167         * Channel 0x0002 is the Rendezvous channel, which
    11681168         * is where Chat Invitiations and various client-client
    11691169         * connection negotiations come from.
     
    11811181         * with the TLVs read below, they are two different pieces.  The
    11821182         * userinfo block contains the number of TLVs that contain user
    1183          * information, the rest are not even though there is no seperation.
     1183         * information, the rest are not even though there is no separation.
    11841184         * aim_extractuserinfo() returns the number of bytes used by the
    11851185         * userinfo tlvs, so you can start reading the rest of them right
     
    12531253/*
    12541254 *
    1255  * I definitly recommend sending this.  If you don't, you'll be stuck
     1255 * I definitely recommend sending this.  If you don't, you'll be stuck
    12561256 * with the rather unreasonable defaults.  You don't want those.  Send this.
    12571257 *
  • protocols/oscar/misc.c

    r63cad66 r3fbce97  
    33 * aim_misc.c
    44 *
    5  * TODO: Seperate a lot of this into an aim_bos.c.
     5 * TODO: Separate a lot of this into an aim_bos.c.
    66 *
    77 * Other things...
  • protocols/oscar/msgcookie.c

    r63cad66 r3fbce97  
    142142 * @cookiep: the address of a pointer to the cookie struct to remove
    143143 *
    144  * this function removes the cookie *cookie from teh list of cookies
     144 * this function removes the cookie *cookie from the list of cookies
    145145 * in sess, and then frees all memory associated with it. including
    146146 * its data! if you want to use the private data after calling this,
  • protocols/oscar/oscar.c

    r63cad66 r3fbce97  
    12661266        } break;
    12671267
    1268         case 2: {         /* rendevous */
     1268        case 2: {         /* rendezvous */
    12691269                struct aim_incomingim_ch2_args *args;
    12701270                args = va_arg(ap, struct aim_incomingim_ch2_args *);
  • protocols/oscar/rxhandlers.c

    r63cad66 r3fbce97  
    381381        /*
    382382         * This doesn't have to be called here.  It could easily be done
    383          * by a seperate thread or something. It's an administrative operation,
     383         * by a separate thread or something. It's an administrative operation,
    384384         * and can take a while. Though the less you call it the less memory
    385385         * you'll have :)
  • protocols/oscar/rxqueue.c

    r63cad66 r3fbce97  
    361361/*
    362362 * Grab a single command sequence off the socket, and enqueue
    363  * it in the incoming event queue in a seperate struct.
     363 * it in the incoming event queue in a separate struct.
    364364 */
    365365int aim_get_command(aim_session_t *sess, aim_conn_t *conn)
     
    479479
    480480/*
    481  * Purge recieve queue of all handled commands (->handled==1).  Also
     481 * Purge receive queue of all handled commands (->handled==1).  Also
    482482 * allows for selective freeing using ->nofree so that the client can
    483483 * keep the data for various purposes.
  • protocols/oscar/service.c

    r63cad66 r3fbce97  
    157157
    158158/*
    159  * OSCAR defines several 'rate classes'.  Each class has seperate
     159 * OSCAR defines several 'rate classes'.  Each class has separate
    160160 * rate limiting properties (limit level, alert level, disconnect
    161161 * level, etc), and a set of SNAC family/type pairs associated with
     
    709709 * of memory.  (I won't put it past them to start requesting data in
    710710 * less static regions -- regions that are initialized at run time, but still
    711  * before the client recieves this request.)
    712  *
    713  * When the client recieves the request, it adds it to the current ds
     711 * before the client receives this request.)
     712 *
     713 * When the client receives the request, it adds it to the current ds
    714714 * (0x00400000) and dereferences it, copying the data into a buffer which
    715715 * it then runs directly through the MD5 hasher.  The 16 byte output of
     
    723723 *     download a FREE, fully featured, and authorized client, here
    724724 *     http://www.aol.com/aim/download2.html"
    725  * The connection is then closed, recieving disconnect code 1, URL
     725 * The connection is then closed, receiving disconnect code 1, URL
    726726 * http://www.aim.aol.com/errors/USER_LOGGED_OFF_NEW_LOGIN.html.
    727727 *
    728728 * Note, however, that numerous inconsistencies can cause the above error,
    729  * not just sending back a bad hash.  Do not immediatly suspect this code
     729 * not just sending back a bad hash.  Do not immediately suspect this code
    730730 * if you get disconnected.  AOL and the open/free software community have
    731731 * played this game for a couple years now, generating the above message
    732  * on numerous ocassions.
     732 * on numerous occasions.
    733733 *
    734734 * Anyway, neener.  We win again.
  • protocols/oscar/tlv.c

    r63cad66 r3fbce97  
    2525 * bstream references, so that at least the ->value portion of each
    2626 * element doesn't need to be malloc/memcpy'd.  This could prove to be
    27  * just as effecient as the in-place TLV parsing used in a couple places
     27 * just as efficient as the in-place TLV parsing used in a couple places
    2828 * in libfaim.
    2929 *
     
    135135/**
    136136 * aim_addtlvtochain_str - Add a string to a TLV chain
    137  * @list: Desination chain (%NULL pointer if empty)
     137 * @list: Designation chain (%NULL pointer if empty)
    138138 * @type: TLV type
    139139 * @str: String to add
  • protocols/oscar/txqueue.c

    r63cad66 r3fbce97  
    6767 * The overall purpose here is to enqueue the passed in command struct
    6868 * into the outgoing (tx) queue.  Basically...
    69  *   1) Make a scope-irrelevent copy of the struct
     69 *   1) Make a scope-irrelevant copy of the struct
    7070 *   3) Mark as not-sent-yet
    7171 *   4) Enqueue the struct into the list
  • protocols/purple/ft-direct.c

    r63cad66 r3fbce97  
    193193PurpleXferUiOps bee_xfer_uiops =
    194194{
    195         prplcb_xfer_new,
    196         prplcb_xfer_dbg,
    197         prplcb_xfer_dbg,
    198         prplcb_xfer_progress,
    199         prplcb_xfer_dbg,
    200         prplcb_xfer_dbg,
    201         prplcb_xfer_write,
    202         prplcb_xfer_read,
    203         prplcb_xfer_dbg,
     195        prplcb_xfer_new,           /* new_xfer */
     196        prplcb_xfer_dbg,           /* destroy */
     197        prplcb_xfer_dbg,           /* add_xfer */
     198        prplcb_xfer_progress,      /* update_progress */
     199        prplcb_xfer_dbg,           /* cancel_local */
     200        prplcb_xfer_dbg,           /* cancel_remote */
     201        prplcb_xfer_write,         /* ui_write */
     202        prplcb_xfer_read,          /* ui_read */
     203        prplcb_xfer_dbg,           /* data_not_sent */
    204204};
    205205
  • protocols/purple/ft.c

    r63cad66 r3fbce97  
    232232}
    233233
    234 static void prplcb_xfer_dbg(PurpleXfer *xfer)
    235 {
    236         fprintf(stderr, "prplcb_xfer_dbg 0x%p\n", xfer);
    237 }
    238 
    239234
    240235/* Sending files (UI->IM): */
     
    337332PurpleXferUiOps bee_xfer_uiops =
    338333{
    339         prplcb_xfer_new,
    340         prplcb_xfer_destroy,
    341         NULL, /* prplcb_xfer_add, */
    342         prplcb_xfer_progress,
    343         prplcb_xfer_dbg,
    344         prplcb_xfer_cancel_remote,
    345         NULL,
    346         NULL,
    347         prplcb_xfer_dbg,
     334        prplcb_xfer_new,           /* new_xfer */
     335        prplcb_xfer_destroy,       /* destroy */
     336        NULL,                      /* add_xfer */
     337        prplcb_xfer_progress,      /* update_progress */
     338        NULL,                      /* cancel_local */
     339        prplcb_xfer_cancel_remote, /* cancel_remote */
     340        NULL,                      /* ui_write */
     341        NULL,                      /* ui_read */
     342        NULL,                      /* data_not_sent */
    348343};
  • protocols/purple/purple.c

    r63cad66 r3fbce97  
    113113           servers anyway! */
    114114        if (!dir_fixed) {
     115                PurpleCertificatePool *pool;
    115116                irc_t *irc = acc->bee->ui_data;
    116117                char *dir;
     
    122123                purple_blist_load();
    123124                purple_prefs_load();
     125
     126                if (proxytype == PROXY_SOCKS4A) {
     127                        /* do this here after loading prefs. yes, i know, it sucks */
     128                        purple_prefs_set_bool("/purple/proxy/socks4_remotedns", TRUE);
     129                }
     130
     131                /* re-create the certificate cache directory */
     132                pool = purple_certificate_find_pool("x509", "tls_peers");
     133                dir = purple_certificate_pool_mkpath(pool, NULL);
     134                purple_build_dir(dir, 0700);
     135                g_free(dir);
     136
    124137                dir_fixed = TRUE;
    125138        }
     
    352365        if (!pd) {
    353366                return;
     367        }
     368
     369        while (ic->groupchats) {
     370                imcb_chat_free(ic->groupchats->data);
    354371        }
    355372
     
    639656        /* Call the fucker. */
    640657        callback = (void *) mi->callback;
    641         callback(&pb->node, menu->data);
     658        callback(&pb->node, mi->data);
    642659
    643660        return NULL;
     
    707724                        g_hash_table_replace(chat_hash, "passwd", g_strdup(password));
    708725                }
    709         }
     726
     727                g_free(pce);
     728        }
     729
     730        g_list_free(info);
    710731
    711732        serv_join_chat(purple_account_get_connection(pd->account), chat_hash);
    712733
    713         return NULL;
     734        g_hash_table_destroy(chat_hash);
     735
     736        return imcb_chat_new(ic, room);
    714737}
    715738
     
    733756static PurpleCoreUiOps bee_core_uiops =
    734757{
    735         NULL,
    736         NULL,
    737         purple_ui_init,
    738         NULL,
    739         prplcb_ui_info,
     758        NULL,                      /* ui_prefs_init */
     759        NULL,                      /* debug_ui_init */
     760        purple_ui_init,            /* ui_init */
     761        NULL,                      /* quit */
     762        prplcb_ui_info,            /* get_ui_info */
    740763};
    741764
     
    764787        purple_gg_buddylist_import(gc);
    765788
    766         if (gc->flags & PURPLE_CONNECTION_HTML) {
    767                 ic->flags |= OPT_DOES_HTML;
    768         }
     789        ic->flags |= OPT_DOES_HTML;
    769790}
    770791
     
    800821static PurpleConnectionUiOps bee_conn_uiops =
    801822{
    802         prplcb_conn_progress,
    803         prplcb_conn_connected,
    804         prplcb_conn_disconnected,
    805         prplcb_conn_notice,
    806         NULL,
    807         NULL,
    808         NULL,
    809         prplcb_conn_report_disconnect_reason,
     823        prplcb_conn_progress,                    /* connect_progress */
     824        prplcb_conn_connected,                   /* connected */
     825        prplcb_conn_disconnected,                /* disconnected */
     826        prplcb_conn_notice,                      /* notice */
     827        NULL,                                    /* report_disconnect */
     828        NULL,                                    /* network_connected */
     829        NULL,                                    /* network_disconnected */
     830        prplcb_conn_report_disconnect_reason,    /* report_disconnect_reason */
    810831};
    811832
     
    882903static PurpleBlistUiOps bee_blist_uiops =
    883904{
    884         NULL,
    885         prplcb_blist_new,
    886         NULL,
    887         prplcb_blist_update,
    888         prplcb_blist_remove,
     905        NULL,                      /* new_list */
     906        prplcb_blist_new,          /* new_node */
     907        NULL,                      /* show */
     908        prplcb_blist_update,       /* update */
     909        prplcb_blist_remove,       /* remove */
    889910};
    890911
     
    895916                struct groupchat *gc;
    896917
    897                 gc = imcb_chat_new(ic, conv->name);
    898                 if (conv->title != NULL) {
    899                         imcb_chat_name_hint(gc, conv->title);
     918                gc = bee_chat_by_title(ic->bee, ic, conv->name);
     919
     920                if (!gc) {
     921                        gc = imcb_chat_new(ic, conv->name);
     922                        if (conv->title != NULL) {
     923                                imcb_chat_name_hint(gc, conv->title);
     924                        }
     925                }
     926
     927                /* don't set the topic if it's just the name */
     928                if (conv->title != NULL && strcmp(conv->name, conv->title) != 0) {
    900929                        imcb_chat_topic(gc, NULL, conv->title, 0);
    901930                }
     
    940969}
    941970
    942 void prplcb_conv_chat_msg(PurpleConversation *conv, const char *who, const char *message, PurpleMessageFlags flags,
    943                           time_t mtime)
    944 {
     971/* Generic handler for IM or chat messages, covers write_chat, write_im and write_conv */
     972static void handle_conv_msg(PurpleConversation *conv, const char *who, const char *message, guint32 bee_flags, time_t mtime)
     973{
     974        struct im_connection *ic = purple_ic_by_pa(conv->account);
    945975        struct groupchat *gc = conv->ui_data;
    946976        PurpleBuddy *buddy;
    947 
    948         /* ..._SEND means it's an outgoing message, no need to echo those. */
    949         if (flags & PURPLE_MESSAGE_SEND) {
    950                 return;
    951         }
    952977
    953978        buddy = purple_find_buddy(conv->account, who);
     
    956981        }
    957982
    958         imcb_chat_msg(gc, who, (char *) message, 0, mtime);
    959 }
    960 
    961 static void prplcb_conv_im(PurpleConversation *conv, const char *who, const char *message, PurpleMessageFlags flags,
    962                            time_t mtime)
    963 {
    964         struct im_connection *ic = purple_ic_by_pa(conv->account);
    965         PurpleBuddy *buddy;
    966 
    967         /* ..._SEND means it's an outgoing message, no need to echo those. */
     983        if (conv->type == PURPLE_CONV_TYPE_IM) {
     984                imcb_buddy_msg(ic, (char *) who, (char *) message, bee_flags, mtime);
     985        } else if (gc) {
     986                imcb_chat_msg(gc, who, (char *) message, bee_flags, mtime);
     987        }
     988}
     989
     990/* Handles write_im and write_chat. Removes echoes of locally sent messages */
     991static void prplcb_conv_msg(PurpleConversation *conv, const char *who, const char *message, PurpleMessageFlags flags, time_t mtime)
     992{
     993        if (!(flags & PURPLE_MESSAGE_SEND)) {
     994                handle_conv_msg(conv, who, message, 0, mtime);
     995        }
     996}
     997
     998/* Handles write_conv. Only passes self messages from other locations through.
     999 * That is, only writes of PURPLE_MESSAGE_SEND.
     1000 * There are more events which might be handled in the future, but some are tricky.
     1001 * (images look like <img id="123">, what do i do with that?) */
     1002static void prplcb_conv_write(PurpleConversation *conv, const char *who, const char *alias, const char *message,
     1003                              PurpleMessageFlags flags, time_t mtime)
     1004{
    9681005        if (flags & PURPLE_MESSAGE_SEND) {
    969                 return;
    970         }
    971 
    972         buddy = purple_find_buddy(conv->account, who);
    973         if (buddy != NULL) {
    974                 who = purple_buddy_get_name(buddy);
    975         }
    976 
    977         imcb_buddy_msg(ic, (char *) who, (char *) message, 0, mtime);
     1006                handle_conv_msg(conv, who, message, OPT_SELFMESSAGE, mtime);
     1007        }
    9781008}
    9791009
     
    10081038        prplcb_conv_new,           /* create_conversation  */
    10091039        prplcb_conv_free,          /* destroy_conversation */
    1010         prplcb_conv_chat_msg,      /* write_chat           */
    1011         prplcb_conv_im,            /* write_im             */
    1012         NULL,                      /* write_conv           */
     1040        prplcb_conv_msg,           /* write_chat           */
     1041        prplcb_conv_msg,           /* write_im             */
     1042        prplcb_conv_write,         /* write_conv           */
    10131043        prplcb_conv_add_users,     /* chat_add_users       */
    10141044        NULL,                      /* chat_rename_user     */
     
    11751205static PurpleRequestUiOps bee_request_uiops =
    11761206{
    1177         prplcb_request_input,
    1178         NULL,
    1179         prplcb_request_action,
    1180         NULL,
    1181         NULL,
    1182         prplcb_close_request,
    1183         NULL,
     1207        prplcb_request_input,      /* request_input */
     1208        NULL,                      /* request_choice */
     1209        prplcb_request_action,     /* request_action */
     1210        NULL,                      /* request_fields */
     1211        NULL,                      /* request_file */
     1212        prplcb_close_request,      /* close_request */
     1213        NULL,                      /* request_folder */
    11841214};
    11851215
     
    12221252static PurplePrivacyUiOps bee_privacy_uiops =
    12231253{
    1224         prplcb_privacy_permit_added,
    1225         prplcb_privacy_permit_removed,
    1226         prplcb_privacy_deny_added,
    1227         prplcb_privacy_deny_removed,
     1254        prplcb_privacy_permit_added,       /* permit_added */
     1255        prplcb_privacy_permit_removed,     /* permit_removed */
     1256        prplcb_privacy_deny_added,         /* deny_added */
     1257        prplcb_privacy_deny_removed,       /* deny_removed */
    12281258};
    12291259
     
    12351265static PurpleDebugUiOps bee_debug_uiops =
    12361266{
    1237         prplcb_debug_print,
     1267        prplcb_debug_print,        /* print */
    12381268};
    12391269
     
    12561286static PurpleEventLoopUiOps glib_eventloops =
    12571287{
    1258         prplcb_ev_timeout_add,
    1259         prplcb_ev_remove,
    1260         prplcb_ev_input_add,
    1261         prplcb_ev_remove,
     1288        prplcb_ev_timeout_add,     /* timeout_add */
     1289        prplcb_ev_remove,          /* timeout_remove */
     1290        prplcb_ev_input_add,       /* input_add */
     1291        prplcb_ev_remove,          /* input_remove */
    12621292};
     1293
     1294/* Absolutely no connection context at all. Thanks purple! brb crying */
     1295static void *prplcb_notify_message(PurpleNotifyMsgType type, const char *title,
     1296                                   const char *primary, const char *secondary)
     1297{
     1298        char *text = g_strdup_printf("%s%s - %s%s%s",
     1299                (type == PURPLE_NOTIFY_MSG_ERROR) ? "Error: " : "",
     1300                title,
     1301                primary ?: "",
     1302                (primary && secondary) ? " - " : "",
     1303                secondary ?: ""
     1304        );
     1305
     1306        if (local_bee->ui->log) {
     1307                local_bee->ui->log(local_bee, "purple", text);
     1308        }
     1309
     1310        g_free(text);
     1311
     1312        return NULL;
     1313}
    12631314
    12641315static void *prplcb_notify_email(PurpleConnection *gc, const char *subject, const char *from,
     
    13221373static PurpleNotifyUiOps bee_notify_uiops =
    13231374{
    1324         NULL,
    1325         prplcb_notify_email,
    1326         NULL,
    1327         NULL,
    1328         NULL,
    1329         NULL,
    1330         prplcb_notify_userinfo,
     1375        prplcb_notify_message,     /* notify_message */
     1376        prplcb_notify_email,       /* notify_email */
     1377        NULL,                      /* notify_emails */
     1378        NULL,                      /* notify_formatted */
     1379        NULL,                      /* notify_searchresults */
     1380        NULL,                      /* notify_searchresults_new_rows */
     1381        prplcb_notify_userinfo,    /* notify_userinfo */
    13311382};
    13321383
     
    13551406static PurpleAccountUiOps bee_account_uiops =
    13561407{
    1357         NULL,
    1358         NULL,
    1359         NULL,
    1360         prplcb_account_request_authorize,
    1361         NULL,
     1408        NULL,                              /* notify_added */
     1409        NULL,                              /* status_changed */
     1410        NULL,                              /* request_add */
     1411        prplcb_account_request_authorize,  /* request_authorize */
     1412        NULL,                              /* close_account_request */
    13621413};
    13631414
     
    13871438        char *dir;
    13881439
    1389         if (B_EV_IO_READ != PURPLE_INPUT_READ ||
    1390             B_EV_IO_WRITE != PURPLE_INPUT_WRITE) {
    1391                 /* FIXME FIXME FIXME FIXME FIXME :-) */
    1392                 exit(1);
    1393         }
     1440        g_assert((int) B_EV_IO_READ == (int) PURPLE_INPUT_READ);
     1441        g_assert((int) B_EV_IO_WRITE == (int) PURPLE_INPUT_WRITE);
    13941442
    13951443        dir = g_strdup_printf("%s/purple", global.conf->configdir);
     
    14091457                PurpleProxyInfo *pi = purple_global_proxy_get_info();
    14101458                switch (proxytype) {
     1459                case PROXY_SOCKS4A:
    14111460                case PROXY_SOCKS4:
    14121461                        purple_proxy_info_set_type(pi, PURPLE_PROXY_SOCKS4);
  • protocols/skype/README

    r63cad66 r3fbce97  
    203203
    204204  * `account skype set skypeconsole_receive true` will make the
    205     `skypeconsole` account dump all the recieved raw traffic for you
     205    `skypeconsole` account dump all the received raw traffic for you
    206206
    207207- If you want to automatically join bookmarked groupchats right after
  • protocols/skype/skype.c

    r63cad66 r3fbce97  
    2121
    2222#define _XOPEN_SOURCE
    23 #define _BSD_SOURCE
    2423#include <poll.h>
    2524#include <stdio.h>
     
    188187
    189188        va_start(args, fmt);
    190         vsnprintf(str, IRC_LINE_SIZE, fmt, args);
     189        g_vsnprintf(str, IRC_LINE_SIZE, fmt, args);
    191190        va_end(args);
    192191
     
    322321        }
    323322        return NULL;
     323}
     324
     325static struct groupchat *skype_chat_get_or_create(struct im_connection *ic, char *id)
     326{
     327        struct skype_data *sd = ic->proto_data;
     328        struct groupchat *gc = bee_chat_by_title(ic->bee, ic, id);
     329
     330        if (!gc) {
     331                gc = imcb_chat_new(ic, id);
     332                imcb_chat_name_hint(gc, id);
     333                imcb_chat_add_buddy(gc, sd->username);
     334
     335                skype_printf(ic, "GET CHAT %s ADDER\n", id);
     336                skype_printf(ic, "GET CHAT %s TOPIC\n", id);
     337                skype_printf(ic, "GET CHAT %s ACTIVEMEMBERS\n", id);
     338        }
     339
     340        return gc;
    324341}
    325342
     
    688705                info += 9;
    689706                if (sd->handle && sd->body && sd->type) {
    690                         struct groupchat *gc = bee_chat_by_title(ic->bee, ic, info);
     707                        struct groupchat *gc = skype_chat_get_or_create(ic, info);
    691708                        int i;
    692709                        for (i = 0; i < g_list_length(sd->body); i++) {
     
    10261043        }
    10271044        if (!strcmp(info, "STATUS MULTI_SUBSCRIBED")) {
    1028                 gc = bee_chat_by_title(ic->bee, ic, id);
    1029                 if (!gc) {
    1030                         gc = imcb_chat_new(ic, id);
    1031                         imcb_chat_name_hint(gc, id);
    1032                 }
    1033                 skype_printf(ic, "GET CHAT %s ADDER\n", id);
    1034                 skype_printf(ic, "GET CHAT %s TOPIC\n", id);
     1045                skype_chat_get_or_create(ic, id);
    10351046        } else if (!strcmp(info, "STATUS DIALOG") && sd->groupchat_with) {
    1036                 gc = imcb_chat_new(ic, id);
    1037                 imcb_chat_name_hint(gc, id);
     1047                gc = skype_chat_get_or_create(ic, id);
    10381048                /* According to the docs this
    10391049                 * is necessary. However it
     
    10461056                           sd->groupchat_with);
    10471057                imcb_chat_add_buddy(gc, buf);
    1048                 imcb_chat_add_buddy(gc, sd->username);
    10491058                g_free(sd->groupchat_with);
    10501059                sd->groupchat_with = NULL;
    1051                 skype_printf(ic, "GET CHAT %s ADDER\n", id);
    1052                 skype_printf(ic, "GET CHAT %s TOPIC\n", id);
    10531060        } else if (!strcmp(info, "STATUS UNSUBSCRIBED")) {
    10541061                gc = bee_chat_by_title(ic->bee, ic, id);
     
    12571264                }
    12581265                g_strfreev(lines);
    1259         } else if (st == 0 || (st < 0 && !sockerr_again())) {
     1266        } else if (st == 0 || (st < 0 && !ssl_sockerr_again(sd->ssl))) {
    12601267                ssl_disconnect(sd->ssl);
    12611268                sd->fd = -1;
  • protocols/skype/t/add-yes-bitlbee.mock

    r63cad66 r3fbce97  
    1 >> NOTICE AUTH
     1>> NOTICE *
    22<< NICK alice
    33<< USER alice alice localhost :Alice
  • protocols/skype/t/added-no-bitlbee.mock

    r63cad66 r3fbce97  
    1 >> NOTICE AUTH
     1>> NOTICE *
    22<< NICK alice
    33<< USER alice alice localhost :Alice
  • protocols/skype/t/added-yes-bitlbee.mock

    r63cad66 r3fbce97  
    1 >> NOTICE AUTH
     1>> NOTICE *
    22<< NICK alice
    33<< USER alice alice localhost :Alice
  • protocols/skype/t/away-set-bitlbee.mock

    r63cad66 r3fbce97  
    1 >> NOTICE AUTH
     1>> NOTICE *
    22<< NICK alice
    33<< USER alice alice localhost :Alice
  • protocols/skype/t/call-bitlbee.mock

    r63cad66 r3fbce97  
    1 >> NOTICE AUTH
     1>> NOTICE *
    22<< NICK alice
    33<< USER alice alice localhost :alice
  • protocols/skype/t/call-failed-bitlbee.mock

    r63cad66 r3fbce97  
    1 >> NOTICE AUTH
     1>> NOTICE *
    22<< NICK alice
    33<< USER alice alice localhost :alice
  • protocols/skype/t/called-no-bitlbee.mock

    r63cad66 r3fbce97  
    1 >> NOTICE AUTH
     1>> NOTICE *
    22<< NICK alice
    33<< USER alice alice localhost :Alice
  • protocols/skype/t/called-yes-bitlbee.mock

    r63cad66 r3fbce97  
    1 >> NOTICE AUTH
     1>> NOTICE *
    22<< NICK alice
    33<< USER alice alice localhost :Alice
  • protocols/skype/t/ctcp-help-bitlbee.mock

    r63cad66 r3fbce97  
    1 >> NOTICE AUTH
     1>> NOTICE *
    22<< NICK alice
    33<< USER alice alice localhost :Alice
  • protocols/skype/t/filetransfer-bitlbee.mock

    r63cad66 r3fbce97  
    1 >> NOTICE AUTH
     1>> NOTICE *
    22<< NICK alice
    33<< USER alice alice localhost :Alice
  • protocols/skype/t/group-add-bitlbee.mock

    r63cad66