Changeset 537d9b9


Ignore:
Timestamp:
2016-11-20T08:40:36Z (8 years ago)
Author:
dequis <dx@…>
Children:
3f44e43
Parents:
ba52ac5 (diff), 9f03c47 (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 master up to commit '9f03c47' into parson

Files:
4 added
70 edited

Legend:

Unmodified
Added
Removed
  • .travis.yml

    rba52ac5 r537d9b9  
    33
    44script:
    5  - ./configure
     5 - ./configure --pam=1 --ldap=1
    66 - make check
    77 - BITLBEE_SKYPE=plugin dpkg-buildpackage -uc -us -d
     
    2929    - libpurple-dev
    3030    - check
     31    - libpam0g-dev
     32    - libldap2-dev
    3133  coverity_scan:
    3234    project:
     
    3436      description: "An IRC to other chat networks gateway"
    3537    notification_email: dx@dxzone.com.ar
    36     build_command_prepend: ./configure --otr=1 --debug=1
     38    build_command_prepend: ./configure --otr=1 --debug=1 --pam=1 --ldap=1
    3739    build_command: make
    3840    branch_pattern: coverity_scan
  • Makefile

    rba52ac5 r537d9b9  
    1010
    1111# Program variables
    12 objects = bitlbee.o dcc.o help.o ipc.o irc.o irc_im.o irc_cap.o irc_channel.o irc_commands.o irc_send.o irc_user.o irc_util.o nick.o $(OTR_BI) query.o root_commands.o set.o storage.o $(STORAGE_OBJS) unix.o conf.o log.o
     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) auth.o $(AUTH_OBJS) unix.o conf.o log.o
    1313headers = $(wildcard $(_SRCDIR_)*.h $(_SRCDIR_)lib/*.h $(_SRCDIR_)protocols/*.h)
    1414subdirs = lib protocols
  • bitlbee.conf

    rba52ac5 r537d9b9  
    5252# AuthMode = Open
    5353
     54## AuthBackend
     55##
     56## By default, the authentication data for a user is stored in the storage
     57## backend. If you want to authenticate against another authentication system
     58## (e.g. ldap), you can specify that here.
     59##
     60## Beware that this disables password changes and causes passwords for the
     61## accounts people create to be stored in plain text instead of encrypted with
     62## their bitlbee password.
     63##
     64## Currently available backends:
     65##
     66## - storage (internal storage)
     67## - pam (Linux PAM authentication)
     68## - ldap (LDAP server configured in the openldap settings)
     69#
     70# AuthBackend = storage
     71#
     72
    5473## AuthPassword
    5574##
     
    6988## or
    7089# OperPassword = md5:I0mnZbn1t4R731zzRdDN2/pK7lRX
     90
     91## AllowAccountAdd
     92##
     93## Whether to allow registered and identified users to add new accounts using
     94## 'account add'
     95##
     96# AllowAccountAdd 1
    7197
    7298## HostName
  • bitlbee.h

    rba52ac5 r537d9b9  
    3636
    3737#define PACKAGE "BitlBee"
    38 #define BITLBEE_VERSION "3.4.1"
     38#define BITLBEE_VERSION "3.4.2"
    3939#define VERSION BITLBEE_VERSION
    4040#define BITLBEE_VER(a, b, c) (((a) << 16) + ((b) << 8) + (c))
    41 #define BITLBEE_VERSION_CODE BITLBEE_VER(3, 4, 1)
     41#define BITLBEE_VERSION_CODE BITLBEE_VER(3, 4, 2)
     42#define BITLBEE_ABI_VERSION_CODE 1
    4243
    4344#define MAX_STRING 511
     
    133134#include "irc.h"
    134135#include "storage.h"
     136#include "auth.h"
    135137#include "set.h"
    136138#include "nogaim.h"
     
    154156        conf_t *conf;
    155157        GList *storage; /* The first backend in the list will be used for saving */
     158        GList *auth;    /* Authentication backends */
    156159        char *helpfile;
    157160        int restart;
     
    170173gboolean root_command_add(const char *command, int params, void (*func)(irc_t *, char **args), int flags);
    171174gboolean cmd_identify_finish(gpointer data, gint fd, b_input_condition cond);
     175void cmd_chat_list_finish(struct im_connection *ic);
    172176gboolean bitlbee_shutdown(gpointer data, gint fd, b_input_condition cond);
    173177
  • conf.c

    rba52ac5 r537d9b9  
    5555        conf->runmode = RUNMODE_INETD;
    5656        conf->authmode = AUTHMODE_OPEN;
     57        conf->auth_backend = NULL;
    5758        conf->auth_pass = NULL;
    5859        conf->oper_pass = NULL;
     60        conf->allow_account_add = 1;
    5961        conf->configdir = g_strdup(CONFIG);
    6062        conf->plugindir = g_strdup(PLUGINDIR);
     
    240242                                        conf->authmode = AUTHMODE_OPEN;
    241243                                }
     244                        } else if (g_strcasecmp(ini->key, "authbackend") == 0) {
     245                                if (g_strcasecmp(ini->value, "storage") == 0) {
     246                                        conf->auth_backend = NULL;
     247                                } else if (g_strcasecmp(ini->value, "pam") == 0 ||
     248                                         g_strcasecmp(ini->value, "ldap") == 0) {
     249                                        g_free(conf->auth_backend);
     250                                        conf->auth_backend = g_strdup(ini->value);
     251                                } else {
     252                                        fprintf(stderr, "Invalid %s value: %s\n", ini->key, ini->value);
     253                                        return 0;
     254                                }
    242255                        } else if (g_strcasecmp(ini->key, "authpassword") == 0) {
    243256                                g_free(conf->auth_pass);
     
    246259                                g_free(conf->oper_pass);
    247260                                conf->oper_pass = g_strdup(ini->value);
     261                        } else if (g_strcasecmp(ini->key, "allowaccountadd") == 0) {
     262                                if (!is_bool(ini->value)) {
     263                                        fprintf(stderr, "Invalid %s value: %s\n", ini->key, ini->value);
     264                                        return 0;
     265                                }
     266                                conf->allow_account_add = bool2int(ini->value);
    248267                        } else if (g_strcasecmp(ini->key, "hostname") == 0) {
    249268                                g_free(conf->hostname);
  • conf.h

    rba52ac5 r537d9b9  
    3737        runmode_t runmode;
    3838        authmode_t authmode;
     39        char *auth_backend;
    3940        char *auth_pass;
    4041        char *oper_pass;
     42        int allow_account_add;
    4143        char *hostname;
    4244        char *configdir;
  • configure

    rba52ac5 r537d9b9  
    5454ssl=auto
    5555
     56pam=0
     57ldap=0
     58
    5659pie=1
    5760
    5861arch=$(uname -s)
    59 cpu=$(uname -m)
    6062
    6163GLIB_MIN_VERSION=2.16
     
    138140--rpc=0/1       Disable/enable RPC plugin interface     $rpc
    139141
     142--pam=0/1       Disable/enable PAM authentication       $pam
     143--ldap=0/1      Disable/enable LDAP authentication      $ldap
     144
    140145--doc=0/1       Disable/enable help.txt generation      $doc
    141146--debug=0/1     Disable/enable debugging                $debug
     
    200205
    201206TARGET=$target
    202 ARCH=$arch
    203 CPU=$cpu
    204207
    205208INSTALL=install -p
     
    249252#define PIDFILE "$pidfile"
    250253#define IPCSOCKET "$ipcsocket"
    251 #define ARCH "$arch"
    252 #define CPU "$cpu"
    253254EOF
    254255
     
    348349fi
    349350
    350 if $PKG_CONFIG --version > /dev/null 2>/dev/null && $PKG_CONFIG glib-2.0; then
     351if ! $PKG_CONFIG --version > /dev/null 2>/dev/null; then
     352        echo
     353        echo 'Cannot find pkg-config, aborting.'
     354        exit 1
     355fi
     356
     357if $PKG_CONFIG glib-2.0; then
    351358        if $PKG_CONFIG glib-2.0 --atleast-version=$GLIB_MIN_VERSION; then
    352359                cat<<EOF >>Makefile.settings
     
    641648done
    642649echo "STORAGE_OBJS="$STORAGE_OBJS >> Makefile.settings
     650
     651authobjs=
     652authlibs=
     653if [ "$pam" = 0 ]; then
     654        echo '#undef WITH_PAM' >> config.h
     655else
     656        if ! echo '#include <security/pam_appl.h>' | $CC -E - >/dev/null 2>/dev/null; then
     657                echo 'Cannot find libpam development libraries, aborting. (Install libpam0g-dev?)'
     658                exit 1
     659        fi
     660        echo '#define WITH_PAM' >> config.h
     661        authobjs=$authobjs'auth_pam.o '
     662        authlibs=$authlibs'-lpam '
     663fi
     664if [ "$ldap" = 0 ]; then
     665        echo '#undef WITH_LDAP' >> config.h
     666else
     667        if ! echo '#include <ldap.h>' | $CC -E - >/dev/null 2>/dev/null; then
     668                echo 'Cannot find libldap development libraries, aborting. (Install libldap2-dev?)'
     669                exit 1
     670        fi
     671        echo '#define WITH_LDAP' >> config.h
     672        authobjs=$authobjs'auth_ldap.o '
     673        authlibs=$authlibs'-lldap '
     674fi
     675echo AUTH_OBJS=$authobjs >> Makefile.settings
     676echo EFLAGS+=$authlibs >> Makefile.settings
    643677
    644678if [ "$strip" = 0 ]; then
  • debian/bitlbee-common.postinst

    rba52ac5 r537d9b9  
    99
    1010CONFDIR=/var/lib/bitlbee/
    11 
    12 update-rc.d bitlbee defaults > /dev/null 2>&1
    1311
    1412## Load default option. Don't want to put this in debconf (yet?)
     
    6664fi
    6765
    68 if [ "$BITLBEE_UPGRADE_DONT_RESTART" != "1" -a  -n "$2" ]; then
     66if [ "$BITLBEE_UPGRADE_DONT_RESTART" != "1" -a  -n "$2" -a -x "/etc/init.d/bitlbee" ]; then
    6967        invoke-rc.d bitlbee restart
    7068fi
     
    9290fi
    9391
    94 if [ -z "$2" ]; then
    95         invoke-rc.d bitlbee start
    96 fi
     92#DEBHELPER#
  • debian/bitlbee-common.postrm

    rba52ac5 r537d9b9  
    33set -e
    44
    5 [ "$1" = "purge" ] || exit 0
    6 
    7 if [ -e /usr/share/debconf/confmodule ]; then
    8         . /usr/share/debconf/confmodule;
    9         db_purge;
     5if [ "$1" = "purge" ]; then
     6        rm -f /etc/default/bitlbee
     7        deluser --system bitlbee || true
     8        rm -rf /var/lib/bitlbee ## deluser doesn't seem to do this for homedirs in /var
    109fi
    1110
    12 update-rc.d bitlbee remove > /dev/null 2>&1 || true
    13 rm -f /etc/default/bitlbee
    14 
    15 deluser --system bitlbee || true
    16 rm -rf /var/lib/bitlbee ## deluser doesn't seem to do this for homedirs in /var
     11#DEBHELPER#
  • debian/bitlbee-common.prerm

    rba52ac5 r537d9b9  
    1111                mv /usr/share/bitlbee/help.txt /usr/share/bitlbee/help.upgrading
    1212        fi
    13 else
    14         invoke-rc.d bitlbee stop
    1513fi
     14
     15#DEBHELPER#
  • debian/bitlbee.prerm

    rba52ac5 r537d9b9  
    66        invoke-rc.d bitlbee stop
    77fi
     8
     9#DEBHELPER#
  • debian/changelog

    rba52ac5 r537d9b9  
     1bitlbee (3.4.2-1) unstable; urgency=medium
     2
     3  [ Jelmer Vernooij ]
     4  * Make the build reproducible by not encoding ARCH / CPU defines in
     5    the binary.
     6  * Fix Vcs-* control headers after migration to Git.
     7  * Don't start synopsis with an article.
     8  * Bump standards version to 3.9.8 (no changes).
     9  * Use dh_prep rather than deprecated 'dh_clean -k'.
     10
     11  [ Wilmer van der Gaast ]
     12  * New upstream release.
     13
     14 -- Wilmer van der Gaast <wilmer@gaast.net>  Sun, 12 Jun 2016 22:31:18 +0100
     15
    116bitlbee (3.4.1-1) unstable; urgency=medium
    217
  • debian/control

    rba52ac5 r537d9b9  
    33Priority: optional
    44Maintainer: Wilmer van der Gaast <wilmer@gaast.net>
    5 Uploaders: Jelmer Vernooij <jelmer@debian.org>
    6 Standards-Version: 3.9.5
    7 Build-Depends: libglib2.0-dev (>= 2.4), libevent-dev, libgnutls28-dev | libgnutls-dev | gnutls-dev, po-debconf, libpurple-dev, libotr5-dev, debhelper (>= 6.0.7~)
     5Uploaders: Jelmer Vernooij <jelmer@debian.org>
     6Standards-Version: 3.9.8
     7Build-Depends: libglib2.0-dev (>= 2.4), libevent-dev, libgnutls28-dev | libgnutls-dev | gnutls-dev, po-debconf, libpurple-dev, libotr5-dev, debhelper (>= 6.0.7~), dh-systemd (>= 1.5) | debhelper (<< 9.20131227)
    88Homepage: http://www.bitlbee.org/
    9 Vcs-Bzr: http://code.bitlbee.org/bitlbee/
     9Vcs-Git: https://github.com/bitlbee/bitlbee
     10Vcs-Browser: https://github.com/bitlbee/bitlbee
    1011
    1112Package: bitlbee
     
    1415Conflicts: bitlbee-libpurple
    1516Replaces: bitlbee-libpurple
    16 Description: An IRC to other chat networks gateway (default version)
     17Description: IRC to other chat networks gateway (default version)
    1718 This program can be used as an IRC server which forwards everything you
    1819 say to people on other chat networks: Jabber (which includes Google Talk
     
    2425Conflicts: bitlbee
    2526Replaces: bitlbee
    26 Description: An IRC to other chat networks gateway (using libpurple)
     27Description: IRC to other chat networks gateway (using libpurple)
    2728 This program can be used as an IRC server which forwards everything you
    2829 say to people on other chat networks: Jabber (which includes Google Talk
     
    4041Depends: ${misc:Depends}, net-tools, adduser
    4142Replaces: bitlbee
    42 Description: An IRC to other chat networks gateway (common files/docs)
     43Description: IRC to other chat networks gateway (common files/docs)
    4344 This program can be used as an IRC server which forwards everything you
    4445 say to people on other chat networks: Jabber (which includes Google Talk
     
    5152Architecture: all
    5253Depends: ${misc:Depends}, bitlbee (>= ${source:Version}) | bitlbee-libpurple (>= ${source:Version}), bitlbee (<< ${source:Version}.1~) | bitlbee-libpurple (<< ${source:Version}.1~), bitlbee-common (= ${source:Version})
    53 Description: An IRC to other chat networks gateway (dev files)
     54Description: IRC to other chat networks gateway (dev files)
    5455 This program can be used as an IRC server which forwards everything you
    5556 say to people on other chat networks: Jabber (which includes Google Talk
     
    6162Architecture: any
    6263Depends: ${misc:Depends}, ${shlibs:Depends}, bitlbee (= ${binary:Version}) | bitlbee-libpurple (= ${binary:Version}), bitlbee-common (= ${source:Version})
    63 Description: An IRC to other chat networks gateway (OTR plugin)
     64Description: IRC to other chat networks gateway (OTR plugin)
    6465 This program can be used as an IRC server which forwards everything you
    6566 say to people on other chat networks: Jabber (which includes Google Talk
     
    7374Depends: ${misc:Depends}, ${shlibs:Depends}, bitlbee (= ${binary:Version}) | bitlbee-libpurple (= ${binary:Version}), bitlbee-common (= ${source:Version})
    7475Recommends: skyped
    75 Description: An IRC to other chat networks gateway (Skype plugin)
     76Description: IRC to other chat networks gateway (Skype plugin)
    7677 This program can be used as an IRC server which forwards everything you
    7778 say to people on other chat networks: Jabber (which includes Google Talk
  • debian/rules

    rba52ac5 r537d9b9  
    4040CONFIGURE_OVERRIDES:=CPPFLAGS="$(CPPFLAGS)" CFLAGS="$(CFLAGS)" LDFLAGS="$(LDFLAGS)"
    4141
     42HAS_DH_SYSTEMD:=$(shell dpkg-query -W -f='$${Status}' dh-systemd 2>/dev/null | grep -c "ok installed")
     43
    4244build: build-stamp
    4345build-stamp:
     
    7274        dh_testdir
    7375        dh_testroot
    74         dh_clean -k
     76        dh_prep
    7577        dh_installdirs
    7678
     
    8082        $(MAKE) -C debian/build-native install-plugin-otr DESTDIR=`pwd`/debian/bitlbee-plugin-otr
    8183        $(MAKE) -C debian/build-native install-plugin-skype DESTDIR=`pwd`/debian/skyped
     84
     85ifeq ($(HAS_DH_SYSTEMD),1)
     86        $(MAKE) -C debian/build-native install-systemd DESTDIR=`pwd`/debian/bitlbee-common
     87endif
    8288
    8389ifneq ($(BITLBEE_SKYPE),0)
     
    108114        for p in bitlbee bitlbee-libpurple bitlbee-dev bitlbee-plugin-otr; do rm -r debian/$$p/usr/share/doc/$$p && ln -s bitlbee-common debian/$$p/usr/share/doc/$$p || true; done
    109115        dh_installdebconf
     116ifeq ($(HAS_DH_SYSTEMD),1)
     117        dh_systemd_enable
    110118        dh_installinit --init-script=bitlbee
     119        dh_systemd_start
     120else
     121        dh_installinit --init-script=bitlbee
     122endif
    111123        dh_installman
    112124        dh_lintian
  • doc/CHANGES

    rba52ac5 r537d9b9  
    33
    44https://github.com/bitlbee/bitlbee/commits/master
     5
     6Version 3.4.2:
     7- irc:
     8  * Self-messages (messages sent by yourself from other IM clients), given
     9    support by the IM protocols and your IRC client. See this for details:
     10    https://wiki.bitlbee.org/SelfMessages
     11  * IRCv3.1 support and part of 3.2: cap-3.2, sasl-3.2, multi-prefix,
     12    away-notify, extended-join, userhost-in-names
     13  * Send numeric errors when failing to join a channel, to not confuse clients
     14  * Channel autojoins should be more reliable now.
     15- jabber:
     16  * Carbons (XEP-0280), for self-message support. It's not widely supported
     17    by most public XMPP servers (easier if you host your own), but this will
     18    probably change in the next few years. Thanks kormat for the original patch.
     19  * Fix typing notifications between two bitlbee users or with gtalk users
     20  * Remove facebook XMPP code, point people at bitlbee-facebook.
     21  * Show groupchat kick/ban/leave reasons
     22  * SASL ANONYMOUS (XEP-0175), for "guest" logins, see "help set anonymous"
     23  * Hipchat: 'chat add hipchat "channel name"' now tries to guess the JID
     24- purple:
     25  * Fix problems remembering SSL certificates as trusted
     26  * Fix /join #channel, which joined a differently named channel
     27  * Fix crash when doing "chat with" with skypeweb
     28  * Fix html entities appearing in some protocols
     29  * Fix setting away states in jabber, which failed silently
     30  * Implement notify_message UI op, to be able to show some error messages.
     31- skype:
     32  * Show all messages as groupchats since we can't tell which ones are private.
     33  * This plugin is mostly-deprecated and mostly-broken but it's still useful
     34    for p2p-based groupchats, which aren't delivered over newer protocols.
     35    Everyone else should use the skypeweb purple plugin or msn instead.
     36- msn:
     37  * Minor tweaks. Faster login, better error reporting, fixed add/remove.
     38    Still MSNP21. Disregard that "Next release!" in the previous release.
     39- otr:
     40  * Don't use NOTICE for user messages (revmischa)
     41  * Fix crashes when using the jabber xmlconsole
     42  * A few minor fixes: color multiline messages, filter incoming color codes.
     43- Packaging:
     44  * Show ./configure args in bitlbee -V, config.h and Makefile.settings
     45  * Allow setting the plugin dir in bitlbee.conf, for NixOS (anderspapitto)
     46  * Improved cross compiler support (gamaral)
     47- Other important bugfixes:
     48  * Fix potential crashes when leaving temporary channels
     49  * Fix all sorts of crashing bugs when cancelling in-progress connections.
     50
     51Finished 19 Mar 2016
    552
    653Version 3.4.1:
  • doc/user-guide/commands.xml

    rba52ac5 r537d9b9  
    258258
    259259                        <para>
    260                                 Available actions: add, with. See <emphasis>help chat &lt;action&gt;</emphasis> for more information.
     260                                Available actions: add, with, list. See <emphasis>help chat &lt;action&gt;</emphasis> for more information.
    261261                        </para>
    262262
     
    264264
    265265                <bitlbee-command name="add">
    266                         <syntax>chat add &lt;account id&gt; &lt;room&gt; [&lt;channel&gt;]</syntax>
     266                        <syntax>chat add &lt;account id&gt; &lt;room|!index&gt; [&lt;channel&gt;]</syntax>
    267267
    268268                        <description>
     
    272272
    273273                                <para>
    274                                         After adding a room to your list, you can simply use the IRC /join command to enter the room. Also, you can tell BitlBee to automatically join the room when you log in. (See <emphasis>chat set</emphasis>)
     274                                        After adding a room to your list, you can simply use the IRC /join command to enter the room. Also, you can tell BitlBee to automatically join the room when you log in. (<emphasis>channel &lt;channel&gt; set auto_join true</emphasis>)
    275275                                </para>
    276276
     
    280280                        </description>
    281281
     282                </bitlbee-command>
     283
     284                <bitlbee-command name="list">
     285                        <syntax>chat list &lt;account id&gt; [&lt;server&gt;]</syntax>
     286
     287                        <description>
     288                                <para>
     289                                        List existing chatrooms provided by an account. BitlBee needs this to propogate an internal list of chats. The existing chat can then be added with <emphasis>chat add</emphasis>, using the number in the index column after a "!" as a shortcut.
     290                                </para>
     291                        </description>
     292
     293                        <ircexample>
     294                                <ircline nick="dx">chat list facebook</ircline>
     295                                <ircline pre="1" nick="root">Index  Title                 Topic</ircline>
     296                                <ircline pre="1" nick="root">    1  869891016470949       cool kids club</ircline>
     297                                <ircline pre="1" nick="root">    2  457892181062459       uncool kids club</ircline>
     298                                <ircline nick="root">2 facebook chatrooms</ircline>
     299                                <ircline nick="dx">chat add facebook !1 #cool-kids-club</ircline>
     300                        </ircexample>
    282301                </bitlbee-command>
    283302
     
    288307                                <para>
    289308                                        While most <emphasis>chat</emphasis> subcommands are about named chatrooms, this command can be used to open an unnamed groupchat with one or more persons. This command is what <emphasis>/join #nickname</emphasis> used to do in older BitlBee versions.
     309                                </para>
     310
     311                                <para>
     312                                        Another way to do this is to join to a new, empty channel with <emphasis>/join #newchannel</emphasis> and invite the first person with <emphasis>/invite nickname</emphasis>
    290313                                </para>
    291314                        </description>
     
    645668                        <para>
    646669                                When you're already connected to a BitlBee server and you connect (and identify) again, BitlBee will offer to migrate your existing session to the new connection. If for whatever reason you don't want this, you can disable this setting.
     670                        </para>
     671                </description>
     672        </bitlbee-setting>
     673
     674        <bitlbee-setting name="always_use_nicks" type="boolean" scope="channel">
     675                <default>false</default>
     676
     677                <description>
     678                        <para>
     679                                Jabber groupchat specific. This setting ensures that the nicks defined by the other members of a groupchat are used, instead of the username part of their JID. This only applies to groupchats where their real JID is known (either "non-anonymous" ones, or "semi-anonymous" from the point of view of the channel moderators)
     680                        </para>
     681
     682                        <para>
     683                                Enabling this may have the side effect of changing the nick of existing contacts, either in your buddy list or in other groupchats. If a contact is in multiple groupchats with different nicks, enabling this setting for all those would result in multiple nick changes when joining, and the order of those changes may vary.
     684                        </para>
     685
     686                        <para>
     687                                Note that manual nick changes done through the <emphasis>rename</emphasis> command always take priority
    647688                        </para>
    648689                </description>
     
    14911532                                and any modes they should have. The following statuses are currently
    14921533                                recognised: <emphasis>online</emphasis> (i.e. available, not
    1493                                 away), <emphasis>special</emphasis>, <emphasis>away</emphasis>,
    1494                                 and <emphasis>offline</emphasis>.
     1534                                away), <emphasis>special</emphasis> (specific to the protocol),
     1535                                <emphasis>away</emphasis>, and <emphasis>offline</emphasis>.
    14951536                        </para>
    14961537                       
     
    14981539                                If a status is followed by a valid channel mode character
    14991540                                (@, % or +), it will be given to users with that status.
    1500                                 For example, <emphasis>online@,special%,away+,offline</emphasis>
     1541                                For example, <emphasis>online@,special%,away+,offline</emphasis> 
    15011542                                will show all users in the channel. Online people will
    15021543                                have +o, people who are online but away will have +v,
     
    18151856                        </para>
    18161857                </description>
     1858        </bitlbee-command>
     1859
     1860        <bitlbee-command name="plugins">
     1861                <short-description>List all the external plugins and protocols</short-description>
     1862                <syntax>plugins</syntax>
     1863
     1864                <description>
     1865                        <para>
     1866                                This gives you a list of all the external plugins and protocols.
     1867                        </para>
     1868                </description>
     1869
    18171870        </bitlbee-command>
    18181871
  • doc/user-guide/genhelp.py

    rba52ac5 r537d9b9  
    6565
    6666    # Actually normalize whitespace
    67     tag.text = normalize(tag.text)
     67    if 'pre' not in tag.attrib:
     68        tag.text = normalize(tag.text)
    6869    tag.tail = normalize(tag.tail)
    6970
  • doc/user-guide/misc.xml

    rba52ac5 r537d9b9  
    445445</sect1>
    446446
     447<sect1 id="whatsnew030402">
     448<title>New stuff in BitlBee 3.4.2</title>
     449
     450<simplelist>
     451        <member><emphasis>irc:</emphasis> Self-messages (messages sent by yourself from other IM clients), see <emphasis>help set self_messages</emphasis>. IRCv3.1 support and part of 3.2. Many important groupchat related bugfixes.</member>
     452        <member><emphasis>jabber:</emphasis> Carbons, see <emphasis>help set carbons</emphasis>. Removed facebook XMPP, use bitlbee-facebook instead. SASL ANONYMOUS login, see <emphasis>help set anonymous</emphasis>.</member>
     453        <member><emphasis>hipchat:</emphasis> Channels can now be added with <emphasis>chat add hipchat "channel name"</emphasis> which tries to guess the channel JID.</member>
     454        <member><emphasis>skype:</emphasis> Show all messages as groupchats since we can't tell which ones are private. This plugin is mostly-deprecated and mostly-broken, use the skypeweb purple plugin or msn instead.</member>
     455        <member><emphasis>purple:</emphasis> Fix problems remembering SSL certificates as trusted. Groupchat related fixes. Better error reporting. Fixed setting jabber away states.</member>
     456</simplelist>
     457
     458<para>
     459        And lots of bugfixes / stability improvements. See the full changelog for details!
     460</para>
     461
     462</sect1>
     463
    447464</chapter>
  • init/bitlbee.service.in

    rba52ac5 r537d9b9  
    55[Service]
    66ExecStart=@sbindir@bitlbee -F -n
     7KillMode=process
    78
    89[Install]
  • irc.h

    rba52ac5 r537d9b9  
    2929#define IRC_MAX_LINE 512
    3030#define IRC_MAX_ARGS 16
     31#define IRC_WORD_WRAP 425
    3132
    3233#define IRC_LOGIN_TIMEOUT 60
     
    9293                           logging in, this may contain a password we should
    9394                           send to identify after USER/NICK are received. */
     95        char *auth_backend;
    9496
    9597        char umode[8];
  • irc_cap.c

    rba52ac5 r537d9b9  
    177177
    178178        } else if (g_strcasecmp(cmd[1], "END") == 0) {
     179                if (!(irc->status & USTATUS_CAP_PENDING)) {
     180                        return;
     181                }
    179182                irc->status &= ~USTATUS_CAP_PENDING;
    180183
  • irc_channel.c

    rba52ac5 r537d9b9  
    245245        ic->users = g_slist_insert_sorted(ic->users, icu, irc_channel_user_cmp);
    246246
    247         irc_channel_update_ops(ic, set_getstr(&ic->irc->b->set, "ops"));
     247        if (iu == ic->irc->user || iu == ic->irc->root) {
     248                irc_channel_update_ops(ic, set_getstr(&ic->irc->b->set, "ops"));
     249        }
    248250
    249251        if (iu == ic->irc->user || ic->flags & IRC_CHANNEL_JOINED) {
  • irc_commands.c

    rba52ac5 r537d9b9  
    9797        /* just check the password here to be able to reply with useful numerics
    9898         * the actual identification will be handled later */
    99         status = storage_check_pass(user, pass);
     99        status = auth_check_pass(irc, user, pass);
    100100
    101101        if (status == STORAGE_OK) {
     
    767767static void irc_cmd_version(irc_t *irc, char **cmd)
    768768{
    769         irc_send_num(irc, 351, "%s-%s. %s :%s/%s ",
    770                      PACKAGE, BITLBEE_VERSION, irc->root->host, ARCH, CPU);
     769        irc_send_num(irc, 351, "%s-%s. %s :",
     770                     PACKAGE, BITLBEE_VERSION, irc->root->host);
    771771}
    772772
  • irc_im.c

    rba52ac5 r537d9b9  
    120120        }
    121121
    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);
    126         }
    127 
    128122        if ((bu->flags & BEE_USER_ONLINE) != (old->flags & BEE_USER_ONLINE)) {
    129123                if (bu->flags & BEE_USER_ONLINE) {
     
    153147
    154148        bee_irc_channel_update(irc, NULL, iu);
     149
     150        /* If away-notify enabled, send status updates when:
     151         * Away or Online state changes
     152         * Status changes (e.g. "Away" to "Mobile")
     153         * Status message changes
     154         */
     155        if ((irc->caps & CAP_AWAY_NOTIFY) &&
     156            ((bu->flags & BEE_USER_AWAY) != (old->flags & BEE_USER_AWAY) ||
     157             (bu->flags & BEE_USER_ONLINE) != (old->flags & BEE_USER_ONLINE) ||
     158             (g_strcmp0(bu->status, old->status) != 0) ||
     159             (g_strcmp0(bu->status_msg, old->status_msg) != 0))) {
     160                irc_send_away_notify(iu);
     161        }
    155162
    156163        return TRUE;
     
    292299        }
    293300
    294         wrapped = word_wrap(msg, 425);
     301        wrapped = word_wrap(msg, IRC_WORD_WRAP);
    295302        irc_send_msg(src_iu, message_type, dst, wrapped, prefix);
    296303        g_free(wrapped);
     
    342349}
    343350
    344 static gboolean bee_irc_user_nick_update(irc_user_t *iu);
     351static gboolean bee_irc_user_nick_update(irc_user_t *iu, gboolean offline_only);
    345352
    346353static gboolean bee_irc_user_fullname(bee_t *bee, bee_user_t *bu)
     
    370377        }
    371378
    372         bee_irc_user_nick_update(iu);
     379        bee_irc_user_nick_update(iu, TRUE);
    373380
    374381        return TRUE;
     
    377384static gboolean bee_irc_user_nick_hint(bee_t *bee, bee_user_t *bu, const char *hint)
    378385{
    379         bee_irc_user_nick_update((irc_user_t *) bu->ui_data);
     386        bee_irc_user_nick_update((irc_user_t *) bu->ui_data, TRUE);
     387
     388        return TRUE;
     389}
     390
     391static gboolean bee_irc_user_nick_change(bee_t *bee, bee_user_t *bu, const char *nick)
     392{
     393        bee_irc_user_nick_update((irc_user_t *) bu->ui_data, FALSE);
    380394
    381395        return TRUE;
     
    386400        irc_user_t *iu = (irc_user_t *) bu->ui_data;
    387401        irc_t *irc = (irc_t *) bee->ui_data;
    388         bee_user_flags_t online;
    389 
    390         /* Take the user offline temporarily so we can change the nick (if necessary). */
    391         if ((online = bu->flags & BEE_USER_ONLINE)) {
    392                 bu->flags &= ~BEE_USER_ONLINE;
    393         }
    394402
    395403        bee_irc_channel_update(irc, NULL, iu);
    396         bee_irc_user_nick_update(iu);
    397 
    398         if (online) {
    399                 bu->flags |= online;
    400                 bee_irc_channel_update(irc, NULL, iu);
    401         }
    402 
    403         return TRUE;
    404 }
    405 
    406 static gboolean bee_irc_user_nick_update(irc_user_t *iu)
     404        bee_irc_user_nick_update(iu, FALSE);
     405
     406        return TRUE;
     407}
     408
     409static gboolean bee_irc_user_nick_update(irc_user_t *iu, gboolean offline_only)
    407410{
    408411        bee_user_t *bu = iu->bu;
    409412        char *newnick;
    410413
    411         if (bu->flags & BEE_USER_ONLINE) {
     414        if (offline_only && bu->flags & BEE_USER_ONLINE) {
    412415                /* Ignore if the user is visible already. */
    413416                return TRUE;
     
    432435{
    433436        bee_user_t *bu = iu->bu;
    434         bee_user_flags_t online;
    435437
    436438        if (bu == FALSE) {
     
    438440        }
    439441
    440         /* In this case, pretend the user is offline. */
    441         if ((online = bu->flags & BEE_USER_ONLINE)) {
    442                 bu->flags &= ~BEE_USER_ONLINE;
    443         }
    444 
    445442        nick_del(bu);
    446         bee_irc_user_nick_update(iu);
    447 
    448         bu->flags |= online;
     443        bee_irc_user_nick_update(iu, FALSE);
     444
    449445}
    450446
     
    461457        }
    462458
    463         if ((away = irc_user_get_away(iu)) &&
     459        if (iu->last_channel == NULL &&
     460            (away = irc_user_get_away(iu)) &&
    464461            time(NULL) >= iu->away_reply_timeout) {
    465462                irc_send_num(iu->irc, 301, "%s :%s", iu->nick, away);
     
    676673        }
    677674
    678         wrapped = word_wrap(msg, 425);
     675        wrapped = word_wrap(msg, IRC_WORD_WRAP);
    679676        irc_send_msg(iu, "PRIVMSG", ic->name, wrapped, ts);
    680677        g_free(ts);
     
    11461143
    11471144        bee_irc_log,
     1145        bee_irc_user_nick_change,
    11481146};
  • irc_send.c

    rba52ac5 r537d9b9  
    4141{
    4242        irc_send_num(irc,   1, ":Welcome to the %s gateway, %s", PACKAGE, irc->user->nick);
    43         irc_send_num(irc,   2, ":Host %s is running %s %s %s/%s.", irc->root->host,
    44                      PACKAGE, BITLBEE_VERSION, ARCH, CPU);
     43        irc_send_num(irc,   2, ":Host %s is running %s %s.", irc->root->host,
     44                     PACKAGE, BITLBEE_VERSION);
    4545        irc_send_num(irc,   3, ":%s", IRCD_INFO);
    4646        irc_send_num(irc,   4, "%s %s %s %s", irc->root->host, BITLBEE_VERSION, UMODES UMODES_PRIV, CMODES);
     
    5454void irc_send_motd(irc_t *irc)
    5555{
    56         char motd[2048];
    57         ssize_t len;
    58         int fd;
    59 
    60         fd = open(global.conf->motdfile, O_RDONLY);
    61         if (fd == -1 || (len = read(fd, motd, sizeof(motd) - 1)) <= 0) {
     56        char *motd;
     57        size_t len;
     58
     59        g_file_get_contents(global.conf->motdfile, &motd, &len, NULL);
     60
     61        if (!motd || !len) {
    6262                irc_send_num(irc, 422, ":We don't need MOTDs.");
    6363        } else {
     
    6666
    6767                in = motd;
    68                 motd[len] = '\0';
    6968                linebuf[79] = len = 0;
    7069                max = sizeof(linebuf) - 1;
     
    101100        }
    102101
    103         if (fd != -1) {
    104                 close(fd);
    105         }
     102        g_free(motd);
     103
    106104}
    107105
     
    348346                }
    349347
    350                 /* rfc1459 doesn't mention this: G means gone, H means here */
    351                 status_prefix[0] = iu->flags & IRC_USER_AWAY ? 'G' : 'H';
     348                /* If this is the account nick, check configuration to see if away */
     349                if (iu == irc->user) {
     350                        /* rfc1459 doesn't mention this: G means gone, H means here */
     351                        status_prefix[0] = set_getstr(&irc->b->set, "away") ? 'G' : 'H';
     352                } else {
     353                        status_prefix[0] = iu->flags & IRC_USER_AWAY ? 'G' : 'H';
     354                }
    352355
    353356                irc_send_num(irc, 352, "%s %s %s %s %s %s :0 %s",
  • irc_user.c

    rba52ac5 r537d9b9  
    251251        if (g_strcasecmp(ctcp[0], "VERSION") == 0) {
    252252                irc_send_msg_f(iu, "NOTICE", iu->irc->user->nick, "\001%s %s\001",
    253                                ctcp[0], PACKAGE " " BITLBEE_VERSION " " ARCH "/" CPU);
     253                               ctcp[0], PACKAGE " " BITLBEE_VERSION);
    254254        } else if (g_strcasecmp(ctcp[0], "PING") == 0) {
    255255                irc_send_msg_f(iu, "NOTICE", iu->irc->user->nick, "\001%s %s\001",
  • lib/http_client.c

    rba52ac5 r537d9b9  
    9595        request = g_strdup_printf("GET %s HTTP/1.0\r\n"
    9696                                  "Host: %s\r\n"
    97                                   "User-Agent: BitlBee " BITLBEE_VERSION " " ARCH "/" CPU "\r\n"
     97                                  "User-Agent: BitlBee " BITLBEE_VERSION "\r\n"
    9898                                  "\r\n", url->file, url->host);
    9999
  • lib/misc.c

    rba52ac5 r537d9b9  
    549549}
    550550
    551 /* Word wrapping. Yes, I know this isn't UTF-8 clean. I'm willing to take the risk. */
    552551char *word_wrap(const char *msg, int line_len)
    553552{
     
    582581                }
    583582                if (i == 0) {
    584                         g_string_append_len(ret, msg, line_len);
     583                        const char *end;
     584                        size_t len;
     585
     586                        g_utf8_validate(msg, line_len, &end);
     587
     588                        len = (end != msg) ? end - msg : line_len;
     589
     590                        g_string_append_len(ret, msg, len);
    585591                        g_string_append_c(ret, '\n');
    586                         msg += line_len;
     592                        msg += len;
    587593                }
    588594        }
     
    787793        return string;
    788794}
     795
     796/* Returns a string that is exactly 'char_len' utf8 characters long (not bytes),
     797 * padded to the right with spaces or truncated with the 'ellipsis' parameter
     798 * if specified (can be NULL).
     799 * Returns a newly allocated string, or NULL on invalid parameters. */
     800char *str_pad_and_truncate(const char *string, long char_len, const char *ellipsis)
     801{
     802        size_t string_len = strlen(string);
     803        size_t ellipsis_len = (ellipsis) ? strlen(ellipsis) : 0;
     804        long orig_len = g_utf8_strlen(string, -1);
     805
     806        g_return_val_if_fail(char_len > ellipsis_len, NULL);
     807
     808        if (orig_len > char_len) {
     809                char *ret = g_malloc(string_len + 1);
     810                g_utf8_strncpy(ret, string, char_len - ellipsis_len);
     811                if (ellipsis) {
     812                        g_strlcat(ret, ellipsis, string_len);
     813                }
     814                return ret;
     815        } else if (orig_len < char_len) {
     816                return g_strdup_printf("%s%*s", string, (int) (char_len - orig_len), "");
     817        } else {
     818                return g_strdup(string);
     819        }
     820}
  • lib/misc.h

    rba52ac5 r537d9b9  
    151151G_MODULE_EXPORT gboolean parse_int64(char *string, int base, guint64 *number);
    152152G_MODULE_EXPORT char *str_reject_chars(char *string, const char *reject, char replacement);
     153G_MODULE_EXPORT char *str_pad_and_truncate(const char *string, long char_len, const char *ellipsis);
    153154
    154155#endif
  • lib/proxy.c

    rba52ac5 r537d9b9  
    8787}
    8888
     89/* calls phb->func safely by ensuring that the phb struct doesn't exist in the
     90 * case that proxy_disconnect() is called down there */
     91static gboolean phb_connected(struct PHB *phb, gint source)
     92{
     93        /* save func and data here */
     94        b_event_handler func = phb->func;
     95        gpointer data = phb->data;
     96
     97        /* free the struct so that it can't be freed by the callback */
     98        phb_free(phb, TRUE);
     99
     100        /* if any proxy_disconnect() call happens here, it will use the
     101         * fd (still open), look it up in the hash table, get NULL, and
     102         * proceed to close the fd and do nothing else */
     103        func(data, source, B_EV_IO_READ);
     104
     105        return FALSE;
     106}
     107
    89108static gboolean proxy_connected(gpointer data, gint source, b_input_condition cond)
    90109{
     
    125144                phb->proxy_func(phb->proxy_data, source, B_EV_IO_READ);
    126145        } else {
    127                 phb->func(phb->data, source, B_EV_IO_READ);
    128                 phb_free(phb, TRUE);
     146                phb_connected(phb, source);
    129147        }
    130148
     
    222240        if ((memcmp(HTTP_GOODSTRING, inputline, strlen(HTTP_GOODSTRING)) == 0) ||
    223241            (memcmp(HTTP_GOODSTRING2, inputline, strlen(HTTP_GOODSTRING2)) == 0)) {
    224                 phb->func(phb->data, source, B_EV_IO_READ);
    225                 return phb_free(phb, TRUE);
     242                return phb_connected(phb, source);
    226243        }
    227244
     
    295312        memset(packet, 0, sizeof(packet));
    296313        if (read(source, packet, 9) >= 4 && packet[1] == 90) {
    297                 phb->func(phb->data, source, B_EV_IO_READ);
    298                 return phb_free(phb, TRUE);
     314                return phb_connected(phb, source);
    299315        }
    300316
     
    384400        }
    385401
    386         phb->func(phb->data, source, B_EV_IO_READ);
    387         return phb_free(phb, TRUE);
     402        return phb_connected(phb, source);
    388403}
    389404
  • lib/sha1.c

    rba52ac5 r537d9b9  
    2424#define HMAC_BLOCK_SIZE 64
    2525
    26 /* BitlBee addition: */
    27 void sha1_hmac(const char *key_, size_t key_len, const char *payload, size_t payload_len, guint8 digest[SHA1_HASH_SIZE])
     26void b_hmac(GChecksumType checksum_type, const char *key_, size_t key_len,
     27            const char *payload, size_t payload_len, guint8 **digest)
    2828{
    29         sha1_state_t sha1;
    30         guint8 hash[SHA1_HASH_SIZE];
     29        GChecksum *checksum;
     30        size_t hash_len;
     31        guint8 *hash;
    3132        guint8 key[HMAC_BLOCK_SIZE + 1];
    3233        int i;
     34
     35        hash_len = g_checksum_type_get_length(checksum_type);
     36
     37        if (hash_len == (size_t) -1) {
     38                return;
     39        }
     40
     41        hash = g_malloc(hash_len);
    3342
    3443        if (key_len == 0) {
     
    4352        memset(key, 0, HMAC_BLOCK_SIZE + 1);
    4453        if (key_len > HMAC_BLOCK_SIZE) {
    45                 sha1_init(&sha1);
    46                 sha1_append(&sha1, (guint8 *) key_, key_len);
    47                 sha1_finish(&sha1, key);
     54                checksum = g_checksum_new(checksum_type);
     55                g_checksum_update(checksum, (guint8 *) key_, key_len);
     56                g_checksum_get_digest(checksum, key, &hash_len);
     57                g_checksum_free(checksum);
    4858        } else {
    4959                memcpy(key, key_, key_len);
     
    5161
    5262        /* Inner part: H(K XOR 0x36, text) */
    53         sha1_init(&sha1);
     63        checksum = g_checksum_new(checksum_type);
    5464        for (i = 0; i < HMAC_BLOCK_SIZE; i++) {
    5565                key[i] ^= 0x36;
    5666        }
    57         sha1_append(&sha1, key, HMAC_BLOCK_SIZE);
    58         sha1_append(&sha1, (const guint8 *) payload, payload_len);
    59         sha1_finish(&sha1, hash);
     67        g_checksum_update(checksum, key, HMAC_BLOCK_SIZE);
     68        g_checksum_update(checksum, (const guint8 *) payload, payload_len);
     69        g_checksum_get_digest(checksum, hash, &hash_len);
     70        g_checksum_free(checksum);
    6071
    6172        /* Final result: H(K XOR 0x5C, inner stuff) */
    62         sha1_init(&sha1);
     73        checksum = g_checksum_new(checksum_type);
    6374        for (i = 0; i < HMAC_BLOCK_SIZE; i++) {
    6475                key[i] ^= 0x36 ^ 0x5c;
    6576        }
    66         sha1_append(&sha1, key, HMAC_BLOCK_SIZE);
    67         sha1_append(&sha1, hash, SHA1_HASH_SIZE);
    68         sha1_finish(&sha1, digest);
     77        g_checksum_update(checksum, key, HMAC_BLOCK_SIZE);
     78        g_checksum_update(checksum, hash, hash_len);
     79        g_checksum_get_digest(checksum, *digest, &hash_len);
     80        g_checksum_free(checksum);
     81
     82        g_free(hash);
    6983}
     84
     85void sha1_hmac(const char *key_, size_t key_len, const char *payload, size_t payload_len, guint8 digest[SHA1_HASH_SIZE])
     86{
     87        b_hmac(G_CHECKSUM_SHA1, key_, key_len, payload, payload_len, &digest);
     88}
     89
    7090
    7191/* I think this follows the scheme described on:
  • lib/ssl_gnutls.c

    rba52ac5 r537d9b9  
    4242#include <limits.h>
    4343
    44 #if defined(ULONG_MAX) && ULONG_MAX > 4294967295UL
    45 #define GNUTLS_STUPID_CAST (long)
    46 #else
    47 #define GNUTLS_STUPID_CAST (int)
    48 #endif
    49 
    5044#define SSLDEBUG 0
    5145
     
    335329
    336330        sock_make_nonblocking(conn->fd);
    337         gnutls_transport_set_ptr(conn->session, (gnutls_transport_ptr_t) GNUTLS_STUPID_CAST conn->fd);
     331        gnutls_transport_set_ptr(conn->session, (gnutls_transport_ptr_t) (long) conn->fd);
    338332
    339333        ssl_cache_resume(conn);
  • lib/ssl_openssl.c

    rba52ac5 r537d9b9  
    6767        SSL_library_init();
    6868
    69         meth = TLSv1_client_method();
     69        meth = SSLv23_client_method();
    7070        ssl_ctx = SSL_CTX_new(meth);
     71        SSL_CTX_set_options(ssl_ctx, SSL_OP_NO_SSLv2 | SSL_OP_NO_SSLv3);
    7172
    7273        initialized = TRUE;
  • otr.c

    rba52ac5 r537d9b9  
    268268}
    269269
     270#ifndef OTR_BI
     271struct plugin_info *init_plugin_info(void)
     272{
     273        static struct plugin_info info = {
     274                BITLBEE_ABI_VERSION_CODE,
     275                "otr",
     276                BITLBEE_VERSION,
     277                "Off-the-Record communication",
     278                NULL,
     279                NULL
     280        };
     281
     282        return &info;
     283}
     284#endif
     285
    270286gboolean otr_irc_new(irc_t *irc)
    271287{
     
    413429
    414430        /* don't do OTR on certain (not classic IM) protocols, e.g. twitter */
    415         if (a->prpl->options & OPT_NOOTR) {
     431        if (a->prpl->options & PRPL_OPT_NOOTR) {
    416432                return 0;
    417433        }
     
    441457
    442458        /* don't do OTR on certain (not classic IM) protocols, e.g. twitter */
    443         if (ic->acc->prpl->options & OPT_NOOTR ||
     459        if (ic->acc->prpl->options & PRPL_OPT_NOOTR ||
    444460            iu->bu->flags & BEE_USER_NOOTR) {
    445461                return msg;
     
    13841400void display_otr_message(void *opdata, ConnContext *ctx, const char *fmt, ...)
    13851401{
     1402        char *msg_, *msg;
    13861403        struct im_connection *ic =
    13871404                check_imc(opdata, ctx->accountname, ctx->protocol);
     
    13911408
    13921409        va_start(va, fmt);
    1393         char *msg = g_strdup_vprintf(fmt, va);
     1410        msg_ = g_strdup_vprintf(fmt, va);
    13941411        va_end(va);
     1412
     1413        msg = word_wrap(msg_, IRC_WORD_WRAP);
    13951414
    13961415        if (u) {
     
    14011420        }
    14021421
     1422        g_free(msg_);
    14031423        g_free(msg);
    14041424}
  • protocols/account.c

    rba52ac5 r537d9b9  
    6363
    6464        s = set_add(&a->set, "password", NULL, set_eval_account, a);
    65         s->flags |= SET_NOSAVE | SET_NULL_OK | SET_PASSWORD;
     65        s->flags |= SET_NOSAVE | SET_NULL_OK | SET_PASSWORD | ACC_SET_LOCKABLE;
    6666
    6767        s = set_add(&a->set, "tag", NULL, set_eval_account, a);
     
    6969
    7070        s = set_add(&a->set, "username", NULL, set_eval_account, a);
    71         s->flags |= SET_NOSAVE | ACC_SET_OFFLINE_ONLY;
     71        s->flags |= SET_NOSAVE | ACC_SET_OFFLINE_ONLY | ACC_SET_LOCKABLE;
    7272        set_setstr(&a->set, "username", user);
    7373
  • protocols/account.h

    rba52ac5 r537d9b9  
    6161        ACC_SET_OFFLINE_ONLY = 0x02,    /* Allow changes only if the acct is offline. */
    6262        ACC_SET_ONLINE_ONLY = 0x04,     /* Allow changes only if the acct is online. */
     63        ACC_SET_LOCKABLE = 0x08         /* Setting cannot be changed if the account is locked down */
    6364} account_set_flag_t;
    6465
     
    6869        ACC_FLAG_HANDLE_DOMAINS = 0x04, /* Contact handles need a domain portion. */
    6970        ACC_FLAG_LOCAL_CONTACTS = 0x08, /* Contact list is local. */
     71        ACC_FLAG_LOCKED = 0x10,         /* Account is locked (cannot be deleted, certain settings can't changed) */
    7072} account_flag_t;
    7173
  • protocols/bee.h

    rba52ac5 r537d9b9  
    8484} bee_user_t;
    8585
     86typedef struct bee_chat_info {
     87        char *title;
     88        char *topic;
     89} bee_chat_info_t;
     90
    8691/* This one's mostly used so save space and make it easier (cheaper) to
    8792   compare groups of contacts, etc. */
     
    131136
    132137        void (*log)(bee_t *bee, const char *tag, const char *msg);
     138        gboolean (*user_nick_change)(bee_t *bee, bee_user_t *bu, const char *hint);
    133139} bee_ui_funcs_t;
    134140
     
    184190G_MODULE_EXPORT void imcb_chat_invite(struct im_connection *ic, const char *name, const char *who, const char *msg);
    185191
     192G_GNUC_DEPRECATED G_MODULE_EXPORT void bee_chat_list_finish(struct im_connection *ic);
     193G_MODULE_EXPORT void imcb_chat_list_finish(struct im_connection *ic);
     194G_MODULE_EXPORT void imcb_chat_list_free(struct im_connection *ic);
     195
    186196#endif /* __BEE_H__ */
  • protocols/bee_chat.c

    rba52ac5 r537d9b9  
    274274        }
    275275}
     276
     277void imcb_chat_list_finish(struct im_connection *ic)
     278{
     279        cmd_chat_list_finish(ic);
     280}
     281
     282void bee_chat_list_finish(struct im_connection *ic)
     283{
     284        imcb_log(ic, "Warning: using deprecated bee_chat_list_finish. This will be removed in the stable release.");
     285        imcb_chat_list_finish(ic);
     286}
     287
     288void imcb_chat_list_free(struct im_connection *ic)
     289{
     290        bee_chat_info_t *ci;
     291        GSList *l = ic->chatlist;
     292
     293        while (l) {
     294                ci = l->data;
     295                l = g_slist_delete_link(l, l);
     296
     297                g_free(ci->title);
     298                g_free(ci->topic);
     299                g_free(ci);
     300        }
     301
     302        ic->chatlist = NULL;
     303}
  • protocols/bee_ft.c

    rba52ac5 r537d9b9  
    3131        bee_user_t *bu = bee_user_by_handle(bee, ic, handle);
    3232
    33         if (bee->ui->ft_in_start) {
     33        if (bee->ui->ft_in_start && bu) {
    3434                return bee->ui->ft_in_start(bee, bu, file_name, file_size);
    3535        } else {
  • protocols/jabber/conference.c

    rba52ac5 r537d9b9  
    2828static xt_status jabber_chat_self_message(struct im_connection *ic, struct xt_node *node, struct xt_node *orig);
    2929
    30 struct groupchat *jabber_chat_join(struct im_connection *ic, const char *room, const char *nick, const char *password)
     30struct groupchat *jabber_chat_join(struct im_connection *ic, const char *room, const char *nick, const char *password,
     31                                   gboolean always_use_nicks)
    3132{
    3233        struct jabber_chat *jc;
     
    5758                g_free(jc);
    5859                return NULL;
     60        }
     61
     62        if (always_use_nicks) {
     63                jc->flags = JCFLAG_ALWAYS_USE_NICKS;
    5964        }
    6065
     
    95100        g_free(cserv);
    96101
    97         c = jabber_chat_join(ic, rjid, jd->username, NULL);
     102        c = jabber_chat_join(ic, rjid, jd->username, NULL, FALSE);
    98103        g_free(rjid);
    99104        if (c == NULL) {
     
    244249}
    245250
     251static int jabber_chat_has_other_resources(struct im_connection *ic, struct jabber_buddy *bud)
     252{
     253        struct jabber_buddy *cur;
     254
     255        for (cur = jabber_buddy_by_jid(ic, bud->bare_jid, GET_BUDDY_FIRST); cur; cur = cur->next) {
     256                if (cur != bud && jabber_compare_jid(cur->ext_jid, bud->ext_jid)) {
     257                        return TRUE;
     258                }
     259        }
     260       
     261        return FALSE;
     262}
     263
    246264/* Not really the same syntax as the normal pkt_ functions, but this isn't
    247265   called by the xmltree parser directly and this way I can add some extra
     
    328346                        *s = 0; /* Should NEVER be NULL, but who knows... */
    329347                }
     348
     349                if (bud != jc->me && (jc->flags & JCFLAG_ALWAYS_USE_NICKS) && !(bud->flags & JBFLAG_IS_ANONYMOUS)) {
     350                        imcb_buddy_nick_change(ic, bud->ext_jid, bud->resource);
     351                }
     352
    330353                imcb_chat_add_buddy(chat, bud->ext_jid);
    331354                if (s) {
     
    333356                }
    334357        } else if (type) { /* type can only be NULL or "unavailable" in this function */
    335                 if ((bud->flags & JBFLAG_IS_CHATROOM) && bud->ext_jid) {
     358                if ((bud->flags & JBFLAG_IS_CHATROOM) && bud->ext_jid && !jabber_chat_has_other_resources(ic, bud)) {
    336359                        char *reason = NULL;
    337360                        char *status = NULL;
     
    443466
    444467        if (subject && chat) {
    445                 char *subject_text = subject->text_len > 0 ? subject->text : "";
     468                char empty[1] = "";
     469                char *subject_text = subject->text_len > 0 ? subject->text : empty;
    446470                if (g_strcmp0(chat->topic, subject_text) != 0) {
    447471                        bare_jid = (bud) ? jabber_get_bare_jid(bud->ext_jid) : NULL;
     
    479503                bare_jid = jabber_get_bare_jid(bud->ext_jid ? bud->ext_jid : bud->full_jid);
    480504                final_from = bare_jid;
    481                 flags = (bud == jc->me) ? OPT_SELFMESSAGE : 0;
     505                if (bud == jc->me || (g_strcasecmp(final_from, ic->acc->user) == 0)) {
     506                        flags = OPT_SELFMESSAGE;
     507                }
    482508        } else {
    483509                final_from = nick;
  • protocols/jabber/iq.c

    rba52ac5 r537d9b9  
    6767                        xt_add_child(reply, xt_new_node("name", set_getstr(&ic->acc->set, "user_agent"), NULL));
    6868                        xt_add_child(reply, xt_new_node("version", BITLBEE_VERSION, NULL));
    69                         xt_add_child(reply, xt_new_node("os", ARCH, NULL));
    7069                } else if (strcmp(s, XMLNS_TIME_OLD) == 0) {
    7170                        time_t time_ep;
     
    234233                imcb_log(ic, "Warning: Received incomplete IQ packet while authenticating");
    235234                imc_logout(ic, FALSE);
    236                 return XT_HANDLED;
     235                return XT_ABORT;
    237236        }
    238237
     
    287286                imcb_log(ic, "Warning: Received incomplete IQ packet while authenticating");
    288287                imc_logout(ic, FALSE);
    289                 return XT_HANDLED;
     288                return XT_ABORT;
    290289        }
    291290
     
    10601059        return XT_HANDLED;
    10611060}
     1061
     1062xt_status jabber_iq_disco_muc_response(struct im_connection *ic, struct xt_node *node, struct xt_node *orig);
     1063
     1064int jabber_iq_disco_muc(struct im_connection *ic, const char *muc_server)
     1065{
     1066        struct xt_node *node;
     1067        int st;
     1068
     1069        node = xt_new_node("query", NULL, NULL);
     1070        xt_add_attr(node, "xmlns", XMLNS_DISCO_ITEMS);
     1071        node = jabber_make_packet("iq", "get", (char *) muc_server, node);
     1072
     1073        jabber_cache_add(ic, node, jabber_iq_disco_muc_response);
     1074        st = jabber_write_packet(ic, node);
     1075
     1076        return st;
     1077}
     1078
     1079xt_status jabber_iq_disco_muc_response(struct im_connection *ic, struct xt_node *node, struct xt_node *orig)
     1080{
     1081        struct xt_node *query, *c;
     1082        struct jabber_error *err;
     1083        GSList *rooms =  NULL;
     1084
     1085        if ((err = jabber_error_parse(xt_find_node(node->children, "error"), XMLNS_STANZA_ERROR))) {
     1086                imcb_error(ic, "The server replied with an error: %s%s%s",
     1087                           err->code, err->text ? ": " : "", err->text ? err->text : "");
     1088                jabber_error_free(err);
     1089                return XT_HANDLED;
     1090        }
     1091
     1092        if (!(query = xt_find_node(node->children, "query"))) {
     1093                imcb_error(ic, "Received incomplete MUC list reply");
     1094                return XT_HANDLED;
     1095        }
     1096
     1097        c = query->children;
     1098        while ((c = xt_find_node(c, "item"))) {
     1099                char *jid = xt_find_attr(c, "jid");
     1100
     1101                if (!jid || !strchr(jid, '@')) {
     1102                        c = c->next;
     1103                        continue;
     1104                }
     1105
     1106                bee_chat_info_t *ci = g_new(bee_chat_info_t, 1);
     1107                ci->title = g_strdup(xt_find_attr(c, "jid"));
     1108                ci->topic = g_strdup(xt_find_attr(c, "name"));
     1109                rooms = g_slist_prepend(rooms, ci);
     1110
     1111                c = c->next;
     1112        }
     1113
     1114        imcb_chat_list_free(ic);
     1115        ic->chatlist = g_slist_reverse(rooms);
     1116        imcb_chat_list_finish(ic);
     1117
     1118        return XT_HANDLED;
     1119}
  • protocols/jabber/jabber.c

    rba52ac5 r537d9b9  
    8282        s->flags |= SET_NOSAVE | ACC_SET_OFFLINE_ONLY | SET_NULL_OK;
    8383
     84        set_add(&acc->set, "oauth", "false", set_eval_oauth, acc);
     85
    8486        if (strcmp(acc->prpl->name, "hipchat") == 0) {
    8587                set_setstr(&acc->set, "server", "chat.hipchat.com");
    8688        } else {
    87                 set_add(&acc->set, "oauth", "false", set_eval_oauth, acc);
    88 
    8989                /* this reuses set_eval_oauth, which clears the password */
    9090                set_add(&acc->set, "anonymous", "false", set_eval_oauth, acc);
     
    321321        struct jabber_data *jd = ic->proto_data;
    322322
     323        imcb_chat_list_free(ic);
     324
    323325        while (jd->filetransfers) {
    324326                imcb_file_canceled(ic, (( struct jabber_transfer *) jd->filetransfers->data)->ft, "Logging out");
     
    397399        if (g_strcasecmp(who, JABBER_OAUTH_HANDLE) == 0 &&
    398400            !(jd->flags & OPT_LOGGED_IN) && jd->fd == -1) {
    399                 if (sasl_oauth2_get_refresh_token(ic, message)) {
     401
     402                if (jd->flags & JFLAG_HIPCHAT) {
     403                        sasl_oauth2_got_token(ic, message, NULL, NULL);
     404                        return 1;
     405                } else if (sasl_oauth2_get_refresh_token(ic, message)) {
    400406                        return 1;
    401407                } else {
     
    577583        } else {
    578584                /* jabber_chat_join without the underscore is the conference.c one */
    579                 return jabber_chat_join(ic, room, final_nick, set_getstr(sets, "password"));
     585                return jabber_chat_join(ic, room, final_nick, set_getstr(sets, "password"),
     586                                        set_getbool(sets, "always_use_nicks"));
    580587        }
    581588
     
    586593{
    587594        return jabber_chat_with(ic, who);
     595}
     596
     597static void jabber_chat_list_(struct im_connection *ic, const char *server)
     598{
     599        struct jabber_data *jd = ic->proto_data;
     600
     601        if (server && *server) {
     602                jabber_iq_disco_muc(ic, server);
     603        } else if (jd->muc_host && *jd->muc_host) {
     604                jabber_iq_disco_muc(ic, jd->muc_host);
     605        } else {
     606                /* throw an error here, don't query conference.[server] directly.
     607                 * for things like jabber.org it gets you 18000 results of garbage */
     608                imcb_error(ic, "Please specify a server name such as `conference.%s'", jd->server);
     609        }
    588610}
    589611
     
    686708void jabber_chat_add_settings(account_t *acc, set_t **head)
    687709{
     710        set_add(head, "always_use_nicks", "false", set_eval_bool, NULL);
     711
    688712        /* Meh. Stupid room passwords. Not trying to obfuscate/hide
    689713           them from the user for now. */
     
    693717void jabber_chat_free_settings(account_t *acc, set_t **head)
    694718{
     719        set_del(head, "always_use_nicks");
     720
    695721        set_del(head, "password");
    696722}
     
    759785        ret->chat_join = jabber_chat_join_;
    760786        ret->chat_with = jabber_chat_with_;
     787        ret->chat_list = jabber_chat_list_;
    761788        ret->chat_add_settings = jabber_chat_add_settings;
    762789        ret->chat_free_settings = jabber_chat_free_settings;
  • protocols/jabber/jabber.h

    rba52ac5 r537d9b9  
    7575        JCFLAG_MESSAGE_SENT = 1,        /* Set this after sending the first message, so
    7676                                           we can detect echoes/backlogs. */
     77        JCFLAG_ALWAYS_USE_NICKS = 2,
    7778} jabber_chat_flags_t;
    7879
     
    262263void jabber_iq_version_send(struct im_connection *ic, struct jabber_buddy *bud, void *data);
    263264int jabber_iq_disco_server(struct im_connection *ic);
     265int jabber_iq_disco_muc(struct im_connection *ic, const char *muc_server);
    264266
    265267/* si.c */
     
    339341int sasl_oauth2_get_refresh_token(struct im_connection *ic, const char *msg);
    340342int sasl_oauth2_refresh(struct im_connection *ic, const char *refresh_token);
     343void sasl_oauth2_got_token(gpointer data, const char *access_token, const char *refresh_token, const char *error);
    341344
    342345extern const struct oauth2_service oauth2_service_google;
    343346
    344347/* conference.c */
    345 struct groupchat *jabber_chat_join(struct im_connection *ic, const char *room, const char *nick, const char *password);
     348struct groupchat *jabber_chat_join(struct im_connection *ic, const char *room, const char *nick, const char *password,
     349                                   gboolean always_use_nicks);
    346350struct groupchat *jabber_chat_with(struct im_connection *ic, char *who);
    347351struct groupchat *jabber_chat_by_jid(struct im_connection *ic, const char *name);
  • protocols/jabber/jabber_util.c

    rba52ac5 r537d9b9  
    315315        int i;
    316316
     317        if (!jid1 || !jid2) {
     318                return FALSE;
     319        }
     320
    317321        for (i = 0;; i++) {
    318322                if (jid1[i] == '\0' || jid1[i] == '/' || jid2[i] == '\0' || jid2[i] == '/') {
  • protocols/jabber/message.c

    rba52ac5 r537d9b9  
    2727{
    2828        struct im_connection *ic = data;
    29         struct jabber_data *jd = ic->proto_data;
    3029        char *from = xt_find_attr(node, carbons_sent ? "to" : "from");
    3130        char *type = xt_find_attr(node, "type");
     
    3837        if (!from) {
    3938                return XT_HANDLED; /* Consider this packet corrupted. */
    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                 }
    5139        }
    5240
  • protocols/jabber/presence.c

    rba52ac5 r537d9b9  
    222222        cap = xt_new_node("c", NULL, NULL);
    223223        xt_add_attr(cap, "xmlns", XMLNS_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         }
     224        xt_add_attr(cap, "node", "http://bitlbee.org/xmpp/caps");
    231225        xt_add_attr(cap, "ver", BITLBEE_VERSION);   /* The XEP wants this hashed, but nobody's doing that. */
    232226        xt_add_child(node, cap);
  • protocols/jabber/s5bytestream.c

    rba52ac5 r537d9b9  
    928928                proxy = next;
    929929        }
     930
     931        g_free(proxysetting);
    930932}
    931933
  • protocols/jabber/sasl.c

    rba52ac5 r537d9b9  
    3939};
    4040
     41/* """"""""""""""""""""""""""""""oauth"""""""""""""""""""""""""""""" */
     42#define HIPCHAT_SO_CALLED_OAUTH_URL "https://hipchat.com/account/api"
     43
    4144xt_status sasl_pkt_mechanisms(struct xt_node *node, gpointer data)
    4245{
     
    4548        struct xt_node *c, *reply;
    4649        char *s;
    47         int sup_plain = 0, sup_digest = 0, sup_gtalk = 0, sup_anonymous = 0;
     50        int sup_plain = 0, sup_digest = 0, sup_gtalk = 0, sup_anonymous = 0, sup_hipchat_oauth = 0;
    4851        int want_oauth = FALSE, want_hipchat = FALSE, want_anonymous = FALSE;
    4952        GString *mechs;
     
    8083                } else if (c->text && g_strcasecmp(c->text, "X-OAUTH2") == 0) {
    8184                        sup_gtalk = 1;
     85                } else if (c->text && g_strcasecmp(c->text, "X-HIPCHAT-OAUTH2") == 0) {
     86                        sup_hipchat_oauth = 1;
    8287                }
    8388
     
    9095
    9196        if (!want_oauth && !sup_plain && !sup_digest) {
    92                 if (!sup_gtalk) {
     97                if (sup_gtalk || sup_hipchat_oauth) {
    9398                        imcb_error(ic, "This server requires OAuth "
    9499                                   "(supported schemes:%s)", mechs->str);
     
    110115        }
    111116
    112         if (sup_gtalk && want_oauth) {
    113                 int len;
    114 
    115                 /* X-OAUTH2 is, not *the* standard OAuth2 SASL/XMPP implementation.
    116                    It's currently used by GTalk and vaguely documented on
    117                    http://code.google.com/apis/cloudprint/docs/rawxmpp.html . */
    118                 xt_add_attr(reply, "mechanism", "X-OAUTH2");
    119 
    120                 len = strlen(jd->username) + strlen(jd->oauth2_access_token) + 2;
    121                 s = g_malloc(len + 1);
    122                 s[0] = 0;
    123                 strcpy(s + 1, jd->username);
    124                 strcpy(s + 2 + strlen(jd->username), jd->oauth2_access_token);
    125                 reply->text = base64_encode((unsigned char *) s, len);
     117        if ((sup_gtalk || sup_hipchat_oauth) && want_oauth) {
     118                GString *gs;
     119
     120                gs = g_string_sized_new(128);
     121
     122                g_string_append_c(gs, '\0');
     123
     124                if (sup_gtalk) {
     125                        /* X-OAUTH2 is not *the* standard OAuth2 SASL/XMPP implementation.
     126                           It's currently used by GTalk and vaguely documented on
     127                           http://code.google.com/apis/cloudprint/docs/rawxmpp.html */
     128                        xt_add_attr(reply, "mechanism", "X-OAUTH2");
     129
     130                        g_string_append(gs, jd->username);
     131                        g_string_append_c(gs, '\0');
     132                        g_string_append(gs, jd->oauth2_access_token);
     133                } else if (sup_hipchat_oauth) {
     134                        /* Hipchat's variant, not standard either, is documented here:
     135                           https://docs.atlassian.com/hipchat.xmpp/latest/xmpp/auth.html */
     136                        xt_add_attr(reply, "mechanism", "oauth2");
     137
     138                        g_string_append(gs, jd->oauth2_access_token);
     139                        g_string_append_c(gs, '\0');
     140                        g_string_append(gs, set_getstr(&ic->acc->set, "resource"));
     141                }
     142
     143                reply->text = base64_encode((unsigned char *) gs->str, gs->len);
    126144                reply->text_len = strlen(reply->text);
    127                 g_free(s);
     145                g_string_free(gs, TRUE);
     146
    128147        } else if (want_oauth) {
    129148                imcb_error(ic, "OAuth requested, but not supported by server");
     
    149168                /* The rest will be done later, when we receive a <challenge/>. */
    150169        } else if (sup_plain) {
    151                 int len;
    152170                GString *gs;
    153171                char *username;
     
    174192                }
    175193
    176                 len = gs->len;
    177                 s = g_string_free(gs, FALSE);
    178 
    179                 reply->text = base64_encode((unsigned char *) s, len);
     194                reply->text = base64_encode((unsigned char *) gs->str, gs->len);
    180195                reply->text_len = strlen(reply->text);
    181                 g_free(s);
     196                g_string_free(gs, TRUE);
    182197        }
    183198
     
    428443{
    429444        struct jabber_data *jd = ic->proto_data;
    430         char *msg, *url;
    431445
    432446        imcb_log(ic, "Starting OAuth authentication");
     
    434448        /* Temporary contact, just used to receive the OAuth response. */
    435449        imcb_add_buddy(ic, JABBER_OAUTH_HANDLE, NULL);
    436         url = oauth2_url(jd->oauth2_service);
    437         msg = g_strdup_printf("Open this URL in your browser to authenticate: %s", url);
    438         imcb_buddy_msg(ic, JABBER_OAUTH_HANDLE, msg, 0, 0);
     450
     451        if (jd->flags & JFLAG_HIPCHAT) {
     452                imcb_buddy_msg(ic, JABBER_OAUTH_HANDLE,
     453                        "Open this URL and generate a token with 'View Group' and 'Send Message' scopes: "
     454                        HIPCHAT_SO_CALLED_OAUTH_URL, 0, 0);
     455        } else {
     456                char *msg, *url;
     457
     458                url = oauth2_url(jd->oauth2_service);
     459                msg = g_strdup_printf("Open this URL in your browser to authenticate: %s", url);
     460                imcb_buddy_msg(ic, JABBER_OAUTH_HANDLE, msg, 0, 0);
     461
     462                g_free(msg);
     463                g_free(url);
     464        }
    439465        imcb_buddy_msg(ic, JABBER_OAUTH_HANDLE, "Respond to this message with the returned "
    440466                       "authorization token.", 0, 0);
    441467
    442         g_free(msg);
    443         g_free(url);
    444468}
    445469
     
    453477        return FALSE;
    454478}
    455 
    456 static void sasl_oauth2_got_token(gpointer data, const char *access_token, const char *refresh_token,
    457                                   const char *error);
    458479
    459480int sasl_oauth2_get_refresh_token(struct im_connection *ic, const char *msg)
     
    486507}
    487508
    488 static void sasl_oauth2_got_token(gpointer data, const char *access_token, const char *refresh_token, const char *error)
     509void sasl_oauth2_got_token(gpointer data, const char *access_token, const char *refresh_token, const char *error)
    489510{
    490511        struct im_connection *ic = data;
  • protocols/jabber/si.c

    rba52ac5 r537d9b9  
    393393        struct jabber_transfer *tf = NULL;
    394394        struct jabber_data *jd = ic->proto_data;
     395        struct jabber_error *err;
    395396
    396397        if (!(tgt_jid = xt_find_attr(node, "from")) ||
    397             !(ini_jid = xt_find_attr(node, "to"))) {
     398            !(ini_jid = xt_find_attr(node, "to")) ||
     399            !(iq_id   = xt_find_attr(node, "id"))) {
    398400                imcb_log(ic, "Invalid SI response from=%s to=%s", tgt_jid, ini_jid);
     401                return XT_HANDLED;
     402        }
     403
     404        /* Let's see if we can find out what this bytestream should be for... */
     405
     406        for (tflist = jd->filetransfers; tflist; tflist = g_slist_next(tflist)) {
     407                struct jabber_transfer *tft = tflist->data;
     408                if ((strcmp(tft->iq_id, iq_id) == 0)) {
     409                        tf = tft;
     410                        break;
     411                }
     412        }
     413
     414        if (!tf) {
     415                imcb_log(ic, "WARNING: Received bytestream request from %s that doesn't match an SI request", ini_jid);
     416                return XT_HANDLED;
     417        }
     418
     419        err = jabber_error_parse(xt_find_node(node->children, "error"), XMLNS_STANZA_ERROR);
     420
     421        if (err) {
     422                if (g_strcmp0(err->code, "forbidden") == 0) {
     423                        imcb_log(ic, "File %s: %s rejected the transfer", tf->ft->file_name, tgt_jid);
     424                } else {
     425                        imcb_log(ic, "Error: Stream initiation request failed: %s (%s)", err->code, err->text);
     426                }
     427                imcb_file_canceled(ic, tf->ft, "Stream initiation request failed");
     428                jabber_error_free(err);
    399429                return XT_HANDLED;
    400430        }
     
    409439         *                                      <value>
    410440         */
    411         if (!(tgt_jid = xt_find_attr(node, "from")) ||
    412             !(ini_jid = xt_find_attr(node, "to")) ||
    413             !(iq_id   = xt_find_attr(node, "id")) ||
    414             !(c = xt_find_node(node->children, "si")) ||
     441        if (!(c = xt_find_node(node->children, "si")) ||
    415442            !(cmp = xt_find_attr(c, "xmlns")) ||
    416443            !(strcmp(cmp, XMLNS_SI) == 0) ||
     
    439466        }
    440467
    441         /* Let's see if we can find out what this bytestream should be for... */
    442 
    443         for (tflist = jd->filetransfers; tflist; tflist = g_slist_next(tflist)) {
    444                 struct jabber_transfer *tft = tflist->data;
    445                 if ((strcmp(tft->iq_id, iq_id) == 0)) {
    446                         tf = tft;
    447                         break;
    448                 }
    449         }
    450 
    451         if (!tf) {
    452                 imcb_log(ic, "WARNING: Received bytestream request from %s that doesn't match an SI request", ini_jid);
    453                 return XT_HANDLED;
    454         }
    455 
    456468        tf->ini_jid = g_strdup(ini_jid);
    457469        tf->tgt_jid = g_strdup(tgt_jid);
  • protocols/nogaim.c

    rba52ac5 r537d9b9  
    4040
    4141#ifdef WITH_PLUGINS
     42GList *plugins = NULL;
     43
     44static gint pluginscmp(gconstpointer a, gconstpointer b, gpointer data)
     45{
     46        const struct plugin_info *ia = a;
     47        const struct plugin_info *ib = b;
     48
     49        return g_strcasecmp(ia->name, ib->name);
     50}
     51
    4252gboolean load_plugin(char *path)
    4353{
     54        GList *l;
     55        struct plugin_info *i;
     56        struct plugin_info *info;
     57        struct plugin_info * (*info_function) (void) = NULL;
    4458        void (*init_function) (void);
    4559
    4660        GModule *mod = g_module_open(path, G_MODULE_BIND_LAZY);
     61        gboolean loaded = FALSE;
    4762
    4863        if (!mod) {
    49                 log_message(LOGLVL_ERROR, "Can't find `%s', not loading (%s)\n", path, g_module_error());
     64                log_message(LOGLVL_ERROR, "Error loading plugin `%s': %s\n", path, g_module_error());
    5065                return FALSE;
     66        }
     67
     68        if (g_module_symbol(mod, "init_plugin_info", (gpointer *) &info_function)) {
     69                info = info_function();
     70
     71                if (info->abiver != BITLBEE_ABI_VERSION_CODE) {
     72                        log_message(LOGLVL_ERROR,
     73                                    "`%s' uses ABI %u but %u is required\n",
     74                                    path, info->abiver,
     75                                    BITLBEE_ABI_VERSION_CODE);
     76                        g_module_close(mod);
     77                        return FALSE;
     78                }
     79
     80                if (!info->name || !info->version) {
     81                        log_message(LOGLVL_ERROR,
     82                                    "Name or version missing from the "
     83                                    "plugin info in `%s'\n", path);
     84                        g_module_close(mod);
     85                        return FALSE;
     86                }
     87
     88                for (l = plugins; l; l = l->next) {
     89                        i = l->data;
     90
     91                        if (g_strcasecmp(i->name, info->name) == 0) {
     92                                loaded = TRUE;
     93                                break;
     94                        }
     95                }
     96
     97                if (loaded) {
     98                        log_message(LOGLVL_WARNING,
     99                                    "%s plugin already loaded\n",
     100                                    info->name);
     101                        g_module_close(mod);
     102                        return FALSE;
     103                }
     104        } else {
     105                log_message(LOGLVL_WARNING, "Can't find function `init_plugin_info' in `%s'\n", path);
    51106        }
    52107
    53108        if (!g_module_symbol(mod, "init_plugin", (gpointer *) &init_function)) {
    54109                log_message(LOGLVL_WARNING, "Can't find function `init_plugin' in `%s'\n", path);
     110                g_module_close(mod);
    55111                return FALSE;
    56112        }
    57113
     114        if (info_function) {
     115                plugins = g_list_insert_sorted_with_data(plugins, info,
     116                                                         pluginscmp, NULL);
     117        }
     118
    58119        init_function();
    59 
    60120        return TRUE;
    61121}
     
    73133
    74134                while ((entry = g_dir_read_name(dir))) {
     135                        if (!g_str_has_suffix(entry, "." G_MODULE_SUFFIX)) {
     136                                continue;
     137                        }
     138
    75139                        path = g_build_filename(global.conf->plugindir, entry, NULL);
    76140                        if (!path) {
     
    86150                g_dir_close(dir);
    87151        }
     152}
     153
     154GList *get_plugins()
     155{
     156        return plugins;
    88157}
    89158#endif
     
    168237        load_plugins();
    169238#endif
     239}
     240
     241GList *get_protocols()
     242{
     243        return protocols;
     244}
     245
     246GList *get_protocols_disabled()
     247{
     248        return disabled_protocols;
    170249}
    171250
     
    495574}
    496575
    497 /* Mainly meant for ICQ (and now also for Jabber conferences) to allow IM
    498    modules to suggest a nickname for a handle. */
    499 void imcb_buddy_nick_hint(struct im_connection *ic, const char *handle, const char *nick)
     576/* Implements either imcb_buddy_nick_hint() or imcb_buddy_nick_change() depending on the value of 'change' */
     577static void buddy_nick_hint_or_change(struct im_connection *ic, const char *handle, const char *nick, gboolean change)
    500578{
    501579        bee_t *bee = ic->bee;
     
    509587        bu->nick = g_strdup(nick);
    510588
    511         if (bee->ui->user_nick_hint) {
     589        if (change && bee->ui->user_nick_change) {
     590                bee->ui->user_nick_change(bee, bu, nick);
     591        } else if (!change && bee->ui->user_nick_hint) {
    512592                bee->ui->user_nick_hint(bee, bu, nick);
    513593        }
     594}
     595
     596/* Soft variant, for newly created users. Does nothing if it's already online */
     597void imcb_buddy_nick_hint(struct im_connection *ic, const char *handle, const char *nick)
     598{
     599        buddy_nick_hint_or_change(ic, handle, nick, FALSE);
     600}
     601
     602/* Hard variant, always changes the nick */
     603void imcb_buddy_nick_change(struct im_connection *ic, const char *handle, const char *nick)
     604{
     605        buddy_nick_hint_or_change(ic, handle, nick, TRUE);
    514606}
    515607
     
    667759        if (away && *away) {
    668760                GList *m = ic->acc->prpl->away_states(ic);
     761                if (m == NULL) {
     762                        return 0;
     763                }
    669764                msg = ic->acc->flags & ACC_FLAG_AWAY_MESSAGE ? away : NULL;
    670765                away = imc_away_state_find(m, away, &msg) ? :
  • protocols/nogaim.h

    rba52ac5 r537d9b9  
    102102
    103103        GSList *groupchats;
     104        GSList *chatlist;
    104105};
    105106
     
    144145        char *description;
    145146};
     147
     148/* This enum takes a few things from libpurple and a few things from old OPT_ flags.
     149 * The only flag that was used before this struct was PRPL_OPT_NOOTR.
     150 *
     151 * The libpurple ones only use the same values as the PurpleProtocolOptions
     152 * enum for convenience, but there's no promise of direct compatibility with
     153 * those values. As of libpurple 2.8.0 they use up to 0x800 (1 << 11), which is
     154 * a nice coincidence.
     155 */
     156typedef enum {
     157        /* The protocol doesn't use passwords
     158         * Mirrors libpurple's OPT_PROTO_NO_PASSWORD */
     159        PRPL_OPT_NO_PASSWORD = 1 << 4,
     160
     161        /* The protocol doesn't require passwords, but may use them
     162         * Mirrors libpurple's OPT_PROTO_PASSWORD_OPTIONAL */
     163        PRPL_OPT_PASSWORD_OPTIONAL = 1 << 7,
     164
     165        /* The protocol is not suitable for OTR, see OPT_NOOTR */
     166        PRPL_OPT_NOOTR = 1 << 12,
     167} prpl_options_t;
    146168
    147169struct prpl {
     
    269291        gboolean (* handle_is_self) (struct im_connection *, const char *who);
    270292
     293        /* This sets/updates the im_connection->chatlist field with a
     294         * bee_chat_info_t GSList. This function should ensure the
     295         * bee_chat_list_finish() function gets called at some point
     296         * after the chat list is completely updated.
     297         */
     298        void (* chat_list) (struct im_connection *, const char *server);
     299
    271300        /* Some placeholders so eventually older plugins may cooperate with newer BitlBees. */
    272301        void *resv1;
     
    277306};
    278307
     308struct plugin_info
     309{
     310        guint abiver;
     311        const char *name;
     312        const char *version;
     313        const char *description;
     314        const char *author;
     315        const char *url;
     316};
     317
     318#ifdef WITH_PLUGINS
     319G_MODULE_EXPORT GList *get_plugins();
     320#endif
     321
    279322/* im_api core stuff. */
    280323void nogaim_init();
     324G_MODULE_EXPORT GList *get_protocols();
     325G_MODULE_EXPORT GList *get_protocols_disabled();
    281326G_MODULE_EXPORT GSList *get_connections();
    282327G_MODULE_EXPORT struct prpl *find_protocol(const char *name);
     
    330375G_MODULE_EXPORT void imcb_rename_buddy(struct im_connection *ic, const char *handle, const char *realname);
    331376G_MODULE_EXPORT void imcb_buddy_nick_hint(struct im_connection *ic, const char *handle, const char *nick);
     377G_MODULE_EXPORT void imcb_buddy_nick_change(struct im_connection *ic, const char *handle, const char *nick);
    332378G_MODULE_EXPORT void imcb_buddy_action_response(bee_user_t *bu, const char *action, char * const args[], void *data);
    333379G_MODULE_EXPORT GSList *imcb_get_local_contacts(struct im_connection *ic);
  • protocols/oscar/conn.c

    rba52ac5 r537d9b9  
    383383{
    384384        aim_conn_t *connstruct;
    385         guint16 port = AIM_LOGIN_PORT;
    386         char *host;
    387         int i;
    388385
    389386        if (!(connstruct = aim_conn_getnext(sess))) {
     
    400397        }
    401398
    402         /*
    403          * As of 23 Jul 1999, AOL now sends the port number, preceded by a
    404          * colon, in the BOS redirect.  This fatally breaks all previous
    405          * libfaims.  Bad, bad AOL.
    406          *
    407          * We put this here to catch every case.
    408          *
    409          */
    410 
    411         for (i = 0; i < (int) strlen(dest); i++) {
    412                 if (dest[i] == ':') {
    413                         port = atoi(&(dest[i + 1]));
    414                         break;
    415                 }
    416         }
    417 
    418         host = (char *) g_malloc(i + 1);
    419         strncpy(host, dest, i);
    420         host[i] = '\0';
    421 
    422         connstruct->fd = proxy_connect(host, port, NULL, NULL);
    423 
    424         g_free(host);
     399        /* The code that used to be here was very broken */
     400        g_return_val_if_reached(connstruct);
    425401
    426402        return connstruct;
  • protocols/purple/bpurple.h

    rba52ac5 r537d9b9  
    1313    GHashTable *input_requests;
    1414    guint next_request_id;
     15    char *chat_list_server;
     16    GSList *filetransfers;
    1517};
    1618
  • protocols/purple/ft.c

    rba52ac5 r537d9b9  
    4242        char *fn, *handle;
    4343        gboolean ui_wants_data;
     44        int timeout;
    4445};
    4546
     
    6465        struct prpl_xfer_data *px = ft->data;
    6566
    66         purple_xfer_request_denied(px->xfer);
     67        if (px->xfer) {
     68                if (!purple_xfer_is_completed(px->xfer) && !purple_xfer_is_canceled(px->xfer)) {
     69                        purple_xfer_cancel_local(px->xfer);
     70                }
     71                px->xfer->ui_data = NULL;
     72                purple_xfer_unref(px->xfer);
     73                px->xfer = NULL;
     74        }
     75}
     76
     77static void prpl_xfer_free(struct file_transfer *ft)
     78{
     79        struct prpl_xfer_data *px = ft->data;
     80        struct purple_data *pd = px->ic->proto_data;
     81
     82        pd->filetransfers = g_slist_remove(pd->filetransfers, px);
     83
     84        if (px->xfer) {
     85                px->xfer->ui_data = NULL;
     86                purple_xfer_unref(px->xfer);
     87        }
     88
     89        if (px->timeout) {
     90                b_event_remove(px->timeout);
     91        }
     92
     93        g_free(px->fn);
     94        g_free(px->handle);
     95        if (px->fd >= 0) {
     96                close(px->fd);
     97        }
     98        g_free(px);
    6799}
    68100
    69101static void prplcb_xfer_new(PurpleXfer *xfer)
    70102{
     103        purple_xfer_ref(xfer);
     104
    71105        if (purple_xfer_get_type(xfer) == PURPLE_XFER_RECEIVE) {
    72106                struct prpl_xfer_data *px = g_new0(struct prpl_xfer_data, 1);
     107                struct purple_data *pd;
    73108
    74109                xfer->ui_data = px;
     
    77112                px->fd = -1;
    78113                px->ic = purple_ic_by_pa(xfer->account);
     114
     115                pd = px->ic->proto_data;
     116                pd->filetransfers = g_slist_prepend(pd->filetransfers, px);
    79117
    80118                purple_xfer_set_local_filename(xfer, px->fn);
     
    112150        px->ft->accept = prpl_xfer_accept;
    113151        px->ft->canceled = prpl_xfer_canceled;
     152        px->ft->free = prpl_xfer_free;
    114153        px->ft->write_request = prpl_xfer_write_request;
    115154
     
    164203
    165204
    166 /* Generic (IM<>UI): */
    167205static void prplcb_xfer_destroy(PurpleXfer *xfer)
    168206{
    169207        struct prpl_xfer_data *px = xfer->ui_data;
    170208
    171         g_free(px->fn);
    172         g_free(px->handle);
    173         if (px->fd >= 0) {
    174                 close(px->fd);
    175         }
    176         g_free(px);
     209        if (px) {
     210                px->xfer = NULL;
     211        }
    177212}
    178213
     
    224259        struct prpl_xfer_data *px = xfer->ui_data;
    225260
    226         if (px->ft) {
     261        if (px && px->ft) {
    227262                imcb_file_canceled(px->ic, px->ft, "Canceled by remote end");
    228         } else {
     263        } else if (px) {
    229264                /* px->ft == NULL for sends, because of the two stages. :-/ */
    230265                imcb_error(px->ic, "File transfer cancelled by remote end");
     
    240275{
    241276        struct prpl_xfer_data *px = g_new0(struct prpl_xfer_data, 1);
     277        struct purple_data *pd;
    242278        char *dir, *basename;
    243279
    244280        ft->data = px;
    245281        px->ft = ft;
     282        px->ft->free = prpl_xfer_free;
    246283
    247284        dir = g_strdup("/tmp/bitlbee-purple-ft.XXXXXX");
     
    272309        px->handle = g_strdup(handle);
    273310
     311        pd = px->ic->proto_data;
     312        pd->filetransfers = g_slist_prepend(pd->filetransfers, px);
     313
    274314        imcb_log(ic,
    275315                 "Due to libpurple limitations, the file has to be cached locally before proceeding with the actual file transfer. Please wait...");
    276316
    277         b_timeout_add(0, purple_transfer_request_cb, ft);
     317        px->timeout = b_timeout_add(0, purple_transfer_request_cb, ft);
    278318}
    279319
     
    295335        struct prpl_xfer_data *px = ft->data;
    296336
     337        px->timeout = 0;
     338
    297339        if (ft->write == NULL) {
    298340                ft->write = prpl_xfer_write;
     
    322364                px->ft = NULL;
    323365        } else {
    324                 b_timeout_add(0, purple_transfer_request_cb, ft);
     366                px->timeout = b_timeout_add(0, purple_transfer_request_cb, ft);
    325367        }
    326368
    327369        return TRUE;
     370}
     371
     372void purple_transfer_cancel_all(struct im_connection *ic)
     373{
     374        struct purple_data *pd = ic->proto_data;
     375
     376        while (pd->filetransfers) {
     377                struct prpl_xfer_data *px = pd->filetransfers->data;
     378
     379                if (px->ft) {
     380                        imcb_file_canceled(ic, px->ft, "Logging out");
     381                }
     382
     383                pd->filetransfers = g_slist_remove(pd->filetransfers, px);
     384        }
    328385}
    329386
  • protocols/purple/purple.c

    rba52ac5 r537d9b9  
    4343                                   const char *message, const char *who);
    4444
     45void purple_transfer_cancel_all(struct im_connection *ic);
     46
    4547/* purple_request_input specific stuff */
    4648typedef void (*ri_callback_t)(gpointer, const gchar *);
     
    5456};
    5557
     58struct purple_roomlist_data {
     59        GSList *chats;
     60        gint topic;
     61        gboolean initialized;
     62};
     63
     64
    5665struct im_connection *purple_ic_by_pa(PurpleAccount *pa)
    5766{
     
    94103}
    95104
     105static char *purple_get_account_prpl_id(account_t *acc)
     106{
     107        /* "oscar" is how non-purple bitlbee calls it,
     108         * and it might be icq or aim, depending on the username */
     109        if (g_strcmp0(acc->prpl->name, "oscar") == 0) {
     110                return (g_ascii_isdigit(acc->user[0])) ? "prpl-icq" : "prpl-aim";
     111        }
     112
     113        return acc->prpl->data;
     114}
     115
    96116static void purple_init(account_t *acc)
    97117{
    98         PurplePlugin *prpl = purple_plugins_find_with_id((char *) acc->prpl->data);
     118        char *prpl_id = purple_get_account_prpl_id(acc);
     119        PurplePlugin *prpl = purple_plugins_find_with_id(prpl_id);
    99120        PurplePluginProtocolInfo *pi = prpl->info->extra_info;
    100121        PurpleAccount *pa;
     
    262283        /* Go through all away states to figure out if away/status messages
    263284           are possible. */
    264         pa = purple_account_new(acc->user, (char *) acc->prpl->data);
     285        pa = purple_account_new(acc->user, prpl_id);
    265286        for (st = purple_account_get_status_types(pa); st; st = st->next) {
    266287                PurpleStatusPrimitive prim = purple_status_type_get_primitive(st->data);
     
    345366
    346367        ic->proto_data = pd = g_new0(struct purple_data, 1);
    347         pd->account = purple_account_new(acc->user, (char *) acc->prpl->data);
     368        pd->account = purple_account_new(acc->user, purple_get_account_prpl_id(acc));
    348369        pd->input_requests = g_hash_table_new_full(g_direct_hash, g_direct_equal,
    349370                                                   NULL, g_free);
     
    371392        }
    372393
     394        if (pd->filetransfers) {
     395                purple_transfer_cancel_all(ic);
     396        }
     397
    373398        purple_account_set_enabled(pd->account, "BitlBee", FALSE);
    374399        purple_connections = g_slist_remove(purple_connections, ic);
    375400        purple_accounts_remove(pd->account);
     401        imcb_chat_list_free(ic);
     402        g_free(pd->chat_list_server);
    376403        g_hash_table_destroy(pd->input_requests);
    377404        g_free(pd);
     
    673700}
    674701
     702void purple_chat_set_topic(struct groupchat *gc, char *topic)
     703{
     704        PurpleConversation *pc = gc->data;
     705        PurpleConvChat *pcc = PURPLE_CONV_CHAT(pc);
     706        struct purple_data *pd = gc->ic->proto_data;
     707        PurplePlugin *prpl = purple_plugins_find_with_id(pd->account->protocol_id);
     708        PurplePluginProtocolInfo *pi = prpl->info->extra_info;
     709
     710        if (pi->set_chat_topic) {
     711                pi->set_chat_topic(purple_account_get_connection(pd->account),
     712                                   purple_conv_chat_get_id(pcc),
     713                                   topic);
     714        }
     715}
     716
    675717void purple_chat_kick(struct groupchat *gc, char *who, const char *message)
    676718{
     
    697739        GHashTable *chat_hash;
    698740        PurpleConversation *conv;
     741        struct groupchat *gc;
    699742        GList *info, *l;
    700743
     
    730773        g_list_free(info);
    731774
     775        /* do this before serv_join_chat to handle cases where prplcb_conv_new is called immediately (not async) */
     776        gc = imcb_chat_new(ic, room);
     777
    732778        serv_join_chat(purple_account_get_connection(pd->account), chat_hash);
    733779
    734780        g_hash_table_destroy(chat_hash);
    735781
    736         return imcb_chat_new(ic, room);
     782        return gc;
     783}
     784
     785void purple_chat_list(struct im_connection *ic, const char *server)
     786{
     787        PurpleRoomlist *list;
     788        struct purple_data *pd = ic->proto_data;
     789        PurplePlugin *prpl = purple_plugins_find_with_id(pd->account->protocol_id);
     790        PurplePluginProtocolInfo *pi = prpl->info->extra_info;
     791
     792        if (!pi || !pi->roomlist_get_list) {
     793                imcb_log(ic, "Room listing unsupported by this purple plugin");
     794                return;
     795        }
     796
     797        g_free(pd->chat_list_server);
     798        pd->chat_list_server = (server && *server) ? g_strdup(server) : NULL;
     799
     800        list = purple_roomlist_get_list(pd->account->gc);
     801
     802        if (list) {
     803                struct purple_roomlist_data *rld = list->ui_data;
     804                rld->initialized = TRUE;
     805
     806                purple_roomlist_ref(list);
     807        }
    737808}
    738809
     
    9701041
    9711042/* Generic handler for IM or chat messages, covers write_chat, write_im and write_conv */
    972 static void handle_conv_msg(PurpleConversation *conv, const char *who, const char *message, guint32 bee_flags, time_t mtime)
     1043static void handle_conv_msg(PurpleConversation *conv, const char *who, const char *message_, guint32 bee_flags, time_t mtime)
    9731044{
    9741045        struct im_connection *ic = purple_ic_by_pa(conv->account);
    9751046        struct groupchat *gc = conv->ui_data;
     1047        char *message = g_strdup(message_);
    9761048        PurpleBuddy *buddy;
    9771049
     
    9821054
    9831055        if (conv->type == PURPLE_CONV_TYPE_IM) {
    984                 imcb_buddy_msg(ic, (char *) who, (char *) message, bee_flags, mtime);
     1056                imcb_buddy_msg(ic, who, message, bee_flags, mtime);
    9851057        } else if (gc) {
    986                 imcb_chat_msg(gc, who, (char *) message, bee_flags, mtime);
    987         }
     1058                imcb_chat_msg(gc, who, message, bee_flags, mtime);
     1059        }
     1060
     1061        g_free(message);
    9881062}
    9891063
     
    11711245        struct im_connection *ic = purple_ic_by_pa(account);
    11721246        struct purple_data *pd = ic->proto_data;
    1173         struct request_input_data *ri = g_new0(struct request_input_data, 1);
    1174         guint id = pd->next_request_id++;
     1247        struct request_input_data *ri;
     1248        guint id;
     1249
     1250        /* hack so that jabber's chat list doesn't ask for conference server twice */
     1251        if (pd->chat_list_server && title && g_strcmp0(title, "Enter a Conference Server") == 0) {
     1252                ((ri_callback_t) ok_cb)(user_data, pd->chat_list_server);
     1253                g_free(pd->chat_list_server);
     1254                pd->chat_list_server = NULL;
     1255                return NULL;
     1256        }
     1257
     1258        id = pd->next_request_id++;
     1259        ri = g_new0(struct request_input_data, 1);
    11751260
    11761261        ri->id = id;
     
    11821267
    11831268        imcb_add_buddy(ic, ri->buddy, NULL);
    1184         imcb_buddy_msg(ic, ri->buddy, secondary, 0, 0);
     1269
     1270        if (title && *title) {
     1271                imcb_buddy_msg(ic, ri->buddy, title, 0, 0);
     1272        }
     1273
     1274        if (primary && *primary) {
     1275                imcb_buddy_msg(ic, ri->buddy, primary, 0, 0);
     1276        }
     1277
     1278        if (secondary && *secondary) {
     1279                imcb_buddy_msg(ic, ri->buddy, secondary, 0, 0);
     1280        }
    11851281
    11861282        return ri;
     
    12561352        prplcb_privacy_deny_added,         /* deny_added */
    12571353        prplcb_privacy_deny_removed,       /* deny_removed */
     1354};
     1355
     1356static void prplcb_roomlist_create(PurpleRoomlist *list)
     1357{
     1358        struct purple_roomlist_data *rld;
     1359
     1360        list->ui_data = rld = g_new0(struct purple_roomlist_data, 1);
     1361        rld->topic = -1;
     1362}
     1363
     1364static void prplcb_roomlist_set_fields(PurpleRoomlist *list, GList *fields)
     1365{
     1366        gint topic = -1;
     1367        GList *l;
     1368        guint i;
     1369        PurpleRoomlistField *field;
     1370        struct purple_roomlist_data *rld = list->ui_data;
     1371
     1372        for (i = 0, l = fields; l; i++, l = l->next) {
     1373                field = l->data;
     1374
     1375                /* Use the first visible string field as a fallback topic */
     1376                if (i != 0 && topic < 0 && !field->hidden &&
     1377                    field->type == PURPLE_ROOMLIST_FIELD_STRING) {
     1378                        topic = i;
     1379                }
     1380
     1381                if ((g_strcasecmp(field->name, "description") == 0) ||
     1382                    (g_strcasecmp(field->name, "topic") == 0)) {
     1383                        if (field->type == PURPLE_ROOMLIST_FIELD_STRING) {
     1384                                rld->topic = i;
     1385                        }
     1386                }
     1387        }
     1388
     1389        if (rld->topic < 0) {
     1390                rld->topic = topic;
     1391        }
     1392}
     1393
     1394static void prplcb_roomlist_add_room(PurpleRoomlist *list, PurpleRoomlistRoom *room)
     1395{
     1396        bee_chat_info_t *ci;
     1397        const char *title;
     1398        const char *topic;
     1399        GList *fields;
     1400        struct purple_roomlist_data *rld = list->ui_data;
     1401
     1402        fields = purple_roomlist_room_get_fields(room);
     1403        title = purple_roomlist_room_get_name(room);
     1404
     1405        if (rld->topic >= 0) {
     1406                topic = g_list_nth_data(fields, rld->topic);
     1407        } else {
     1408                topic = NULL;
     1409        }
     1410
     1411        ci = g_new(bee_chat_info_t, 1);
     1412        ci->title = g_strdup(title);
     1413        ci->topic = g_strdup(topic);
     1414        rld->chats = g_slist_prepend(rld->chats, ci);
     1415}
     1416
     1417static void prplcb_roomlist_in_progress(PurpleRoomlist *list, gboolean in_progress)
     1418{
     1419        struct im_connection *ic;
     1420        struct purple_data *pd;
     1421        struct purple_roomlist_data *rld = list->ui_data;
     1422
     1423        if (in_progress || !rld) {
     1424                return;
     1425        }
     1426
     1427        ic = purple_ic_by_pa(list->account);
     1428        imcb_chat_list_free(ic);
     1429
     1430        pd = ic->proto_data;
     1431        g_free(pd->chat_list_server);
     1432        pd->chat_list_server = NULL;
     1433
     1434        ic->chatlist = g_slist_reverse(rld->chats);
     1435        rld->chats = NULL;
     1436
     1437        imcb_chat_list_finish(ic);
     1438
     1439        if (rld->initialized) {
     1440                purple_roomlist_unref(list);
     1441        }
     1442}
     1443
     1444static void prplcb_roomlist_destroy(PurpleRoomlist *list)
     1445{
     1446        g_free(list->ui_data);
     1447        list->ui_data = NULL;
     1448}
     1449
     1450static PurpleRoomlistUiOps bee_roomlist_uiops =
     1451{
     1452        NULL,                         /* show_with_account */
     1453        prplcb_roomlist_create,       /* create */
     1454        prplcb_roomlist_set_fields,   /* set_fields */
     1455        prplcb_roomlist_add_room,     /* add_room */
     1456        prplcb_roomlist_in_progress,  /* in_progress */
     1457        prplcb_roomlist_destroy,      /* destroy */
    12581458};
    12591459
     
    14221622        purple_request_set_ui_ops(&bee_request_uiops);
    14231623        purple_privacy_set_ui_ops(&bee_privacy_uiops);
     1624        purple_roomlist_set_ui_ops(&bee_roomlist_uiops);
    14241625        purple_notify_set_ui_ops(&bee_notify_uiops);
    14251626        purple_accounts_set_ui_ops(&bee_account_uiops);
     
    14381639        char *dir;
    14391640
     1641        if (purple_get_core() != NULL) {
     1642                log_message(LOGLVL_ERROR, "libpurple already initialized. "
     1643                            "Please use inetd or ForkDaemon mode instead.");
     1644                return;
     1645        }
     1646
    14401647        g_assert((int) B_EV_IO_READ == (int) PURPLE_INPUT_READ);
    14411648        g_assert((int) B_EV_IO_WRITE == (int) PURPLE_INPUT_WRITE);
     
    14431650        dir = g_strdup_printf("%s/purple", global.conf->configdir);
    14441651        purple_util_set_user_dir(dir);
     1652        g_free(dir);
     1653
     1654        dir = g_strdup_printf("%s/purple", global.conf->plugindir);
     1655        purple_plugins_add_search_path(dir);
    14451656        g_free(dir);
    14461657
     
    15061717        funcs.chat_with = purple_chat_with;
    15071718        funcs.chat_invite = purple_chat_invite;
     1719        funcs.chat_topic = purple_chat_set_topic;
    15081720        funcs.chat_kick = purple_chat_kick;
    15091721        funcs.chat_leave = purple_chat_leave;
    15101722        funcs.chat_join = purple_chat_join;
     1723        funcs.chat_list = purple_chat_list;
    15111724        funcs.transfer_request = purple_transfer_request;
    15121725
     
    15171730        for (prots = purple_plugins_get_protocols(); prots; prots = prots->next) {
    15181731                PurplePlugin *prot = prots->data;
     1732                PurplePluginProtocolInfo *pi = prot->info->extra_info;
    15191733                struct prpl *ret;
    15201734
     
    15301744                        ret->name += 5;
    15311745                }
     1746
     1747                if (pi->options & OPT_PROTO_NO_PASSWORD) {
     1748                        ret->options |= PRPL_OPT_NO_PASSWORD;
     1749                }
     1750
     1751                if (pi->options & OPT_PROTO_PASSWORD_OPTIONAL) {
     1752                        ret->options |= PRPL_OPT_PASSWORD_OPTIONAL;
     1753                }
     1754
    15321755                register_protocol(ret);
    15331756
     
    15391762                        ret = g_memdup(&funcs, sizeof(funcs));
    15401763                        ret->name = "oscar";
    1541                         ret->data = prot->info->id;
     1764                        /* purple_get_account_prpl_id() determines the actual protocol ID (icq/aim) */
     1765                        ret->data = NULL;
    15421766                        register_protocol(ret);
    15431767                }
  • protocols/skype/skype.c

    rba52ac5 r537d9b9  
    17631763        register_protocol(ret);
    17641764}
     1765
     1766struct plugin_info *init_plugin_info(void)
     1767{
     1768        static struct plugin_info info = {
     1769                BITLBEE_ABI_VERSION_CODE,
     1770                "skype",
     1771                BITLBEE_VERSION,
     1772                "Skype protocol plugin",
     1773                NULL,
     1774                NULL
     1775        };
     1776
     1777        return &info;
     1778}
  • protocols/twitter/twitter.c

    rba52ac5 r537d9b9  
    345345                imcb_log(ic, "Getting contact list");
    346346                twitter_get_friends_ids(ic, -1);
     347                twitter_get_mutes_ids(ic, -1);
     348                twitter_get_noretweets_ids(ic, -1);
    347349        } else {
    348350                twitter_main_loop_start(ic);
     
    469471        g_regex_match(regex, msg, 0, &match_info);
    470472        while (g_match_info_matches(match_info)) {
    471                 gchar *s, *url;
     473                gchar *url;
    472474
    473475                url = g_match_info_fetch(match_info, 2);
    474476                url_len_diff += target_len - g_utf8_strlen(url, -1);
    475477
    476                 /* Add another character for https://t.co/... URLs */
    477                 if ((s = g_match_info_fetch(match_info, 3))) {
    478                         url_len_diff += 1;
    479                         g_free(s);
    480                 }
    481478                g_free(url);
    482479                g_match_info_next(match_info, NULL);
     
    541538        if (strcmp(acc->prpl->name, "twitter") == 0) {
    542539                def_url = TWITTER_API_URL;
    543                 def_tul = "22";
     540                def_tul = "23";
    544541                def_mentions = "true";
    545542        } else {                /* if( strcmp( acc->prpl->name, "identica" ) == 0 ) */
     
    690687                }
    691688
     689                g_slist_foreach(td->mutes_ids, (GFunc) g_free, NULL);
     690                g_slist_free(td->mutes_ids);
     691
     692                g_slist_foreach(td->noretweets_ids, (GFunc) g_free, NULL);
     693                g_slist_free(td->noretweets_ids);
     694
    692695                http_close(td->stream);
    693696                twitter_filter_remove_all(ic);
     
    948951        } else if ((g_strcasecmp(cmd[0], "favourite") == 0 ||
    949952                    g_strcasecmp(cmd[0], "favorite") == 0 ||
    950                     g_strcasecmp(cmd[0], "fav") == 0) && cmd[1]) {
     953                    g_strcasecmp(cmd[0], "fav") == 0 ||
     954                    g_strcasecmp(cmd[0], "like") == 0) && cmd[1]) {
    951955                if ((id = twitter_message_id_from_command_arg(ic, cmd[1], NULL))) {
    952956                        twitter_favourite_tweet(ic, id);
     
    960964        } else if (g_strcasecmp(cmd[0], "unfollow") == 0 && cmd[1]) {
    961965                twitter_remove_buddy(ic, cmd[1], NULL);
     966                goto eof;
     967        } else if (g_strcasecmp(cmd[0], "mute") == 0 && cmd[1]) {
     968                twitter_mute_create_destroy(ic, cmd[1], 1);
     969                goto eof;
     970        } else if (g_strcasecmp(cmd[0], "unmute") == 0 && cmd[1]) {
     971                twitter_mute_create_destroy(ic, cmd[1], 0);
    962972                goto eof;
    963973        } else if ((g_strcasecmp(cmd[0], "report") == 0 ||
     
    10821092        struct prpl *ret = g_new0(struct prpl, 1);
    10831093
    1084         ret->options = OPT_NOOTR;
     1094        ret->options = PRPL_OPT_NOOTR | PRPL_OPT_NO_PASSWORD;
    10851095        ret->name = "twitter";
    10861096        ret->login = twitter_login;
     
    11091119        ret = g_memdup(ret, sizeof(struct prpl));
    11101120        ret->name = "identica";
     1121        ret->options =  PRPL_OPT_NOOTR;
    11111122        register_protocol(ret);
    11121123}
  • protocols/twitter/twitter.h

    rba52ac5 r537d9b9  
    6161
    6262        GSList *follow_ids;
     63        GSList *mutes_ids;
     64        GSList *noretweets_ids;
    6365        GSList *filters;
    6466
  • protocols/twitter/twitter_http.c

    rba52ac5 r537d9b9  
    7979        g_string_printf(request, "%s %s%s%s%s HTTP/1.1\r\n"
    8080                        "Host: %s\r\n"
    81                         "User-Agent: BitlBee " BITLBEE_VERSION " " ARCH "/" CPU "\r\n",
     81                        "User-Agent: BitlBee " BITLBEE_VERSION "\r\n",
    8282                        is_post ? "POST" : "GET",
    8383                        base_url ? base_url->file : td->url_path,
  • protocols/twitter/twitter_lib.c

    rba52ac5 r537d9b9  
    245245
    246246static void twitter_http_get_friends_ids(struct http_request *req);
     247static void twitter_http_get_mutes_ids(struct http_request *req);
     248static void twitter_http_get_noretweets_ids(struct http_request *req);
    247249
    248250/**
     
    257259        args[1] = g_strdup_printf("%" G_GINT64_FORMAT, next_cursor);
    258260        twitter_http(ic, TWITTER_FRIENDS_IDS_URL, twitter_http_get_friends_ids, ic, 0, args, 2);
     261
     262        g_free(args[1]);
     263}
     264
     265/**
     266 * Get the muted users ids.
     267 */
     268void twitter_get_mutes_ids(struct im_connection *ic, gint64 next_cursor)
     269{
     270        char *args[2];
     271
     272        args[0] = "cursor";
     273        args[1] = g_strdup_printf("%" G_GINT64_FORMAT, next_cursor);
     274        twitter_http(ic, TWITTER_MUTES_IDS_URL, twitter_http_get_mutes_ids, ic, 0, args, 2);
     275
     276        g_free(args[1]);
     277}
     278
     279/**
     280 * Get the ids for users from whom we should ignore retweets.
     281 */
     282void twitter_get_noretweets_ids(struct im_connection *ic, gint64 next_cursor)
     283{
     284        char *args[2];
     285
     286        args[0] = "cursor";
     287        args[1] = g_strdup_printf("%" G_GINT64_FORMAT, next_cursor);
     288        twitter_http(ic, TWITTER_NORETWEETS_IDS_URL, twitter_http_get_noretweets_ids, ic, 0, args, 2);
    259289
    260290        g_free(args[1]);
     
    334364                twitter_get_users_lookup(ic);
    335365        }
     366
     367        txl->list = NULL;
     368        txl_free(txl);
     369}
     370
     371/**
     372 * Callback for getting the mutes ids.
     373 */
     374static void twitter_http_get_mutes_ids(struct http_request *req)
     375{
     376        struct im_connection *ic = req->data;
     377        JSON_Value *parsed;
     378        struct twitter_xml_list *txl;
     379        struct twitter_data *td;
     380
     381        // Check if the connection is stil active
     382        if (!g_slist_find(twitter_connections, ic)) {
     383                return;
     384        }
     385
     386        td = ic->proto_data;
     387
     388        if (req->status_code != 200) {
     389                /* Fail silently */
     390                return;
     391        }
     392
     393        // Parse the data.
     394        if (!(parsed = twitter_parse_response(ic, req))) {
     395                return;
     396        }
     397
     398        txl = g_new0(struct twitter_xml_list, 1);
     399        txl->list = td->mutes_ids;
     400
     401        /* mute ids API response is similar enough to friends response
     402           to reuse this method */
     403        twitter_xt_get_friends_id_list(parsed, txl);
     404        json_value_free(parsed);
     405
     406        td->mutes_ids = txl->list;
     407        if (txl->next_cursor) {
     408                /* Recurse while there are still more pages */
     409                twitter_get_mutes_ids(ic, txl->next_cursor);
     410        }
     411
     412        txl->list = NULL;
     413        txl_free(txl);
     414}
     415
     416/**
     417 * Callback for getting the no-retweets ids.
     418 */
     419static void twitter_http_get_noretweets_ids(struct http_request *req)
     420{
     421        struct im_connection *ic = req->data;
     422        JSON_Value *parsed;
     423        struct twitter_xml_list *txl;
     424        struct twitter_data *td;
     425
     426        // Check if the connection is stil active
     427        if (!g_slist_find(twitter_connections, ic)) {
     428                return;
     429        }
     430
     431        if (req->status_code != 200) {
     432                /* Fail silently */
     433                return;
     434        }
     435
     436        td = ic->proto_data;
     437
     438        // Parse the data.
     439        if (!(parsed = twitter_parse_response(ic, req))) {
     440                return;
     441        }
     442
     443        txl = g_new0(struct twitter_xml_list, 1);
     444        txl->list = td->noretweets_ids;
     445       
     446        // Process the retweet ids
     447        txl->type = TXL_ID;
     448        if (json_type(parsed) == JSONArray) {
     449                JSON_Array *arr = json_array(parsed);
     450                unsigned int i;
     451                for (i = 0; i < json_array_get_count(arr); i++) {
     452                        jint id = json_array_get_integer(arr, i);
     453                        txl->list = g_slist_prepend(txl->list,
     454                                                    g_strdup_printf("%lld", id));
     455                }
     456        }
     457
     458        json_value_free(parsed);
     459        td->noretweets_ids = txl->list;
    336460
    337461        txl->list = NULL;
     
    467591#endif
    468592
    469 static void expand_entities(char **text, const JSON_Object *node);
     593static void expand_entities(char **text, const JSON_Object *node, const JSON_Object *extended_node);
    470594
    471595/**
     
    479603static struct twitter_xml_status *twitter_xt_get_status(const JSON_Object *node)
    480604{
    481         struct twitter_xml_status *txs;
     605        struct twitter_xml_status *txs = {0};
    482606        const JSON_Object *rt = NULL;
     607        const JSON_Value *text_value = NULL;
     608        const JSON_Object *extended_node = NULL;
    483609
    484610        if (!node) {
     
    488614
    489615        JSON_O_FOREACH(node, k, v) {
    490                 if (strcmp("text", k) == 0 && (txs->text = g_strdup(json_string(v)))) {
    491                         // TODO: Huh strip html? In json? Not sure whether I have to..
    492                         strip_html(txs->text);
     616                if (strcmp("text", k) == 0 && json_type(v) == JSONString && text_value == NULL) {
     617                        text_value = v;
     618                } else if (strcmp("full_text", k) == 0 && json_type(v) == JSONString) {
     619                        text_value = v;
     620                } else if (strcmp("extended_tweet", k) == 0 && json_type(v) == JSONObject) {
     621                        text_value = json_object_get_value(json_object(v), "full_text");
     622                        extended_node = json_object(v);
    493623                } else if (strcmp("retweeted_status", k) == 0 && (rt = json_object(v))) {
    494624                        // Handling below.
     
    516646                struct twitter_xml_status *rtxs = twitter_xt_get_status(rt);
    517647                if (rtxs) {
    518                         g_free(txs->text);
    519648                        txs->text = g_strdup_printf("RT @%s: %s", rtxs->user->screen_name, rtxs->text);
    520649                        txs->id = rtxs->id;
    521650                        txs_free(rtxs);
    522651                }
    523         } else {
    524                 expand_entities(&txs->text, node);
     652        } else if (text_value && json_type(text_value) == JSONString) {
     653                txs->text = g_strdup(json_string(text_value));
     654                strip_html(txs->text);
     655                expand_entities(&txs->text, node, extended_node);
    525656        }
    526657
     
    564695        }
    565696
    566         expand_entities(&txs->text, node);
     697        expand_entities(&txs->text, node, NULL);
    567698
    568699        if (txs->text && txs->user && txs->id) {
     
    574705}
    575706
    576 static void expand_entities(char **text, const JSON_Object *node)
    577 {
    578         JSON_Object *entities, *quoted;
     707static void expand_entities(char **text, const JSON_Object *node, const JSON_Object *extended_node)
     708{
     709        JSON_Object *entities, *extended_entities, *quoted;
    579710        char *quote_url = NULL, *quote_text = NULL;
    580711
     
    594725        }
    595726
     727        if (extended_node) {
     728                extended_entities = json_object_get_object(extended_node, "entities");
     729                if (extended_entities) {
     730                        entities = extended_entities;
     731                }
     732        }
     733
    596734        JSON_O_FOREACH(entities, k, v) {
    597735                int i;
     
    837975        struct twitter_data *td = ic->proto_data;
    838976        char *last_id_str;
     977        char *uid_str;
    839978
    840979        if (status->user == NULL || status->text == NULL) {
     980                return;
     981        }
     982       
     983        /* Check this is not a tweet that should be muted */
     984        uid_str = g_strdup_printf("%" G_GUINT64_FORMAT, status->user->uid);
     985
     986        if (g_slist_find_custom(td->mutes_ids, uid_str, (GCompareFunc)strcmp)) {
     987                g_free(uid_str);
     988                return;
     989        }
     990        if (status->id != status->rt_id && g_slist_find_custom(td->noretweets_ids, uid_str, (GCompareFunc)strcmp)) {
     991                g_free(uid_str);
    841992                return;
    842993        }
     
    8631014        set_setstr(&ic->acc->set, "_last_tweet", last_id_str);
    8641015        g_free(last_id_str);
     1016        g_free(uid_str);
    8651017}
    8661018
     
    8801032        }
    8811033
    882         ic->flags |= OPT_PONGED;
    8831034        td = ic->proto_data;
    8841035
     
    8961047                imc_logout(ic, TRUE);
    8971048                return;
     1049        }
     1050
     1051        if (req == td->stream) {
     1052                ic->flags |= OPT_PONGED;
    8981053        }
    8991054
     
    9991154        JSON_Object *target = json_object_get_object(o, "target");
    10001155        const char *type = json_object_get_string(o, "event");
     1156        struct twitter_xml_user *us = NULL;
     1157        struct twitter_xml_user *ut = NULL;
    10011158
    10021159        if (!type || !source || !target) {
     
    10051162
    10061163        if (strcmp(type, "follow") == 0) {
    1007                 struct twitter_xml_user *us = twitter_xt_get_user(source);
    1008                 struct twitter_xml_user *ut = twitter_xt_get_user(target);
     1164                us = twitter_xt_get_user(source);
     1165                ut = twitter_xt_get_user(target);
    10091166                if (g_strcasecmp(us->screen_name, td->user) == 0) {
    10101167                        twitter_add_buddy(ic, ut->screen_name, ut->name);
    10111168                }
    1012                 txu_free(us);
    1013                 txu_free(ut);
    1014         }
     1169        } else if (strcmp(type, "mute") == 0) {
     1170                GSList *found;
     1171                char *uid_str;
     1172                ut = twitter_xt_get_user(target);
     1173                uid_str = g_strdup_printf("%" G_GUINT64_FORMAT, ut->uid);
     1174                if (!(found = g_slist_find_custom(td->mutes_ids, uid_str,
     1175                                                  (GCompareFunc)strcmp))) {
     1176                        td->mutes_ids = g_slist_prepend(td->mutes_ids, uid_str);
     1177                }
     1178                twitter_log(ic, "Muted user %s", ut->screen_name);
     1179                if (getenv("BITLBEE_DEBUG")) {
     1180                        fprintf(stderr, "New mute: %s %"G_GUINT64_FORMAT"\n",
     1181                                ut->screen_name, ut->uid);
     1182                }
     1183        } else if (strcmp(type, "unmute") == 0) {
     1184                GSList *found;
     1185                char *uid_str;
     1186                ut = twitter_xt_get_user(target);
     1187                uid_str = g_strdup_printf("%" G_GUINT64_FORMAT, ut->uid);
     1188                if ((found = g_slist_find_custom(td->mutes_ids, uid_str,
     1189                                                (GCompareFunc)strcmp))) {
     1190                        char *found_str = found->data;
     1191                        td->mutes_ids = g_slist_delete_link(td->mutes_ids, found);
     1192                        g_free(found_str);
     1193                }
     1194                g_free(uid_str);
     1195                twitter_log(ic, "Unmuted user %s", ut->screen_name);
     1196                if (getenv("BITLBEE_DEBUG")) {
     1197                        fprintf(stderr, "New unmute: %s %"G_GUINT64_FORMAT"\n",
     1198                                ut->screen_name, ut->uid);
     1199                }
     1200        }
     1201
     1202        txu_free(us);
     1203        txu_free(ut);
    10151204
    10161205        return TRUE;
     
    13091498        td->flags &= ~TWITTER_GOT_TIMELINE;
    13101499
    1311         char *args[6];
     1500        char *args[8];
    13121501        args[0] = "cursor";
    13131502        args[1] = g_strdup_printf("%" G_GINT64_FORMAT, next_cursor);
    13141503        args[2] = "include_entities";
    13151504        args[3] = "true";
     1505        args[4] = "tweet_mode";
     1506        args[5] = "extended";
    13161507        if (td->timeline_id) {
    1317                 args[4] = "since_id";
    1318                 args[5] = g_strdup_printf("%" G_GUINT64_FORMAT, td->timeline_id);
     1508                args[6] = "since_id";
     1509                args[7] = g_strdup_printf("%" G_GUINT64_FORMAT, td->timeline_id);
    13191510        }
    13201511
    13211512        if (twitter_http(ic, TWITTER_HOME_TIMELINE_URL, twitter_http_get_home_timeline, ic, 0, args,
    1322                          td->timeline_id ? 6 : 4) == NULL) {
     1513                         td->timeline_id ? 8 : 6) == NULL) {
    13231514                if (++td->http_fails >= 5) {
    13241515                        imcb_error(ic, "Could not retrieve %s: %s",
     
    13311522        g_free(args[1]);
    13321523        if (td->timeline_id) {
    1333                 g_free(args[5]);
     1524                g_free(args[7]);
    13341525        }
    13351526}
     
    13461537        td->flags &= ~TWITTER_GOT_MENTIONS;
    13471538
    1348         char *args[6];
     1539        char *args[8];
    13491540        args[0] = "cursor";
    13501541        args[1] = g_strdup_printf("%" G_GINT64_FORMAT, next_cursor);
     
    13581549                args[5] = g_strdup_printf("%d", set_getint(&ic->acc->set, "show_old_mentions"));
    13591550        }
     1551        args[6] = "tweet_mode";
     1552        args[7] = "extended";
    13601553
    13611554        if (twitter_http(ic, TWITTER_MENTIONS_URL, twitter_http_get_mentions,
    1362                          ic, 0, args, 6) == NULL) {
     1555                         ic, 0, args, 8) == NULL) {
    13631556                if (++td->http_fails >= 5) {
    13641557                        imcb_error(ic, "Could not retrieve %s: %s",
     
    15291722}
    15301723
     1724/**
     1725 * Mute or unmute a user
     1726 */
     1727void twitter_mute_create_destroy(struct im_connection *ic, char *who, int create)
     1728{
     1729        char *args[2];
     1730
     1731        args[0] = "screen_name";
     1732        args[1] = who;
     1733        twitter_http(ic, create ? TWITTER_MUTES_CREATE_URL : TWITTER_MUTES_DESTROY_URL,
     1734                     twitter_http_post, ic, 1, args, 2);
     1735}
     1736
    15311737void twitter_status_destroy(struct im_connection *ic, guint64 id)
    15321738{
  • protocols/twitter/twitter_lib.h

    rba52ac5 r537d9b9  
    6363#define TWITTER_FRIENDS_IDS_URL "/friends/ids.json"
    6464#define TWITTER_FOLLOWERS_IDS_URL "/followers/ids.json"
     65#define TWITTER_MUTES_IDS_URL "/mutes/users/ids.json"
     66#define TWITTER_NORETWEETS_IDS_URL "/friendships/no_retweets/ids.json"
    6567
    6668/* Account URLs */
     
    7678#define TWITTER_BLOCKS_DESTROY_URL "/blocks/destroy/"
    7779
     80/* Mute URLs */
     81#define TWITTER_MUTES_CREATE_URL "/mutes/users/create.json"
     82#define TWITTER_MUTES_DESTROY_URL "/mutes/users/destroy.json"
     83
    7884/* Report spam */
    7985#define TWITTER_REPORT_SPAM_URL "/users/report_spam.json"
     
    8793gboolean twitter_get_timeline(struct im_connection *ic, gint64 next_cursor);
    8894void twitter_get_friends_ids(struct im_connection *ic, gint64 next_cursor);
     95void twitter_get_mutes_ids(struct im_connection *ic, gint64 next_cursor);
     96void twitter_get_noretweets_ids(struct im_connection *ic, gint64 next_cursor);
    8997void twitter_get_statuses_friends(struct im_connection *ic, gint64 next_cursor);
    9098
     
    92100void twitter_direct_messages_new(struct im_connection *ic, char *who, char *message);
    93101void twitter_friendships_create_destroy(struct im_connection *ic, char *who, int create);
     102void twitter_mute_create_destroy(struct im_connection *ic, char *who, int create);
    94103void twitter_status_destroy(struct im_connection *ic, guint64 id);
    95104void twitter_status_retweet(struct im_connection *ic, guint64 id);
  • root_commands.c

    rba52ac5 r537d9b9  
    143143        }
    144144
    145         if (load) {
     145        status = auth_check_pass(irc, irc->user->nick, password);
     146        if (load && (status == STORAGE_OK)) {
    146147                status = storage_load(irc, password);
    147         } else {
    148                 status = storage_check_pass(irc->user->nick, password);
    149148        }
    150149
     
    159158                irc_rootmsg(irc, "Password accepted%s",
    160159                            load ? ", settings and accounts loaded" : "");
    161                 irc_setpass(irc, password);
    162160                irc->status |= USTATUS_IDENTIFIED;
    163161                irc_umode_set(irc, "+R", 1);
     
    268266        storage_status_t status;
    269267
    270         status = storage_remove(irc->user->nick, cmd[1]);
     268        status = auth_check_pass(irc, irc->user->nick, cmd[1]);
     269        if (status == STORAGE_OK) {
     270                status = storage_remove(irc->user->nick);
     271        }
     272
    271273        switch (status) {
    272274        case STORAGE_NO_SUCH_USER:
     
    340342                int st;
    341343
     344                if (s && s->flags & SET_LOCKED) {
     345                        irc_rootmsg(irc, "This setting can not be changed");
     346                        return 0;
     347                }
    342348                if (s && checkflags && checkflags(irc, s) == 0) {
    343349                        return 0;
     
    388394                irc_rootmsg(irc, "This setting can only be changed when the account is %s-line", "on");
    389395                return 0;
     396        } else if (a->flags & ACC_FLAG_LOCKED && s && s->flags & ACC_SET_LOCKABLE) {
     397                irc_rootmsg(irc, "This setting can not be changed for locked accounts");
     398                return 0;
    390399        }
    391400
     
    409418
    410419                MIN_ARGS(3);
     420
     421                if (!global.conf->allow_account_add) {
     422                        irc_rootmsg(irc, "This server does not allow adding new accounts");
     423                        return;
     424                }
    411425
    412426                if (cmd[4] == NULL) {
     
    456470                                irc_rootmsg(irc, "No need to enter a password for this "
    457471                                            "account since it's using OAuth");
     472                        } else if (prpl->options & PRPL_OPT_NO_PASSWORD) {
     473                                *a->pass = '\0';
     474                        } else if (prpl->options & PRPL_OPT_PASSWORD_OPTIONAL) {
     475                                *a->pass = '\0';
     476                                irc_rootmsg(irc, "Passwords are optional for this account. "
     477                                            "If you wish to enter the password with /OPER, do "
     478                                            "account %s set -del password", a->tag);
    458479                        } else {
    459480                                irc_rootmsg(irc, "You can now use the /OPER command to "
     
    465486                                }
    466487                        }
     488                } else if (prpl->options & PRPL_OPT_NO_PASSWORD) {
     489                        irc_rootmsg(irc, "Note: this account doesn't use password for login");
    467490                }
    468491
     
    553576
    554577        if (len >= 1 && g_strncasecmp(cmd[2], "del", len) == 0) {
    555                 if (a->ic) {
     578                if (a->flags & ACC_FLAG_LOCKED) {
     579                        irc_rootmsg(irc, "Account is locked, can't delete");
     580                }
     581                else if (a->ic) {
    556582                        irc_rootmsg(irc, "Account is still logged in, can't delete");
    557583                } else {
     
    11081134}
    11091135
     1136static gint prplcmp(gconstpointer a, gconstpointer b)
     1137{
     1138        const struct prpl *pa = a;
     1139        const struct prpl *pb = b;
     1140
     1141        return g_strcasecmp(pa->name, pb->name);
     1142}
     1143
     1144static void prplstr(GList *prpls, GString *gstr)
     1145{
     1146        const char *last = NULL;
     1147        GList *l;
     1148        struct prpl *p;
     1149
     1150        prpls = g_list_copy(prpls);
     1151        prpls = g_list_sort(prpls, prplcmp);
     1152
     1153        for (l = prpls; l; l = l->next) {
     1154                p = l->data;
     1155
     1156                if (last && g_strcasecmp(p->name, last) == 0) {
     1157                        /* Ignore duplicates (mainly for libpurple) */
     1158                        continue;
     1159                }
     1160
     1161                if (gstr->len != 0) {
     1162                        g_string_append(gstr, ", ");
     1163                }
     1164
     1165                g_string_append(gstr, p->name);
     1166                last = p->name;
     1167        }
     1168
     1169        g_list_free(prpls);
     1170}
     1171
     1172static void cmd_plugins(irc_t *irc, char **cmd)
     1173{
     1174        GList *prpls;
     1175        GString *gstr;
     1176
     1177#ifdef WITH_PLUGINS
     1178        GList *l;
     1179        struct plugin_info *info;
     1180
     1181        for (l = get_plugins(); l; l = l->next) {
     1182                info = l->data;
     1183                irc_rootmsg(irc, "%s:", info->name);
     1184                irc_rootmsg(irc, "  Version: %s", info->version);
     1185
     1186                if (info->description) {
     1187                        irc_rootmsg(irc, "  Description: %s", info->description);
     1188                }
     1189
     1190                if (info->author) {
     1191                        irc_rootmsg(irc, "  Author: %s", info->author);
     1192                }
     1193
     1194                if (info->url) {
     1195                        irc_rootmsg(irc, "  URL: %s", info->url);
     1196                }
     1197
     1198                irc_rootmsg(irc, "");
     1199        }
     1200#endif
     1201
     1202        gstr = g_string_new(NULL);
     1203        prpls = get_protocols();
     1204
     1205        if (prpls) {
     1206                prplstr(prpls, gstr);
     1207                irc_rootmsg(irc, "Enabled Protocols: %s", gstr->str);
     1208                g_string_truncate(gstr, 0);
     1209        }
     1210
     1211        prpls = get_protocols_disabled();
     1212
     1213        if (prpls) {
     1214                prplstr(prpls, gstr);
     1215                irc_rootmsg(irc, "Disabled Protocols: %s", gstr->str);
     1216        }
     1217
     1218        g_string_free(gstr, TRUE);
     1219}
     1220
    11101221static void cmd_qlist(irc_t *irc, char **cmd)
    11111222{
     
    11341245
    11351246        if (g_strcasecmp(cmd[1], "add") == 0) {
    1136                 char *channel, *s;
     1247                bee_chat_info_t *ci;
     1248                char *channel, *room, *s;
    11371249                struct irc_channel *ic;
     1250                guint i;
    11381251
    11391252                MIN_ARGS(3);
     
    11471260                }
    11481261
     1262                if (cmd[3][0] == '!') {
     1263                        if (!acc->ic || !(acc->ic->flags & OPT_LOGGED_IN)) {
     1264                                irc_rootmsg(irc, "Not logged in to account.");
     1265                                return;
     1266                        } else if (!acc->prpl->chat_list) {
     1267                                irc_rootmsg(irc, "Listing chatrooms not supported on that account.");
     1268                                return;
     1269                        }
     1270
     1271                        i = g_ascii_strtoull(cmd[3] + 1, NULL, 10);
     1272                        ci = g_slist_nth_data(acc->ic->chatlist, i - 1);
     1273
     1274                        if (ci == NULL) {
     1275                                irc_rootmsg(irc, "Invalid chatroom index");
     1276                                return;
     1277                        }
     1278
     1279                        room = ci->title;
     1280                } else {
     1281                        room = cmd[3];
     1282                }
     1283
    11491284                if (cmd[4] == NULL) {
    1150                         channel = g_strdup(cmd[3]);
     1285                        channel = g_strdup(room);
    11511286                        if ((s = strchr(channel, '@'))) {
    11521287                                *s = 0;
     
    11681303                    set_setstr(&ic->set, "chat_type", "room") &&
    11691304                    set_setstr(&ic->set, "account", cmd[2]) &&
    1170                     set_setstr(&ic->set, "room", cmd[3])) {
     1305                    set_setstr(&ic->set, "room", room)) {
    11711306                        irc_rootmsg(irc, "Chatroom successfully added.");
    11721307                } else {
     
    11781313                }
    11791314                g_free(channel);
     1315        } else if (g_strcasecmp(cmd[1], "list") == 0) {
     1316                MIN_ARGS(2);
     1317
     1318                if (!(acc = account_get(irc->b, cmd[2]))) {
     1319                        irc_rootmsg(irc, "Invalid account");
     1320                        return;
     1321                } else if (!acc->ic || !(acc->ic->flags & OPT_LOGGED_IN)) {
     1322                        irc_rootmsg(irc, "Not logged in to account.");
     1323                        return;
     1324                } else if (!acc->prpl->chat_list) {
     1325                        irc_rootmsg(irc, "Listing chatrooms not supported on that account.");
     1326                        return;
     1327                }
     1328
     1329                acc->prpl->chat_list(acc->ic, cmd[3]);
    11801330        } else if (g_strcasecmp(cmd[1], "with") == 0) {
    11811331                irc_user_t *iu;
     
    11921342                        irc_rootmsg(irc, "Can't open a groupchat with %s.", cmd[2]);
    11931343                }
    1194         } else if (g_strcasecmp(cmd[1], "list") == 0 ||
    1195                    g_strcasecmp(cmd[1], "set") == 0 ||
     1344        } else if (g_strcasecmp(cmd[1], "set") == 0 ||
    11961345                   g_strcasecmp(cmd[1], "del") == 0) {
    11971346                irc_rootmsg(irc,
     
    12031352                            cmd[1]);
    12041353        }
     1354}
     1355
     1356/* some arbitrary numbers */
     1357#define CHAT_TITLE_LEN_MIN 20
     1358#define CHAT_TITLE_LEN_MAX 100
     1359
     1360void cmd_chat_list_finish(struct im_connection *ic)
     1361{
     1362        account_t *acc = ic->acc;
     1363        bee_chat_info_t *ci;
     1364        char *hformat, *iformat, *topic, *padded;
     1365        GSList *l;
     1366        guint i = 0;
     1367        long title_len, new_len;
     1368        irc_t *irc = ic->bee->ui_data;
     1369
     1370        if (ic->chatlist == NULL) {
     1371                irc_rootmsg(irc, "No existing chatrooms");
     1372                return;
     1373        }
     1374
     1375        /* find a reasonable width for the table */
     1376        title_len = CHAT_TITLE_LEN_MIN;
     1377
     1378        for (l = ic->chatlist; l; l = l->next) {
     1379                ci = l->data;
     1380                new_len = g_utf8_strlen(ci->title, -1);
     1381
     1382                if (new_len >= CHAT_TITLE_LEN_MAX) {
     1383                        title_len = CHAT_TITLE_LEN_MAX;
     1384                        break;
     1385                } else if (title_len < new_len) {
     1386                        title_len = new_len;
     1387                }
     1388        }
     1389
     1390        if (strchr(irc->umode, 'b') != NULL) {
     1391                hformat = "%s\t%s\t%s";
     1392                iformat = "%u\t%s\t%s";
     1393        } else {
     1394                hformat = "%s  %s  %s";
     1395                iformat = "%5u  %s  %s";
     1396        }
     1397
     1398        padded = str_pad_and_truncate("Title", title_len, NULL);
     1399        irc_rootmsg(irc, hformat, "Index", padded, "Topic");
     1400        g_free(padded);
     1401
     1402        for (l = ic->chatlist; l; l = l->next) {
     1403                ci = l->data;
     1404                topic = ci->topic ? ci->topic : "";
     1405
     1406                padded = str_pad_and_truncate(ci->title, title_len, "[...]");
     1407                irc_rootmsg(irc, iformat, ++i, padded, topic);
     1408                g_free(padded);
     1409        }
     1410
     1411        irc_rootmsg(irc, "%u %s chatrooms", i, acc->tag);
    12051412}
    12061413
     
    12561463{
    12571464        GSList *files = irc->file_transfers;
     1465        GSList *next;
    12581466
    12591467        enum { LIST, REJECT, CANCEL };
     
    12731481        }
    12741482
    1275         for (; files; files = g_slist_next(files)) {
     1483        for (; files; files = next) {
     1484                next = files->next;
    12761485                file_transfer_t *file = files->data;
    12771486
     
    13571566        { "nick",           1, cmd_nick,           0 },
    13581567        { "no",             0, cmd_yesno,          0 },
     1568        { "plugins",        0, cmd_plugins,        0 },
    13591569        { "qlist",          0, cmd_qlist,          0 },
    13601570        { "register",       0, cmd_register,       0 },
  • set.h

    rba52ac5 r537d9b9  
    4949        SET_PASSWORD = 0x0400, /* Value shows up in settings list as "********". */
    5050        SET_HIDDEN_DEFAULT = 0x0800, /* Hide unless changed from default. */
     51        SET_LOCKED = 0x1000    /* Setting is locked, don't allow changing it */
    5152} set_flags_t;
    5253
  • storage.c

    rba52ac5 r537d9b9  
    2929#include "bitlbee.h"
    3030
    31 extern storage_t storage_text;
    3231extern storage_t storage_xml;
    3332
     
    9291}
    9392
    94 storage_status_t storage_check_pass(const char *nick, const char *password)
     93storage_status_t storage_check_pass(irc_t *irc, const char *nick, const char *password)
    9594{
    9695        GList *gl;
     
    102101                storage_status_t status;
    103102
    104                 status = st->check_pass(nick, password);
     103                status = st->check_pass(irc, nick, password);
    105104                if (status != STORAGE_NO_SUCH_USER) {
    106105                        return status;
     
    176175}
    177176
    178 storage_status_t storage_remove(const char *nick, const char *password)
     177storage_status_t storage_remove(const char *nick)
    179178{
    180179        GList *gl;
     
    190189                storage_status_t status;
    191190
    192                 status = st->remove(nick, password);
     191                status = st->remove(nick);
    193192                ok |= status == STORAGE_OK;
    194193                if (status != STORAGE_NO_SUCH_USER && status != STORAGE_OK) {
  • storage.h

    rba52ac5 r537d9b9  
    3131        STORAGE_NO_SUCH_USER,
    3232        STORAGE_INVALID_PASSWORD,
     33        STORAGE_CHECK_BACKEND,
    3334        STORAGE_ALREADY_EXISTS,
    3435        STORAGE_OTHER_ERROR /* Error that isn't caused by user input, such as
     
    4344        void (*init)(void);
    4445
    45         storage_status_t (*check_pass)(const char *nick, const char *password);
     46        storage_status_t (*check_pass)(irc_t *irc, const char *nick, const char *password);
    4647
    4748        storage_status_t (*load)(irc_t *irc, const char *password);
    4849        storage_status_t (*save)(irc_t *irc, int overwrite);
    49         storage_status_t (*remove)(const char *nick, const char *password);
     50        storage_status_t (*remove)(const char *nick);
    5051
    5152        /* May be NULL if not supported by backend */
     
    5354} storage_t;
    5455
    55 storage_status_t storage_check_pass(const char *nick, const char *password);
     56storage_status_t storage_check_pass(irc_t *irc, const char *nick, const char *password);
    5657
    5758storage_status_t storage_load(irc_t * irc, const char *password);
    5859storage_status_t storage_save(irc_t *irc, char *password, int overwrite);
    59 storage_status_t storage_remove(const char *nick, const char *password);
     60storage_status_t storage_remove(const char *nick);
    6061
    6162void register_storage_backend(storage_t *);
  • storage_xml.c

    rba52ac5 r537d9b9  
    3434
    3535typedef enum {
    36         XML_PASS_CHECK_ONLY = -1,
    37         XML_PASS_UNKNOWN = 0,
    38         XML_PASS_WRONG,
    39         XML_PASS_OK
    40 } xml_pass_st;
     36        XML_PASS_CHECK = 0,
     37        XML_LOAD
     38} xml_action;
    4139
    4240/* To make it easier later when extending the format: */
     
    6563{
    6664        struct xt_node *c;
     65        struct set *s;
    6766
    6867        for (c = node->children; (c = xt_find_node(c, "setting")); c = c->next) {
    6968                char *name = xt_find_attr(c, "name");
     69                char *locked = xt_find_attr(c, "locked");
    7070
    7171                if (!name) {
     
    8080                }
    8181                set_setstr(head, name, c->text);
     82                if (locked && !g_strcasecmp(locked, "true")) {
     83                        s = set_find(head, name);
     84                        if (s) {
     85                                s->flags |= SET_LOCKED;
     86                        }
     87                }
    8288        }
    8389}
     
    105111{
    106112        struct xml_parsedata *xd = data;
    107         char *protocol, *handle, *server, *password = NULL, *autoconnect, *tag;
     113        char *protocol, *handle, *server, *password = NULL, *autoconnect, *tag, *locked;
    108114        char *pass_b64 = NULL;
    109115        unsigned char *pass_cr = NULL;
     
    118124        autoconnect = xt_find_attr(node, "autoconnect");
    119125        tag = xt_find_attr(node, "tag");
     126        locked = xt_find_attr(node, "locked");
    120127
    121128        protocol = xt_find_attr(node, "protocol");
     
    130137        if (!handle || !pass_b64 || !protocol || !prpl) {
    131138                return XT_ABORT;
    132         } else if ((pass_len = base64_decode(pass_b64, (unsigned char **) &pass_cr)) &&
    133                    arc_decode(pass_cr, pass_len, &password, xd->given_pass) >= 0) {
    134                 acc = account_add(xd->irc->b, prpl, handle, password);
    135                 if (server) {
    136                         set_setstr(&acc->set, "server", server);
    137                 }
    138                 if (autoconnect) {
    139                         set_setstr(&acc->set, "auto_connect", autoconnect);
    140                 }
    141                 if (tag) {
    142                         set_setstr(&acc->set, "tag", tag);
    143                 }
    144                 if (prpl == &protocol_missing) {
    145                         set_t *s = set_add(&acc->set, "_protocol_name", protocol, NULL, NULL);
    146                         s->flags |= SET_HIDDEN | SET_NOSAVE |
    147                                     ACC_SET_OFFLINE_ONLY | ACC_SET_ONLINE_ONLY;
    148                 }
     139        }
     140
     141        pass_len = base64_decode(pass_b64, (unsigned char **) &pass_cr);
     142        if (xd->irc->auth_backend) {
     143                password = g_strdup((char *)pass_cr);
    149144        } else {
    150                 g_free(pass_cr);
    151                 g_free(password);
    152                 return XT_ABORT;
     145                pass_len = arc_decode(pass_cr, pass_len, &password, xd->given_pass);
     146                if (pass_len < 0) {
     147                        g_free(pass_cr);
     148                        g_free(password);
     149                        return XT_ABORT;
     150                }
     151        }
     152
     153        acc = account_add(xd->irc->b, prpl, handle, password);
     154        if (server) {
     155                set_setstr(&acc->set, "server", server);
     156        }
     157        if (autoconnect) {
     158                set_setstr(&acc->set, "auto_connect", autoconnect);
     159        }
     160        if (tag) {
     161                set_setstr(&acc->set, "tag", tag);
     162        }
     163        if (locked && !g_strcasecmp(locked, "true")) {
     164                acc->flags |= ACC_FLAG_LOCKED;
     165        }
     166        if (prpl == &protocol_missing) {
     167                set_t *s = set_add(&acc->set, "_protocol_name", protocol, NULL, NULL);
     168                s->flags |= SET_HIDDEN | SET_NOSAVE |
     169                            ACC_SET_OFFLINE_ONLY | ACC_SET_ONLINE_ONLY;
    153170        }
    154171
     
    210227};
    211228
    212 static storage_status_t xml_load_real(irc_t *irc, const char *my_nick, const char *password, xml_pass_st action)
     229static storage_status_t xml_load_real(irc_t *irc, const char *my_nick, const char *password, xml_action action)
    213230{
    214231        struct xml_parsedata xd[1];
     
    252269        }
    253270
    254         {
     271        if (action == XML_PASS_CHECK) {
    255272                char *nick = xt_find_attr(node, "nick");
    256273                char *pass = xt_find_attr(node, "password");
    257 
    258                 if (!nick || !pass) {
     274                char *backend = xt_find_attr(node, "auth_backend");
     275
     276                if (!nick || !(pass || backend)) {
    259277                        goto error;
     278                }
     279
     280                if (backend) {
     281                        g_free(xd->irc->auth_backend);
     282                        xd->irc->auth_backend = g_strdup(backend);
     283                        ret = STORAGE_CHECK_BACKEND;
    260284                } else if ((st = md5_verify_password(xd->given_pass, pass)) != 0) {
    261285                        ret = STORAGE_INVALID_PASSWORD;
    262                         goto error;
    263                 }
    264         }
    265 
    266         if (action == XML_PASS_CHECK_ONLY) {
    267                 ret = STORAGE_OK;
    268                 goto error;
    269         }
    270 
    271         /* DO NOT call xt_handle() before verifying the password! */
     286                } else {
     287                        ret = STORAGE_OK;
     288                }
     289                goto error;
     290        }
     291
    272292        if (xt_handle(xp, NULL, 1) == XT_HANDLED) {
    273293                ret = STORAGE_OK;
     
    284304static storage_status_t xml_load(irc_t *irc, const char *password)
    285305{
    286         return xml_load_real(irc, irc->user->nick, password, XML_PASS_UNKNOWN);
    287 }
    288 
    289 static storage_status_t xml_check_pass(const char *my_nick, const char *password)
    290 {
    291         return xml_load_real(NULL, my_nick, password, XML_PASS_CHECK_ONLY);
     306        return xml_load_real(irc, irc->user->nick, password, XML_LOAD);
     307}
     308
     309static storage_status_t xml_check_pass(irc_t *irc, const char *my_nick, const char *password)
     310{
     311        return xml_load_real(irc, my_nick, password, XML_PASS_CHECK);
    292312}
    293313
     
    304324        struct xt_node *root, *cur;
    305325
    306         /* Generate a salted md5sum of the password. Use 5 bytes for the salt
    307            (to prevent dictionary lookups of passwords) to end up with a 21-
    308            byte password hash, more convenient for base64 encoding. */
    309         random_bytes(pass_md5 + 16, 5);
    310         md5_init(&md5_state);
    311         md5_append(&md5_state, (md5_byte_t *) irc->password, strlen(irc->password));
    312         md5_append(&md5_state, pass_md5 + 16, 5);   /* Add the salt. */
    313         md5_finish(&md5_state, pass_md5);
    314         /* Save the hash in base64-encoded form. */
    315         pass_buf = base64_encode(pass_md5, 21);
    316 
    317326        root = cur = xt_new_node("user", NULL, NULL);
     327        if (irc->auth_backend) {
     328                xt_add_attr(cur, "auth_backend", irc->auth_backend);
     329        } else {
     330                /* Generate a salted md5sum of the password. Use 5 bytes for the salt
     331                   (to prevent dictionary lookups of passwords) to end up with a 21-
     332                   byte password hash, more convenient for base64 encoding. */
     333                random_bytes(pass_md5 + 16, 5);
     334                md5_init(&md5_state);
     335                md5_append(&md5_state, (md5_byte_t *) irc->password, strlen(irc->password));
     336                md5_append(&md5_state, pass_md5 + 16, 5);   /* Add the salt. */
     337                md5_finish(&md5_state, pass_md5);
     338                /* Save the hash in base64-encoded form. */
     339                pass_buf = base64_encode(pass_md5, 21);
     340                xt_add_attr(cur, "password", pass_buf);
     341                g_free(pass_buf);
     342        }
     343
    318344        xt_add_attr(cur, "nick", irc->user->nick);
    319         xt_add_attr(cur, "password", pass_buf);
    320345        xt_add_attr(cur, "version", XML_FORMAT_VERSION);
    321 
    322         g_free(pass_buf);
    323346
    324347        xml_generate_settings(cur, &irc->b->set);
     
    331354                int pass_len;
    332355
    333                 pass_len = arc_encode(acc->pass, strlen(acc->pass), (unsigned char **) &pass_cr, irc->password, 12);
    334                 pass_b64 = base64_encode(pass_cr, pass_len);
    335                 g_free(pass_cr);
     356                if(irc->auth_backend) {
     357                        /* If we don't "own" the password, it may change without us
     358                         * knowing, so we cannot encrypt the data, as we then may not be
     359                         * able to decrypt it */
     360                        pass_b64 = base64_encode((unsigned char *)acc->pass, strlen(acc->pass));
     361                } else {
     362                        pass_len = arc_encode(acc->pass, strlen(acc->pass), (unsigned char **) &pass_cr, irc->password, 12);
     363                        pass_b64 = base64_encode(pass_cr, pass_len);
     364                        g_free(pass_cr);
     365                }
    336366
    337367                cur = xt_new_node("account", NULL, NULL);
     
    348378                        xt_add_attr(cur, "server", acc->server);
    349379                }
     380                if (acc->flags & ACC_FLAG_LOCKED) {
     381                        xt_add_attr(cur, "locked", "true");
     382                }
    350383
    351384                g_free(pass_b64);
     
    392425                        xt_add_child(cur, xset = xt_new_node("setting", set->value, NULL));
    393426                        xt_add_attr(xset, "name", set->key);
     427                        if (set->flags & SET_LOCKED) {
     428                                xt_add_attr(xset, "locked", "true");
     429                        }
    394430                }
    395431        }
     
    450486
    451487
    452 static storage_status_t xml_remove(const char *nick, const char *password)
     488static storage_status_t xml_remove(const char *nick)
    453489{
    454490        char s[512], *lc;
    455         storage_status_t status;
    456 
    457         status = xml_check_pass(nick, password);
    458         if (status != STORAGE_OK) {
    459                 return status;
    460         }
    461491
    462492        lc = g_strdup(nick);
  • tests/Makefile

    rba52ac5 r537d9b9  
    1515distclean: clean
    1616
    17 main_objs = bitlbee.o conf.o dcc.o help.o ipc.o irc.o irc_cap.o irc_channel.o irc_commands.o irc_im.o irc_send.o irc_user.o irc_util.o irc_commands.o log.o nick.o query.o root_commands.o set.o storage.o storage_xml.o
     17main_objs = bitlbee.o conf.o dcc.o help.o ipc.o irc.o irc_cap.o irc_channel.o irc_commands.o irc_im.o irc_send.o irc_user.o irc_util.o irc_commands.o log.o nick.o query.o root_commands.o set.o storage.o $(STORAGE_OBJS) auth.o $(AUTH_OBJS)
    1818
    1919test_objs = check.o check_util.o check_nick.o check_md5.o check_arc.o check_irc.o check_help.o check_user.o check_set.o check_jabber_sasl.o check_jabber_util.o
  • tests/check_jabber_util.c

    rba52ac5 r537d9b9  
    103103        fail_unless(jabber_compare_jid("bugtest@google.com/A", "bugtest@google.com/A"));
    104104        fail_if(jabber_compare_jid("", "bugtest@google.com/A"));
     105        fail_if(jabber_compare_jid(NULL, ""));
     106        fail_if(jabber_compare_jid("", NULL));
    105107}
    106108
  • tests/check_util.c

    rba52ac5 r537d9b9  
    137137                5,
    138138                "aaaaa\naaaaa\naaaaa\naaaaa\naaaaa\naaaaa\naaaaa\na",
     139        },
     140        {
     141                "áááááááááá",
     142                11,
     143                "ááááá\nááááá",
    139144        },
    140145        {
  • unix.c

    rba52ac5 r537d9b9  
    8080        }
    8181
     82        if (global.conf->runmode == RUNMODE_INETD) {
     83                log_link(LOGLVL_ERROR, LOGOUTPUT_IRC);
     84                log_link(LOGLVL_WARNING, LOGOUTPUT_IRC);
     85        } else {
     86                log_link(LOGLVL_ERROR, LOGOUTPUT_CONSOLE);
     87                log_link(LOGLVL_WARNING, LOGOUTPUT_CONSOLE);
     88        }
     89
    8290        b_main_init();
    8391
     
    104112        }
    105113
     114        global.auth = auth_init(global.conf->auth_backend);
     115        if (global.conf->auth_backend && global.auth == NULL) {
     116                log_message(LOGLVL_ERROR, "Unable to load authentication backend '%s'", global.conf->auth_backend);
     117                return(1);
     118        }
     119
    106120        if (global.conf->runmode == RUNMODE_INETD) {
    107                 log_link(LOGLVL_ERROR, LOGOUTPUT_IRC);
    108                 log_link(LOGLVL_WARNING, LOGOUTPUT_IRC);
    109 
    110121                i = bitlbee_inetd_init();
    111122                log_message(LOGLVL_INFO, "%s %s starting in inetd mode.", PACKAGE, BITLBEE_VERSION);
    112123
    113124        } else if (global.conf->runmode == RUNMODE_DAEMON) {
    114                 log_link(LOGLVL_ERROR, LOGOUTPUT_CONSOLE);
    115                 log_link(LOGLVL_WARNING, LOGOUTPUT_CONSOLE);
    116 
    117125                i = bitlbee_daemon_init();
    118126                log_message(LOGLVL_INFO, "%s %s starting in daemon mode.", PACKAGE, BITLBEE_VERSION);
    119127        } else if (global.conf->runmode == RUNMODE_FORKDAEMON) {
    120                 log_link(LOGLVL_ERROR, LOGOUTPUT_CONSOLE);
    121                 log_link(LOGLVL_WARNING, LOGOUTPUT_CONSOLE);
    122 
    123128                /* In case the operator requests a restart, we need this. */
    124129                old_cwd = g_malloc(256);
Note: See TracChangeset for help on using the changeset viewer.