diff options
author | Wilmer van der Gaast <wilmer@gaast.net> | 2010-10-01 22:34:53 -0700 |
---|---|---|
committer | Wilmer van der Gaast <wilmer@gaast.net> | 2010-10-01 22:34:53 -0700 |
commit | 62f53b508742804d5df6533150f17d41e6afcbb2 (patch) | |
tree | e01a02a2a730110dde6ded977458090859fe2115 | |
parent | 05bf2a0d55999c944ac6cf03ad85270cb2165923 (diff) | |
parent | 04cd284bce74c114fde3043c951a5c8ef9eb79ae (diff) |
Merging msnp13 branch which, confusingly, upgrades the msn module to use
MSNP15. (The reason for this is that A) IMHO MSNP13 is what causes most of
the pain in this upgade and B) I initially intended to only implement MSNP13
but then discovered MS doesn't support it anymore.)
This fixes issues with display names being forgotten, adding contacts (and
them automatically getting blocked sometimes!!), and adds support for
away/status messages and some support for sending offline messages.
-rwxr-xr-x | configure | 6 | ||||
-rw-r--r-- | doc/CHANGES | 5 | ||||
-rw-r--r-- | lib/Makefile | 2 | ||||
-rw-r--r-- | lib/des.c | 646 | ||||
-rw-r--r-- | lib/des.h | 51 | ||||
-rw-r--r-- | lib/misc.c | 7 | ||||
-rw-r--r-- | lib/sha1.c | 47 | ||||
-rw-r--r-- | lib/sha1.h | 1 | ||||
-rw-r--r-- | lib/ssl_client.h | 2 | ||||
-rw-r--r-- | lib/ssl_gnutls.c | 4 | ||||
-rw-r--r-- | lib/ssl_openssl.c | 30 | ||||
-rw-r--r-- | lib/xmltree.c | 106 | ||||
-rw-r--r-- | lib/xmltree.h | 5 | ||||
-rw-r--r-- | protocols/bee.h | 1 | ||||
-rw-r--r-- | protocols/bee_user.c | 29 | ||||
-rw-r--r-- | protocols/msn/Makefile | 2 | ||||
-rw-r--r-- | protocols/msn/msn.c | 129 | ||||
-rw-r--r-- | protocols/msn/msn.h | 122 | ||||
-rw-r--r-- | protocols/msn/msn_util.c | 291 | ||||
-rw-r--r-- | protocols/msn/ns.c | 640 | ||||
-rw-r--r-- | protocols/msn/passport.c | 172 | ||||
-rw-r--r-- | protocols/msn/passport.h | 113 | ||||
-rw-r--r-- | protocols/msn/sb.c | 94 | ||||
-rw-r--r-- | protocols/msn/soap.c | 1076 | ||||
-rw-r--r-- | protocols/msn/soap.h | 349 | ||||
-rw-r--r-- | protocols/msn/tables.c | 4 | ||||
-rw-r--r-- | protocols/nogaim.c | 18 | ||||
-rw-r--r-- | protocols/nogaim.h | 2 | ||||
-rw-r--r-- | storage_xml.c | 10 |
29 files changed, 3140 insertions, 824 deletions
@@ -422,6 +422,12 @@ if [ "$ret" = "0" ]; then exit 1 fi; +if [ "$msn" = "1" -a "$ssl" != "openssl" ]; then + # Needed for MSN only. OpenSSL exports nice cipher functions already, + # others don't, so use our own 3des code. + echo 'DES=des.o' >> Makefile.settings +fi + echo 'SSL_CLIENT=ssl_'$ssl'.o' >> Makefile.settings if detect_resolv_dynamic || detect_resolv_static; then diff --git a/doc/CHANGES b/doc/CHANGES index b0465199..cb071e4c 100644 --- a/doc/CHANGES +++ b/doc/CHANGES @@ -49,6 +49,11 @@ Version * Switching to libpurple should be pretty transparent. See "help purple" for a list of supported protocols (works only in libpurple-enabled binaries). +- Rewritten MSN module, implementing MSNP15 instead of the old MSNP8: + * MSNP8 support from MSN was getting pretty unreliable. There were issues + with remembering display names and adding contacts/auth requests. + * Support for sending offline messages. + * Support for setting and reading status messages. - Support for file transfers, in and out. /DCC SEND a file to a contact and it becomes a file transfer, and incoming file transfers become /DCC SENDs to you. Note that this is mostly useful when combined with libpurple, as diff --git a/lib/Makefile b/lib/Makefile index 0e528014..3ae43935 100644 --- a/lib/Makefile +++ b/lib/Makefile @@ -12,7 +12,7 @@ SRCDIR := $(SRCDIR)lib/ endif # [SH] Program variables -objects = arc.o base64.o $(EVENT_HANDLER) ftutil.o http_client.o ini.o md5.o misc.o oauth.o proxy.o sha1.o $(SSL_CLIENT) url.o xmltree.o +objects = arc.o base64.o $(DES) $(EVENT_HANDLER) ftutil.o http_client.o ini.o md5.o misc.o oauth.o proxy.o sha1.o $(SSL_CLIENT) url.o xmltree.o LFLAGS += -r diff --git a/lib/des.c b/lib/des.c new file mode 100644 index 00000000..3b9cc8d5 --- /dev/null +++ b/lib/des.c @@ -0,0 +1,646 @@ +/* + * FIPS-46-3 compliant 3DES implementation + * + * Copyright (C) 2001-2003 Christophe Devine + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA + */ + +/* + * Modified for BitlBee: Added a function compatible with the existing + * function in ssl_openssl.c, fairly specialised for MSN auth (since that's + * all this is used for at least for now). + * + * Added some consts to the tables at the top, and disabled some 64-bit + * and 128-bit key code that I don't need. + * + * *Many* thanks to Christophe for this compact and easy to import code. + */ + +#include <string.h> +#include <glib.h> +#include "des.h" + +/* the eight DES S-boxes */ + +static const uint32_t SB1[64] = +{ + 0x01010400, 0x00000000, 0x00010000, 0x01010404, + 0x01010004, 0x00010404, 0x00000004, 0x00010000, + 0x00000400, 0x01010400, 0x01010404, 0x00000400, + 0x01000404, 0x01010004, 0x01000000, 0x00000004, + 0x00000404, 0x01000400, 0x01000400, 0x00010400, + 0x00010400, 0x01010000, 0x01010000, 0x01000404, + 0x00010004, 0x01000004, 0x01000004, 0x00010004, + 0x00000000, 0x00000404, 0x00010404, 0x01000000, + 0x00010000, 0x01010404, 0x00000004, 0x01010000, + 0x01010400, 0x01000000, 0x01000000, 0x00000400, + 0x01010004, 0x00010000, 0x00010400, 0x01000004, + 0x00000400, 0x00000004, 0x01000404, 0x00010404, + 0x01010404, 0x00010004, 0x01010000, 0x01000404, + 0x01000004, 0x00000404, 0x00010404, 0x01010400, + 0x00000404, 0x01000400, 0x01000400, 0x00000000, + 0x00010004, 0x00010400, 0x00000000, 0x01010004 +}; + +static const uint32_t SB2[64] = +{ + 0x80108020, 0x80008000, 0x00008000, 0x00108020, + 0x00100000, 0x00000020, 0x80100020, 0x80008020, + 0x80000020, 0x80108020, 0x80108000, 0x80000000, + 0x80008000, 0x00100000, 0x00000020, 0x80100020, + 0x00108000, 0x00100020, 0x80008020, 0x00000000, + 0x80000000, 0x00008000, 0x00108020, 0x80100000, + 0x00100020, 0x80000020, 0x00000000, 0x00108000, + 0x00008020, 0x80108000, 0x80100000, 0x00008020, + 0x00000000, 0x00108020, 0x80100020, 0x00100000, + 0x80008020, 0x80100000, 0x80108000, 0x00008000, + 0x80100000, 0x80008000, 0x00000020, 0x80108020, + 0x00108020, 0x00000020, 0x00008000, 0x80000000, + 0x00008020, 0x80108000, 0x00100000, 0x80000020, + 0x00100020, 0x80008020, 0x80000020, 0x00100020, + 0x00108000, 0x00000000, 0x80008000, 0x00008020, + 0x80000000, 0x80100020, 0x80108020, 0x00108000 +}; + +static const uint32_t SB3[64] = +{ + 0x00000208, 0x08020200, 0x00000000, 0x08020008, + 0x08000200, 0x00000000, 0x00020208, 0x08000200, + 0x00020008, 0x08000008, 0x08000008, 0x00020000, + 0x08020208, 0x00020008, 0x08020000, 0x00000208, + 0x08000000, 0x00000008, 0x08020200, 0x00000200, + 0x00020200, 0x08020000, 0x08020008, 0x00020208, + 0x08000208, 0x00020200, 0x00020000, 0x08000208, + 0x00000008, 0x08020208, 0x00000200, 0x08000000, + 0x08020200, 0x08000000, 0x00020008, 0x00000208, + 0x00020000, 0x08020200, 0x08000200, 0x00000000, + 0x00000200, 0x00020008, 0x08020208, 0x08000200, + 0x08000008, 0x00000200, 0x00000000, 0x08020008, + 0x08000208, 0x00020000, 0x08000000, 0x08020208, + 0x00000008, 0x00020208, 0x00020200, 0x08000008, + 0x08020000, 0x08000208, 0x00000208, 0x08020000, + 0x00020208, 0x00000008, 0x08020008, 0x00020200 +}; + +static const uint32_t SB4[64] = +{ + 0x00802001, 0x00002081, 0x00002081, 0x00000080, + 0x00802080, 0x00800081, 0x00800001, 0x00002001, + 0x00000000, 0x00802000, 0x00802000, 0x00802081, + 0x00000081, 0x00000000, 0x00800080, 0x00800001, + 0x00000001, 0x00002000, 0x00800000, 0x00802001, + 0x00000080, 0x00800000, 0x00002001, 0x00002080, + 0x00800081, 0x00000001, 0x00002080, 0x00800080, + 0x00002000, 0x00802080, 0x00802081, 0x00000081, + 0x00800080, 0x00800001, 0x00802000, 0x00802081, + 0x00000081, 0x00000000, 0x00000000, 0x00802000, + 0x00002080, 0x00800080, 0x00800081, 0x00000001, + 0x00802001, 0x00002081, 0x00002081, 0x00000080, + 0x00802081, 0x00000081, 0x00000001, 0x00002000, + 0x00800001, 0x00002001, 0x00802080, 0x00800081, + 0x00002001, 0x00002080, 0x00800000, 0x00802001, + 0x00000080, 0x00800000, 0x00002000, 0x00802080 +}; + +static const uint32_t SB5[64] = +{ + 0x00000100, 0x02080100, 0x02080000, 0x42000100, + 0x00080000, 0x00000100, 0x40000000, 0x02080000, + 0x40080100, 0x00080000, 0x02000100, 0x40080100, + 0x42000100, 0x42080000, 0x00080100, 0x40000000, + 0x02000000, 0x40080000, 0x40080000, 0x00000000, + 0x40000100, 0x42080100, 0x42080100, 0x02000100, + 0x42080000, 0x40000100, 0x00000000, 0x42000000, + 0x02080100, 0x02000000, 0x42000000, 0x00080100, + 0x00080000, 0x42000100, 0x00000100, 0x02000000, + 0x40000000, 0x02080000, 0x42000100, 0x40080100, + 0x02000100, 0x40000000, 0x42080000, 0x02080100, + 0x40080100, 0x00000100, 0x02000000, 0x42080000, + 0x42080100, 0x00080100, 0x42000000, 0x42080100, + 0x02080000, 0x00000000, 0x40080000, 0x42000000, + 0x00080100, 0x02000100, 0x40000100, 0x00080000, + 0x00000000, 0x40080000, 0x02080100, 0x40000100 +}; + +static const uint32_t SB6[64] = +{ + 0x20000010, 0x20400000, 0x00004000, 0x20404010, + 0x20400000, 0x00000010, 0x20404010, 0x00400000, + 0x20004000, 0x00404010, 0x00400000, 0x20000010, + 0x00400010, 0x20004000, 0x20000000, 0x00004010, + 0x00000000, 0x00400010, 0x20004010, 0x00004000, + 0x00404000, 0x20004010, 0x00000010, 0x20400010, + 0x20400010, 0x00000000, 0x00404010, 0x20404000, + 0x00004010, 0x00404000, 0x20404000, 0x20000000, + 0x20004000, 0x00000010, 0x20400010, 0x00404000, + 0x20404010, 0x00400000, 0x00004010, 0x20000010, + 0x00400000, 0x20004000, 0x20000000, 0x00004010, + 0x20000010, 0x20404010, 0x00404000, 0x20400000, + 0x00404010, 0x20404000, 0x00000000, 0x20400010, + 0x00000010, 0x00004000, 0x20400000, 0x00404010, + 0x00004000, 0x00400010, 0x20004010, 0x00000000, + 0x20404000, 0x20000000, 0x00400010, 0x20004010 +}; + +static const uint32_t SB7[64] = +{ + 0x00200000, 0x04200002, 0x04000802, 0x00000000, + 0x00000800, 0x04000802, 0x00200802, 0x04200800, + 0x04200802, 0x00200000, 0x00000000, 0x04000002, + 0x00000002, 0x04000000, 0x04200002, 0x00000802, + 0x04000800, 0x00200802, 0x00200002, 0x04000800, + 0x04000002, 0x04200000, 0x04200800, 0x00200002, + 0x04200000, 0x00000800, 0x00000802, 0x04200802, + 0x00200800, 0x00000002, 0x04000000, 0x00200800, + 0x04000000, 0x00200800, 0x00200000, 0x04000802, + 0x04000802, 0x04200002, 0x04200002, 0x00000002, + 0x00200002, 0x04000000, 0x04000800, 0x00200000, + 0x04200800, 0x00000802, 0x00200802, 0x04200800, + 0x00000802, 0x04000002, 0x04200802, 0x04200000, + 0x00200800, 0x00000000, 0x00000002, 0x04200802, + 0x00000000, 0x00200802, 0x04200000, 0x00000800, + 0x04000002, 0x04000800, 0x00000800, 0x00200002 +}; + +static const uint32_t SB8[64] = +{ + 0x10001040, 0x00001000, 0x00040000, 0x10041040, + 0x10000000, 0x10001040, 0x00000040, 0x10000000, + 0x00040040, 0x10040000, 0x10041040, 0x00041000, + 0x10041000, 0x00041040, 0x00001000, 0x00000040, + 0x10040000, 0x10000040, 0x10001000, 0x00001040, + 0x00041000, 0x00040040, 0x10040040, 0x10041000, + 0x00001040, 0x00000000, 0x00000000, 0x10040040, + 0x10000040, 0x10001000, 0x00041040, 0x00040000, + 0x00041040, 0x00040000, 0x10041000, 0x00001000, + 0x00000040, 0x10040040, 0x00001000, 0x00041040, + 0x10001000, 0x00000040, 0x10000040, 0x10040000, + 0x10040040, 0x10000000, 0x00040000, 0x10001040, + 0x00000000, 0x10041040, 0x00040040, 0x10000040, + 0x10040000, 0x10001000, 0x10001040, 0x00000000, + 0x10041040, 0x00041000, 0x00041000, 0x00001040, + 0x00001040, 0x00040040, 0x10000000, 0x10041000 +}; + +/* PC1: left and right halves bit-swap */ + +static const uint32_t LHs[16] = +{ + 0x00000000, 0x00000001, 0x00000100, 0x00000101, + 0x00010000, 0x00010001, 0x00010100, 0x00010101, + 0x01000000, 0x01000001, 0x01000100, 0x01000101, + 0x01010000, 0x01010001, 0x01010100, 0x01010101 +}; + +static const uint32_t RHs[16] = +{ + 0x00000000, 0x01000000, 0x00010000, 0x01010000, + 0x00000100, 0x01000100, 0x00010100, 0x01010100, + 0x00000001, 0x01000001, 0x00010001, 0x01010001, + 0x00000101, 0x01000101, 0x00010101, 0x01010101, +}; + +/* platform-independant 32-bit integer manipulation macros */ + +#define GET_UINT32(n,b,i) \ +{ \ + (n) = ( (uint32_t) (b)[(i) ] << 24 ) \ + | ( (uint32_t) (b)[(i) + 1] << 16 ) \ + | ( (uint32_t) (b)[(i) + 2] << 8 ) \ + | ( (uint32_t) (b)[(i) + 3] ); \ +} + +#define PUT_UINT32(n,b,i) \ +{ \ + (b)[(i) ] = (uint8_t) ( (n) >> 24 ); \ + (b)[(i) + 1] = (uint8_t) ( (n) >> 16 ); \ + (b)[(i) + 2] = (uint8_t) ( (n) >> 8 ); \ + (b)[(i) + 3] = (uint8_t) ( (n) ); \ +} + +/* Initial Permutation macro */ + +#define DES_IP(X,Y) \ +{ \ + T = ((X >> 4) ^ Y) & 0x0F0F0F0F; Y ^= T; X ^= (T << 4); \ + T = ((X >> 16) ^ Y) & 0x0000FFFF; Y ^= T; X ^= (T << 16); \ + T = ((Y >> 2) ^ X) & 0x33333333; X ^= T; Y ^= (T << 2); \ + T = ((Y >> 8) ^ X) & 0x00FF00FF; X ^= T; Y ^= (T << 8); \ + Y = ((Y << 1) | (Y >> 31)) & 0xFFFFFFFF; \ + T = (X ^ Y) & 0xAAAAAAAA; Y ^= T; X ^= T; \ + X = ((X << 1) | (X >> 31)) & 0xFFFFFFFF; \ +} + +/* Final Permutation macro */ + +#define DES_FP(X,Y) \ +{ \ + X = ((X << 31) | (X >> 1)) & 0xFFFFFFFF; \ + T = (X ^ Y) & 0xAAAAAAAA; X ^= T; Y ^= T; \ + Y = ((Y << 31) | (Y >> 1)) & 0xFFFFFFFF; \ + T = ((Y >> 8) ^ X) & 0x00FF00FF; X ^= T; Y ^= (T << 8); \ + T = ((Y >> 2) ^ X) & 0x33333333; X ^= T; Y ^= (T << 2); \ + T = ((X >> 16) ^ Y) & 0x0000FFFF; Y ^= T; X ^= (T << 16); \ + T = ((X >> 4) ^ Y) & 0x0F0F0F0F; Y ^= T; X ^= (T << 4); \ +} + +/* DES round macro */ + +#define DES_ROUND(X,Y) \ +{ \ + T = *SK++ ^ X; \ + Y ^= SB8[ (T ) & 0x3F ] ^ \ + SB6[ (T >> 8) & 0x3F ] ^ \ + SB4[ (T >> 16) & 0x3F ] ^ \ + SB2[ (T >> 24) & 0x3F ]; \ + \ + T = *SK++ ^ ((X << 28) | (X >> 4)); \ + Y ^= SB7[ (T ) & 0x3F ] ^ \ + SB5[ (T >> 8) & 0x3F ] ^ \ + SB3[ (T >> 16) & 0x3F ] ^ \ + SB1[ (T >> 24) & 0x3F ]; \ +} + +/* DES key schedule */ + +int des_main_ks( uint32_t SK[32], const uint8_t key[8] ) +{ + int i; + uint32_t X, Y, T; + + GET_UINT32( X, key, 0 ); + GET_UINT32( Y, key, 4 ); + + /* Permuted Choice 1 */ + + T = ((Y >> 4) ^ X) & 0x0F0F0F0F; X ^= T; Y ^= (T << 4); + T = ((Y ) ^ X) & 0x10101010; X ^= T; Y ^= (T ); + + X = (LHs[ (X ) & 0xF] << 3) | (LHs[ (X >> 8) & 0xF ] << 2) + | (LHs[ (X >> 16) & 0xF] << 1) | (LHs[ (X >> 24) & 0xF ] ) + | (LHs[ (X >> 5) & 0xF] << 7) | (LHs[ (X >> 13) & 0xF ] << 6) + | (LHs[ (X >> 21) & 0xF] << 5) | (LHs[ (X >> 29) & 0xF ] << 4); + + Y = (RHs[ (Y >> 1) & 0xF] << 3) | (RHs[ (Y >> 9) & 0xF ] << 2) + | (RHs[ (Y >> 17) & 0xF] << 1) | (RHs[ (Y >> 25) & 0xF ] ) + | (RHs[ (Y >> 4) & 0xF] << 7) | (RHs[ (Y >> 12) & 0xF ] << 6) + | (RHs[ (Y >> 20) & 0xF] << 5) | (RHs[ (Y >> 28) & 0xF ] << 4); + + X &= 0x0FFFFFFF; + Y &= 0x0FFFFFFF; + + /* calculate subkeys */ + + for( i = 0; i < 16; i++ ) + { + if( i < 2 || i == 8 || i == 15 ) + { + X = ((X << 1) | (X >> 27)) & 0x0FFFFFFF; + Y = ((Y << 1) | (Y >> 27)) & 0x0FFFFFFF; + } + else + { + X = ((X << 2) | (X >> 26)) & 0x0FFFFFFF; + Y = ((Y << 2) | (Y >> 26)) & 0x0FFFFFFF; + } + + *SK++ = ((X << 4) & 0x24000000) | ((X << 28) & 0x10000000) + | ((X << 14) & 0x08000000) | ((X << 18) & 0x02080000) + | ((X << 6) & 0x01000000) | ((X << 9) & 0x00200000) + | ((X >> 1) & 0x00100000) | ((X << 10) & 0x00040000) + | ((X << 2) & 0x00020000) | ((X >> 10) & 0x00010000) + | ((Y >> 13) & 0x00002000) | ((Y >> 4) & 0x00001000) + | ((Y << 6) & 0x00000800) | ((Y >> 1) & 0x00000400) + | ((Y >> 14) & 0x00000200) | ((Y ) & 0x00000100) + | ((Y >> 5) & 0x00000020) | ((Y >> 10) & 0x00000010) + | ((Y >> 3) & 0x00000008) | ((Y >> 18) & 0x00000004) + | ((Y >> 26) & 0x00000002) | ((Y >> 24) & 0x00000001); + + *SK++ = ((X << 15) & 0x20000000) | ((X << 17) & 0x10000000) + | ((X << 10) & 0x08000000) | ((X << 22) & 0x04000000) + | ((X >> 2) & 0x02000000) | ((X << 1) & 0x01000000) + | ((X << 16) & 0x00200000) | ((X << 11) & 0x00100000) + | ((X << 3) & 0x00080000) | ((X >> 6) & 0x00040000) + | ((X << 15) & 0x00020000) | ((X >> 4) & 0x00010000) + | ((Y >> 2) & 0x00002000) | ((Y << 8) & 0x00001000) + | ((Y >> 14) & 0x00000808) | ((Y >> 9) & 0x00000400) + | ((Y ) & 0x00000200) | ((Y << 7) & 0x00000100) + | ((Y >> 7) & 0x00000020) | ((Y >> 3) & 0x00000011) + | ((Y << 2) & 0x00000004) | ((Y >> 21) & 0x00000002); + } + + return( 0 ); +} + +#if TEST +int des_set_key( des_context *ctx, uint8_t key[8] ) +{ + int i; + + /* setup encryption subkeys */ + + des_main_ks( ctx->esk, key ); + + /* setup decryption subkeys */ + + for( i = 0; i < 32; i += 2 ) + { + ctx->dsk[i ] = ctx->esk[30 - i]; + ctx->dsk[i + 1] = ctx->esk[31 - i]; + } + + return( 0 ); +} + +/* DES 64-bit block encryption/decryption */ + +void des_crypt( uint32_t SK[32], uint8_t input[8], uint8_t output[8] ) +{ + uint32_t X, Y, T; + + GET_UINT32( X, input, 0 ); + GET_UINT32( Y, input, 4 ); + + DES_IP( X, Y ); + + DES_ROUND( Y, X ); DES_ROUND( X, Y ); + DES_ROUND( Y, X ); DES_ROUND( X, Y ); + DES_ROUND( Y, X ); DES_ROUND( X, Y ); + DES_ROUND( Y, X ); DES_ROUND( X, Y ); + DES_ROUND( Y, X ); DES_ROUND( X, Y ); + DES_ROUND( Y, X ); DES_ROUND( X, Y ); + DES_ROUND( Y, X ); DES_ROUND( X, Y ); + DES_ROUND( Y, X ); DES_ROUND( X, Y ); + + DES_FP( Y, X ); + + PUT_UINT32( Y, output, 0 ); + PUT_UINT32( X, output, 4 ); +} + +void des_encrypt( des_context *ctx, uint8_t input[8], uint8_t output[8] ) +{ + des_crypt( ctx->esk, input, output ); +} + +void des_decrypt( des_context *ctx, uint8_t input[8], uint8_t output[8] ) +{ + des_crypt( ctx->dsk, input, output ); +} + +/* Triple-DES key schedule */ + +int des3_set_2keys( des3_context *ctx, const uint8_t key1[8], const uint8_t key2[8] ) +{ + int i; + + des_main_ks( ctx->esk , key1 ); + des_main_ks( ctx->dsk + 32, key2 ); + + for( i = 0; i < 32; i += 2 ) + { + ctx->dsk[i ] = ctx->esk[30 - i]; + ctx->dsk[i + 1] = ctx->esk[31 - i]; + + ctx->esk[i + 32] = ctx->dsk[62 - i]; + ctx->esk[i + 33] = ctx->dsk[63 - i]; + + ctx->esk[i + 64] = ctx->esk[ i]; + ctx->esk[i + 65] = ctx->esk[ 1 + i]; + + ctx->dsk[i + 64] = ctx->dsk[ i]; + ctx->dsk[i + 65] = ctx->dsk[ 1 + i]; + } + + return( 0 ); +} +#endif + +int des3_set_3keys( des3_context *ctx, const uint8_t key1[8], const uint8_t key2[8], + const uint8_t key3[8] ) +{ + int i; + + des_main_ks( ctx->esk , key1 ); + des_main_ks( ctx->dsk + 32, key2 ); + des_main_ks( ctx->esk + 64, key3 ); + + for( i = 0; i < 32; i += 2 ) + { + ctx->dsk[i ] = ctx->esk[94 - i]; + ctx->dsk[i + 1] = ctx->esk[95 - i]; + + ctx->esk[i + 32] = ctx->dsk[62 - i]; + ctx->esk[i + 33] = ctx->dsk[63 - i]; + + ctx->dsk[i + 64] = ctx->esk[30 - i]; + ctx->dsk[i + 65] = ctx->esk[31 - i]; + } + + return( 0 ); +} + +/* Triple-DES 64-bit block encryption/decryption */ + +void des3_crypt( uint32_t SK[96], uint8_t input[8], uint8_t output[8] ) +{ + uint32_t X, Y, T; + + GET_UINT32( X, input, 0 ); + GET_UINT32( Y, input, 4 ); + + DES_IP( X, Y ); + + DES_ROUND( Y, X ); DES_ROUND( X, Y ); + DES_ROUND( Y, X ); DES_ROUND( X, Y ); + DES_ROUND( Y, X ); DES_ROUND( X, Y ); + DES_ROUND( Y, X ); DES_ROUND( X, Y ); + DES_ROUND( Y, X ); DES_ROUND( X, Y ); + DES_ROUND( Y, X ); DES_ROUND( X, Y ); + DES_ROUND( Y, X ); DES_ROUND( X, Y ); + DES_ROUND( Y, X ); DES_ROUND( X, Y ); + + DES_ROUND( X, Y ); DES_ROUND( Y, X ); + DES_ROUND( X, Y ); DES_ROUND( Y, X ); + DES_ROUND( X, Y ); DES_ROUND( Y, X ); + DES_ROUND( X, Y ); DES_ROUND( Y, X ); + DES_ROUND( X, Y ); DES_ROUND( Y, X ); + DES_ROUND( X, Y ); DES_ROUND( Y, X ); + DES_ROUND( X, Y ); DES_ROUND( Y, X ); + DES_ROUND( X, Y ); DES_ROUND( Y, X ); + + DES_ROUND( Y, X ); DES_ROUND( X, Y ); + DES_ROUND( Y, X ); DES_ROUND( X, Y ); + DES_ROUND( Y, X ); DES_ROUND( X, Y ); + DES_ROUND( Y, X ); DES_ROUND( X, Y ); + DES_ROUND( Y, X ); DES_ROUND( X, Y ); + DES_ROUND( Y, X ); DES_ROUND( X, Y ); + DES_ROUND( Y, X ); DES_ROUND( X, Y ); + DES_ROUND( Y, X ); DES_ROUND( X, Y ); + + DES_FP( Y, X ); + + PUT_UINT32( Y, output, 0 ); + PUT_UINT32( X, output, 4 ); +} + +void des3_encrypt( des3_context *ctx, uint8_t input[8], uint8_t output[8] ) +{ + des3_crypt( ctx->esk, input, output ); +} + +void des3_decrypt( des3_context *ctx, uint8_t input[8], uint8_t output[8] ) +{ + des3_crypt( ctx->dsk, input, output ); +} + +size_t ssl_des3_encrypt( const unsigned char *key, size_t key_len, const unsigned char *input, + size_t input_len, const unsigned char *iv, unsigned char **res ) +{ + des3_context ctx3; + size_t off; + uint8_t buf[8]; + + /* Keep it simple, for as long as this is just used for MSN auth anyway. */ + if( key_len != 24 || ( input_len % 8 ) != 0 ) + return 0; + + *res = g_malloc( input_len ); + des3_set_3keys( &ctx3, key, key + 8, key + 16 ); + + /* This loop does CBC 3DES. */ + memcpy( buf, iv, 8 ); + for( off = 0; off < input_len; off += 8 ) + { + int i; + + for( i = 0; i < 8; i ++ ) + buf[i] ^= input[off+i]; + des3_encrypt( &ctx3, buf, buf ); + memcpy( *res + off, buf, 8 ); + } + + return input_len; +} + +#ifdef TEST + +#include <string.h> +#include <stdio.h> + +/* + * Triple-DES Monte Carlo Test: ECB mode + * source: NIST - tripledes-vectors.zip + */ + +static const unsigned char DES3_keys[3][8] = +{ + { 0x01, 0x23, 0x45, 0x67, 0x89, 0xAB, 0xCD, 0xEF }, + { 0x23, 0x45, 0x67, 0x89, 0xAB, 0xCD, 0xEF, 0x01 }, + { 0x45, 0x67, 0x89, 0xAB, 0xCD, 0xEF, 0x01, 0x23 } +}; + +static const unsigned char DES3_init[8] = +{ + 0x4E, 0x6F, 0x77, 0x20, 0x69, 0x73, 0x20, 0x74 +}; + +static const unsigned char DES3_enc_test[3][8] = +{ + { 0x6A, 0x2A, 0x19, 0xF4, 0x1E, 0xCA, 0x85, 0x4B }, + { 0x03, 0xE6, 0x9F, 0x5B, 0xFA, 0x58, 0xEB, 0x42 }, + { 0xDD, 0x17, 0xE8, 0xB8, 0xB4, 0x37, 0xD2, 0x32 } +}; + +static const unsigned char DES3_dec_test[3][8] = +{ + { 0xCD, 0xD6, 0x4F, 0x2F, 0x94, 0x27, 0xC1, 0x5D }, + { 0x69, 0x96, 0xC8, 0xFA, 0x47, 0xA2, 0xAB, 0xEB }, + { 0x83, 0x25, 0x39, 0x76, 0x44, 0x09, 0x1A, 0x0A } +}; + +int main( void ) +{ + int m, n, i; + des_context ctx; + des3_context ctx3; + unsigned char buf[8]; + + for( m = 0; m < 2; m++ ) + { + printf( "\n Triple-DES Monte Carlo Test (ECB mode) - " ); + + if( m == 0 ) printf( "encryption\n\n" ); + if( m == 1 ) printf( "decryption\n\n" ); + + for( n = 0; n < 3; n++ ) + { + printf( " Test %d, key size = %3d bits: ", + n + 1, 64 + n * 64 ); + + fflush( stdout ); + + memcpy( buf, DES3_init, 8 ); + + switch( n ) + { + case 0: + des_set_key( &ctx, DES3_keys[0] ); + break; + + case 1: + des3_set_2keys( &ctx3, DES3_keys[0], + DES3_keys[1] ); + break; + + case 2: + des3_set_3keys( &ctx3, DES3_keys[0], + DES3_keys[1], + DES3_keys[2] ); + break; + } + + for( i = 0; i < 10000; i++ ) + { + if( n == 0 ) + { + if( m == 0 ) des_encrypt( &ctx, buf, buf ); + if( m == 1 ) des_decrypt( &ctx, buf, buf ); + } + else + { + if( m == 0 ) des3_encrypt( &ctx3, buf, buf ); + if( m == 1 ) des3_decrypt( &ctx3, buf, buf ); + } + } + + if( ( m == 0 && memcmp( buf, DES3_enc_test[n], 8 ) ) || + ( m == 1 && memcmp( buf, DES3_dec_test[n], 8 ) ) ) + { + printf( "failed!\n" ); + return( 1 ); + } + + printf( "passed.\n" ); + } + } + + printf( "\n" ); + + return( 0 ); +} + +#endif diff --git a/lib/des.h b/lib/des.h new file mode 100644 index 00000000..92fbfd22 --- /dev/null +++ b/lib/des.h @@ -0,0 +1,51 @@ +/* + * FIPS-46-3 compliant 3DES implementation + * + * Copyright (C) 2001-2003 Christophe Devine + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA + */ + +#ifndef _DES_H +#define _DES_H + +#include <stdint.h> + +typedef struct +{ + uint32_t esk[32]; /* DES encryption subkeys */ + uint32_t dsk[32]; /* DES decryption subkeys */ +} +des_context; + +typedef struct +{ + uint32_t esk[96]; /* Triple-DES encryption subkeys */ + uint32_t dsk[96]; /* Triple-DES decryption subkeys */ +} +des3_context; + +int des_set_key( des_context *ctx, uint8_t key[8] ); +void des_encrypt( des_context *ctx, uint8_t input[8], uint8_t output[8] ); +void des_decrypt( des_context *ctx, uint8_t input[8], uint8_t output[8] ); + +int des3_set_2keys( des3_context *ctx, const uint8_t key1[8], const uint8_t key2[8] ); +int des3_set_3keys( des3_context *ctx, const uint8_t key1[8], const uint8_t key2[8], + const uint8_t key3[8] ); + +void des3_encrypt( des3_context *ctx, uint8_t input[8], uint8_t output[8] ); +void des3_decrypt( des3_context *ctx, uint8_t input[8], uint8_t output[8] ); + +#endif /* des.h */ @@ -297,11 +297,10 @@ void http_decode( char *s ) /* This fuction is safe, but make sure you call it safely as well! */ void http_encode( char *s ) { - char *t; + char t[strlen(s)+1]; int i, j; - t = g_strdup( s ); - + strcpy( t, s ); for( i = j = 0; t[i]; i ++, j ++ ) { /* Warning: isalnum() is locale-aware, so don't use it here! */ @@ -319,8 +318,6 @@ void http_encode( char *s ) } } s[j] = 0; - - g_free( t ); } /* Strip newlines from a string. Modifies the string passed to it. */ @@ -35,6 +35,7 @@ * */ +#include <string.h> #include "sha1.h" /* @@ -373,3 +374,49 @@ static void sha1_pad(sha1_state_t * context) sha1_process_block(context); } + +#define HMAC_BLOCK_SIZE 64 + +/* BitlBee addition: */ +void sha1_hmac(const char *key_, size_t key_len, const char *payload, size_t payload_len, uint8_t Message_Digest[sha1_hash_size]) +{ + sha1_state_t sha1; + uint8_t hash[sha1_hash_size]; + uint8_t key[HMAC_BLOCK_SIZE+1]; + int i; + + if( key_len == 0 ) + key_len = strlen( key_ ); + if( payload_len == 0 ) + payload_len = strlen( payload ); + + /* Create K. If our current key is >64 chars we have to hash it, + otherwise just pad. */ + memset( key, 0, HMAC_BLOCK_SIZE + 1 ); + if( key_len > HMAC_BLOCK_SIZE ) + { + sha1_init( &sha1 ); + sha1_append( &sha1, (uint8_t*) key_, key_len ); + sha1_finish( &sha1, key ); + } + else + { + memcpy( key, key_, key_len ); + } + + /* Inner part: H(K XOR 0x36, text) */ + sha1_init( &sha1 ); + for( i = 0; i < HMAC_BLOCK_SIZE; i ++ ) + key[i] ^= 0x36; + sha1_append( &sha1, key, HMAC_BLOCK_SIZE ); + sha1_append( &sha1, (const uint8_t*) payload, payload_len ); + sha1_finish( &sha1, hash ); + + /* Final result: H(K XOR 0x5C, inner stuff) */ + sha1_init( &sha1 ); + for( i = 0; i < HMAC_BLOCK_SIZE; i ++ ) + key[i] ^= 0x36 ^ 0x5c; + sha1_append( &sha1, key, HMAC_BLOCK_SIZE ); + sha1_append( &sha1, hash, sha1_hash_size ); + sha1_finish( &sha1, Message_Digest ); +} @@ -66,5 +66,6 @@ typedef struct SHA1Context { G_MODULE_EXPORT int sha1_init(sha1_state_t *); G_MODULE_EXPORT int sha1_append(sha1_state_t *, const uint8_t *, unsigned int); G_MODULE_EXPORT int sha1_finish(sha1_state_t *, uint8_t Message_Digest[sha1_hash_size]); +G_MODULE_EXPORT void sha1_hmac(const char *key_, size_t key_len, const char *payload, size_t payload_len, uint8_t Message_Digest[sha1_hash_size]); #endif diff --git a/lib/ssl_client.h b/lib/ssl_client.h index 0a8e82d8..787d528a 100644 --- a/lib/ssl_client.h +++ b/lib/ssl_client.h @@ -77,3 +77,5 @@ G_MODULE_EXPORT int ssl_getfd( void *conn ); adding an event handler to the queue. (And it should perform exactly the same action as the handler that just received the SSL_AGAIN.) */ G_MODULE_EXPORT b_input_condition ssl_getdirection( void *conn ); + +G_MODULE_EXPORT size_t ssl_des3_encrypt(const unsigned char *key, size_t key_len, const unsigned char *input, size_t input_len, const unsigned char *iv, unsigned char **res); diff --git a/lib/ssl_gnutls.c b/lib/ssl_gnutls.c index 5a14b825..4fc7c33f 100644 --- a/lib/ssl_gnutls.c +++ b/lib/ssl_gnutls.c @@ -188,6 +188,8 @@ int ssl_read( void *conn, char *buf, int len ) if( st == GNUTLS_E_AGAIN || st == GNUTLS_E_INTERRUPTED ) ssl_errno = SSL_AGAIN; + if( 0 && getenv( "BITLBEE_DEBUG" ) && st > 0 ) write( 1, buf, st ); + return st; } @@ -207,6 +209,8 @@ int ssl_write( void *conn, const char *buf, int len ) if( st == GNUTLS_E_AGAIN || st == GNUTLS_E_INTERRUPTED ) ssl_errno = SSL_AGAIN; + if( 0 && getenv( "BITLBEE_DEBUG" ) && st > 0 ) write( 1, buf, st ); + return st; } diff --git a/lib/ssl_openssl.c b/lib/ssl_openssl.c index 8abff390..e0143791 100644 --- a/lib/ssl_openssl.c +++ b/lib/ssl_openssl.c @@ -115,7 +115,9 @@ static gboolean ssl_connected( gpointer data, gint source, b_input_condition con if( !initialized ) { initialized = TRUE; - SSLeay_add_ssl_algorithms(); + SSL_library_init(); + //SSLeay_add_ssl_algorithms(); + //OpenSSL_add_all_algorithms(); } meth = TLSv1_client_method(); @@ -204,6 +206,8 @@ int ssl_read( void *conn, char *buf, int len ) ssl_errno = SSL_AGAIN; } + if( 0 && getenv( "BITLBEE_DEBUG" ) && st > 0 ) write( 1, buf, st ); + return st; } @@ -219,6 +223,8 @@ int ssl_write( void *conn, const char *buf, int len ) st = SSL_write( ((struct scd*)conn)->ssl, buf, len ); + if( 0 && getenv( "BITLBEE_DEBUG" ) && st > 0 ) write( 1, buf, st ); + ssl_errno = SSL_OK; if( st <= 0 ) { @@ -271,3 +277,25 @@ b_input_condition ssl_getdirection( void *conn ) { return( ((struct scd*)conn)->lasterr == SSL_ERROR_WANT_WRITE ? B_EV_IO_WRITE : B_EV_IO_READ ); } + +size_t ssl_des3_encrypt(const unsigned char *key, size_t key_len, const unsigned char *input, size_t input_len, const unsigned char *iv, unsigned char **res) +{ + int output_length = 0; + EVP_CIPHER_CTX ctx; + + *res = g_new0(unsigned char, 72); + + /* Don't set key or IV because we will modify the parameters */ + EVP_CIPHER_CTX_init(&ctx); + EVP_CipherInit_ex(&ctx, EVP_des_ede3_cbc(), NULL, NULL, NULL, 1); + EVP_CIPHER_CTX_set_key_length(&ctx, key_len); + EVP_CIPHER_CTX_set_padding(&ctx, 0); + /* We finished modifying parameters so now we can set key and IV */ + EVP_CipherInit_ex(&ctx, NULL, NULL, key, iv, 1); + EVP_CipherUpdate(&ctx, *res, &output_length, input, input_len); + EVP_CipherFinal_ex(&ctx, *res, &output_length); + EVP_CIPHER_CTX_cleanup(&ctx); + //EVP_cleanup(); + + return output_length; +} diff --git a/lib/xmltree.c b/lib/xmltree.c index b0a945ce..bee9007f 100644 --- a/lib/xmltree.c +++ b/lib/xmltree.c @@ -140,7 +140,7 @@ void xt_reset( struct xt_parser *xt ) /* Feed the parser, don't execute any handler. Returns -1 on errors, 0 on end-of-stream and 1 otherwise. */ -int xt_feed( struct xt_parser *xt, char *text, int text_len ) +int xt_feed( struct xt_parser *xt, const char *text, int text_len ) { if( !g_markup_parse_context_parse( xt->parser, text, text_len, &xt->gerr ) ) { @@ -173,20 +173,20 @@ int xt_handle( struct xt_parser *xt, struct xt_node *node, int depth ) if( node->flags & XT_COMPLETE && !( node->flags & XT_SEEN ) ) { - for( i = 0; xt->handlers[i].func; i ++ ) + if( xt->handlers ) for( i = 0; xt->handlers[i].func; i ++ ) { /* This one is fun! \o/ */ - /* If handler.name == NULL it means it should always match. */ + /* If handler.name == NULL it means it should always match. */ if( ( xt->handlers[i].name == NULL || - /* If it's not, compare. There should always be a name. */ + /* If it's not, compare. There should always be a name. */ g_strcasecmp( xt->handlers[i].name, node->name ) == 0 ) && - /* If handler.parent == NULL, it's a match. */ + /* If handler.parent == NULL, it's a match. */ ( xt->handlers[i].parent == NULL || - /* If there's a parent node, see if the name matches. */ + /* If there's a parent node, see if the name matches. */ ( node->parent ? g_strcasecmp( xt->handlers[i].parent, node->parent->name ) == 0 : - /* If there's no parent, the handler should mention <root> as a parent. */ - g_strcasecmp( xt->handlers[i].parent, "<root>" ) == 0 ) ) ) + /* If there's no parent, the handler should mention <root> as a parent. */ + strcmp( xt->handlers[i].parent, "<root>" ) == 0 ) ) ) { st = xt->handlers[i].func( node, xt->data ); @@ -259,6 +259,20 @@ void xt_cleanup( struct xt_parser *xt, struct xt_node *node, int depth ) } } +struct xt_node *xt_from_string( const char *in ) +{ + struct xt_parser *parser; + struct xt_node *ret; + + parser = xt_new( NULL, NULL ); + xt_feed( parser, in, strlen( in ) ); + ret = parser->root; + parser->root = NULL; + xt_free( parser ); + + return ret; +} + static void xt_to_string_real( struct xt_node *node, GString *str ) { char *buf; @@ -316,14 +330,18 @@ void xt_print( struct xt_node *node ) /* Indentation */ for( c = node; c->parent; c = c->parent ) - printf( "\t" ); + printf( " " ); /* Start the tag */ printf( "<%s", node->name ); /* Print the attributes */ for( i = 0; node->attr[i].key; i ++ ) - printf( " %s=\"%s\"", node->attr[i].key, g_markup_escape_text( node->attr[i].value, -1 ) ); + { + char *v = g_markup_escape_text( node->attr[i].value, -1 ); + printf( " %s=\"%s\"", node->attr[i].key, v ); + g_free( v ); + } /* /> in case there's really *nothing* inside this tag, otherwise just >. */ @@ -343,7 +361,11 @@ void xt_print( struct xt_node *node ) { for( i = 0; node->text[i] && isspace( node->text[i] ); i ++ ); if( node->text[i] ) - printf( "%s", g_markup_escape_text( node->text, -1 ) ); + { + char *v = g_markup_escape_text( node->text, -1 ); + printf( "%s", v ); + g_free( v ); + } } if( node->children ) @@ -354,7 +376,7 @@ void xt_print( struct xt_node *node ) if( node->children ) for( c = node; c->parent; c = c->parent ) - printf( "\t" ); + printf( " " ); /* Non-empty tag is now finished. */ printf( "</%s>\n", node->name ); @@ -464,6 +486,46 @@ struct xt_node *xt_find_node( struct xt_node *node, const char *name ) return node; } +/* More advanced than the one above, understands something like + ../foo/bar to find a subnode bar of a node foo which is a child + of node's parent. Pass the node directly, not its list of children. */ +struct xt_node *xt_find_path( struct xt_node *node, const char *name ) +{ + while( name && *name && node ) + { + char *colon, *slash; + int n; + + if( ( slash = strchr( name, '/' ) ) ) + n = slash - name; + else + n = strlen( name ); + + if( strncmp( name, "..", n ) == 0 ) + { + node = node->parent; + } + else + { + node = node->children; + + while( node ) + { + if( g_strncasecmp( node->name, name, n ) == 0 || + ( ( colon = strchr( node->name, ':' ) ) && + g_strncasecmp( colon + 1, name, n ) == 0 ) ) + break; + + node = node->next; + } + } + + name = slash ? slash + 1 : NULL; + } + + return node; +} + char *xt_find_attr( struct xt_node *node, const char *key ) { int i; @@ -549,6 +611,26 @@ void xt_add_child( struct xt_node *parent, struct xt_node *child ) } } +/* Same, but at the beginning. */ +void xt_insert_child( struct xt_node *parent, struct xt_node *child ) +{ + struct xt_node *node, *last; + + for( node = child; node; node = node->next ) + { + if( node->parent != NULL ) + { + /* ERROR CONDITION: They seem to have a parent already??? */ + } + + node->parent = parent; + last = node; + } + + last->next = parent->children; + parent->children = child; +} + void xt_add_attr( struct xt_node *node, const char *key, const char *value ) { int i; diff --git a/lib/xmltree.h b/lib/xmltree.h index 34e3be68..5a0dbc8e 100644 --- a/lib/xmltree.h +++ b/lib/xmltree.h @@ -78,19 +78,22 @@ struct xt_parser struct xt_parser *xt_new( const struct xt_handler_entry *handlers, gpointer data ); void xt_reset( struct xt_parser *xt ); -int xt_feed( struct xt_parser *xt, char *text, int text_len ); +int xt_feed( struct xt_parser *xt, const char *text, int text_len ); int xt_handle( struct xt_parser *xt, struct xt_node *node, int depth ); void xt_cleanup( struct xt_parser *xt, struct xt_node *node, int depth ); +struct xt_node *xt_from_string( const char *in ); char *xt_to_string( struct xt_node *node ); void xt_print( struct xt_node *node ); struct xt_node *xt_dup( struct xt_node *node ); void xt_free_node( struct xt_node *node ); void xt_free( struct xt_parser *xt ); struct xt_node *xt_find_node( struct xt_node *node, const char *name ); +struct xt_node *xt_find_path( struct xt_node *node, const char *name ); char *xt_find_attr( struct xt_node *node, const char *key ); struct xt_node *xt_new_node( char *name, const char *text, struct xt_node *children ); void xt_add_child( struct xt_node *parent, struct xt_node *child ); +void xt_insert_child( struct xt_node *parent, struct xt_node *child ); void xt_add_attr( struct xt_node *node, const char *key, const char *value ); int xt_remove_attr( struct xt_node *node, const char *key ); diff --git a/protocols/bee.h b/protocols/bee.h index b99c8de7..077c3661 100644 --- a/protocols/bee.h +++ b/protocols/bee.h @@ -150,6 +150,7 @@ void bee_group_free( bee_t *bee ); * OPT_LOGGED_IN and OPT_AWAY. * - 'state' and 'message' can be NULL */ G_MODULE_EXPORT void imcb_buddy_status( struct im_connection *ic, const char *handle, int flags, const char *state, const char *message ); +G_MODULE_EXPORT void imcb_buddy_status_msg( struct im_connection *ic, const char *handle, const char *message ); G_MODULE_EXPORT void imcb_buddy_times( struct im_connection *ic, const char *handle, time_t login, time_t idle ); /* Call when a handle says something. 'flags' and 'sent_at may be just 0. */ G_MODULE_EXPORT void imcb_buddy_msg( struct im_connection *ic, const char *handle, char *msg, guint32 flags, time_t sent_at ); diff --git a/protocols/bee_user.c b/protocols/bee_user.c index 04253c47..1f9b1b47 100644 --- a/protocols/bee_user.c +++ b/protocols/bee_user.c @@ -186,8 +186,13 @@ void imcb_buddy_status( struct im_connection *ic, const char *handle, int flags, /* TODO(wilmer): OPT_AWAY, or just state == NULL ? */ bu->flags = flags; - bu->status = g_strdup( ( flags & OPT_AWAY ) && state == NULL ? "Away" : state ); bu->status_msg = g_strdup( message ); + if( state && *state ) + bu->status = g_strdup( state ); + else if( flags & OPT_AWAY ) + bu->status = g_strdup( "Away" ); + else + bu->status = NULL; if( bu->status == NULL && ( flags & OPT_MOBILE ) && set_getbool( &bee->set, "mobile_is_away" ) ) @@ -204,6 +209,28 @@ void imcb_buddy_status( struct im_connection *ic, const char *handle, int flags, g_free( old ); } +/* Same, but only change the away/status message, not any away/online state info. */ +void imcb_buddy_status_msg( struct im_connection *ic, const char *handle, const char *message ) +{ + bee_t *bee = ic->bee; + bee_user_t *bu, *old; + + if( !( bu = bee_user_by_handle( bee, ic, handle ) ) ) + { + return; + } + + old = g_memdup( bu, sizeof( bee_user_t ) ); + + bu->status_msg = message && *message ? g_strdup( message ) : NULL; + + if( bee->ui->user_status ) + bee->ui->user_status( bee, bu, old ); + + g_free( old->status_msg ); + g_free( old ); +} + void imcb_buddy_times( struct im_connection *ic, const char *handle, time_t login, time_t idle ) { bee_t *bee = ic->bee; diff --git a/protocols/msn/Makefile b/protocols/msn/Makefile index 068d7e98..8845d41b 100644 --- a/protocols/msn/Makefile +++ b/protocols/msn/Makefile @@ -12,7 +12,7 @@ SRCDIR := $(SRCDIR)protocols/msn/ endif # [SH] Program variables -objects = msn.o msn_util.o ns.o passport.o sb.o tables.o +objects = msn.o msn_util.o ns.o sb.o soap.o tables.o LFLAGS += -r diff --git a/protocols/msn/msn.c b/protocols/msn/msn.c index 60d58532..1584efa4 100644 --- a/protocols/msn/msn.c +++ b/protocols/msn/msn.c @@ -1,7 +1,7 @@ /********************************************************************\ * BitlBee -- An IRC to other IM-networks gateway * * * - * Copyright 2002-2004 Wilmer van der Gaast and others * + * Copyright 2002-2010 Wilmer van der Gaast and others * \********************************************************************/ /* MSN module - Main file; functions to be called from BitlBee */ @@ -24,6 +24,7 @@ */ #include "nogaim.h" +#include "soap.h" #include "msn.h" int msn_chat_id; @@ -34,10 +35,15 @@ static char *set_eval_display_name( set_t *set, char *value ); static void msn_init( account_t *acc ) { - set_add( &acc->set, "display_name", NULL, set_eval_display_name, acc ); - set_add( &acc->set, "local_display_name", "false", set_eval_bool, acc ); + set_t *s; + + s = set_add( &acc->set, "display_name", NULL, set_eval_display_name, acc ); + s->flags |= ACC_SET_NOSAVE | ACC_SET_ONLINE_ONLY; + set_add( &acc->set, "mail_notifications", "false", set_eval_bool, acc ); set_add( &acc->set, "switchboard_keepalives", "false", set_eval_bool, acc ); + + acc->flags |= ACC_FLAG_AWAY_MESSAGE | ACC_FLAG_STATUS_MESSAGE; } static void msn_login( account_t *acc ) @@ -46,7 +52,6 @@ static void msn_login( account_t *acc ) struct msn_data *md = g_new0( struct msn_data, 1 ); ic->proto_data = md; - md->fd = -1; if( strchr( acc->user, '@' ) == NULL ) { @@ -55,26 +60,22 @@ static void msn_login( account_t *acc ) return; } - imcb_log( ic, "Connecting" ); - - md->fd = proxy_connect( "messenger.hotmail.com", 1863, msn_ns_connected, ic ); - if( md->fd < 0 ) - { - imcb_error( ic, "Could not connect to server" ); - imc_logout( ic, TRUE ); - return; - } - md->ic = ic; md->away_state = msn_away_state_list; + md->domaintree = g_tree_new( msn_domaintree_cmp ); + md->ns->fd = -1; - msn_connections = g_slist_append( msn_connections, ic ); + msn_connections = g_slist_prepend( msn_connections, ic ); + + imcb_log( ic, "Connecting" ); + msn_ns_connect( ic, md->ns, MSN_NS_HOST, MSN_NS_PORT ); } static void msn_logout( struct im_connection *ic ) { struct msn_data *md = ic->proto_data; GSList *l; + int i; if( md ) { @@ -84,24 +85,30 @@ static void msn_logout( struct im_connection *ic ) } */ - if( md->fd >= 0 ) - closesocket( md->fd ); - - if( md->handler ) - { - if( md->handler->rxq ) g_free( md->handler->rxq ); - if( md->handler->cmd_text ) g_free( md->handler->cmd_text ); - g_free( md->handler ); - } + msn_ns_close( md->ns ); while( md->switchboards ) msn_sb_destroy( md->switchboards->data ); msn_msgq_purge( ic, &md->msgq ); + msn_soapq_flush( ic, FALSE ); + + for( i = 0; i < sizeof( md->tokens ) / sizeof( md->tokens[0] ); i ++ ) + g_free( md->tokens[i] ); + g_free( md->lock_key ); + g_free( md->pp_policy ); - while( md->groupcount > 0 ) - g_free( md->grouplist[--md->groupcount] ); - g_free( md->grouplist ); + while( md->groups ) + { + struct msn_group *mg = md->groups->data; + g_free( mg->id ); + g_free( mg->name ); + g_free( mg ); + md->groups = g_slist_remove( md->groups, mg ); + } + + g_tree_destroy( md->domaintree ); + md->domaintree = NULL; while( md->grpq ) { @@ -133,8 +140,7 @@ static int msn_buddy_msg( struct im_connection *ic, char *who, char *message, in #ifdef DEBUG if( strcmp( who, "raw" ) == 0 ) { - msn_write( ic, message, strlen( message ) ); - msn_write( ic, "\r\n", 2 ); + msn_ns_write( ic, -1, "%s\r\n", message ); } else #endif @@ -172,7 +178,7 @@ static GList *msn_away_states( struct im_connection *ic ) static void msn_set_away( struct im_connection *ic, char *state, char *message ) { - char buf[1024]; + char *uux; struct msn_data *md = ic->proto_data; if( state == NULL ) @@ -180,13 +186,13 @@ static void msn_set_away( struct im_connection *ic, char *state, char *message ) else if( ( md->away_state = msn_away_state_by_name( state ) ) == NULL ) md->away_state = msn_away_state_list + 1; - g_snprintf( buf, sizeof( buf ), "CHG %d %s\r\n", ++md->trId, md->away_state->code ); - msn_write( ic, buf, strlen( buf ) ); -} - -static void msn_set_my_name( struct im_connection *ic, char *info ) -{ - msn_set_display_name( ic, info ); + if( !msn_ns_write( ic, -1, "CHG %d %s\r\n", ++md->trId, md->away_state->code ) ) + return; + + uux = g_markup_printf_escaped( "<Data><PSM>%s</PSM><CurrentMedia></CurrentMedia>" + "</Data>", message ? message : "" ); + msn_ns_write( ic, -1, "UUX %d %zd\r\n%s", ++md->trId, strlen( uux ), uux ); + g_free( uux ); } static void msn_get_info(struct im_connection *ic, char *who) @@ -199,14 +205,14 @@ static void msn_add_buddy( struct im_connection *ic, char *who, char *group ) { struct bee_user *bu = bee_user_by_handle( ic->bee, ic, who ); - msn_buddy_list_add( ic, "FL", who, who, group ); + msn_buddy_list_add( ic, MSN_BUDDY_FL, who, who, group ); if( bu && bu->group ) - msn_buddy_list_remove( ic, "FL", who, bu->group->name ); + msn_buddy_list_remove( ic, MSN_BUDDY_FL, who, bu->group->name ); } static void msn_remove_buddy( struct im_connection *ic, char *who, char *group ) { - msn_buddy_list_remove( ic, "FL", who, NULL ); + msn_buddy_list_remove( ic, MSN_BUDDY_FL, who, NULL ); } static void msn_chat_msg( struct groupchat *c, char *message, int flags ) @@ -266,24 +272,24 @@ static struct groupchat *msn_chat_with( struct im_connection *ic, char *who ) static void msn_keepalive( struct im_connection *ic ) { - msn_write( ic, "PNG\r\n", strlen( "PNG\r\n" ) ); + msn_ns_write( ic, -1, "PNG\r\n" ); } static void msn_add_permit( struct im_connection *ic, char *who ) { - msn_buddy_list_add( ic, "AL", who, who, NULL ); + msn_buddy_list_add( ic, MSN_BUDDY_AL, who, who, NULL ); } static void msn_rem_permit( struct im_connection *ic, char *who ) { - msn_buddy_list_remove( ic, "AL", who, NULL ); + msn_buddy_list_remove( ic, MSN_BUDDY_AL, who, NULL ); } static void msn_add_deny( struct im_connection *ic, char *who ) { struct msn_switchboard *sb; - msn_buddy_list_add( ic, "BL", who, who, NULL ); + msn_buddy_list_add( ic, MSN_BUDDY_BL, who, who, NULL ); /* If there's still a conversation with this person, close it. */ if( ( sb = msn_sb_by_handle( ic, who ) ) ) @@ -294,7 +300,7 @@ static void msn_add_deny( struct im_connection *ic, char *who ) static void msn_rem_deny( struct im_connection *ic, char *who ) { - msn_buddy_list_remove( ic, "BL", who, NULL ); + msn_buddy_list_remove( ic, MSN_BUDDY_BL, who, NULL ); } static int msn_send_typing( struct im_connection *ic, char *who, int typing ) @@ -313,10 +319,7 @@ static char *set_eval_display_name( set_t *set, char *value ) { account_t *acc = set->data; struct im_connection *ic = acc->ic; - - /* Allow any name if we're offline. */ - if( ic == NULL ) - return value; + struct msn_data *md = ic->proto_data; if( strlen( value ) > 129 ) { @@ -324,10 +327,26 @@ static char *set_eval_display_name( set_t *set, char *value ) return NULL; } - /* Returning NULL would be better, because the server still has to - confirm the name change. However, it looks a bit confusing to the - user. */ - return msn_set_display_name( ic, value ) ? value : NULL; + if( md->flags & MSN_GOT_PROFILE_DN ) + imcb_log( ic, "Warning: Persistent name changes for this account have to be done " + "in the profile. BitlBee doesn't currently support this." ); + + msn_soap_addressbook_set_display_name( ic, value ); + return msn_ns_set_display_name( ic, value ) ? value : NULL; +} + +static void msn_buddy_data_add( bee_user_t *bu ) +{ + struct msn_data *md = bu->ic->proto_data; + bu->data = g_new0( struct msn_buddy_data, 1 ); + g_tree_insert( md->domaintree, bu->handle, bu ); +} + +static void msn_buddy_data_free( bee_user_t *bu ) +{ + struct msn_data *md = bu->ic->proto_data; + g_tree_remove( md->domaintree, bu->handle ); + g_free( bu->data ); } void msn_initmodule() @@ -342,7 +361,6 @@ void msn_initmodule() ret->away_states = msn_away_states; ret->set_away = msn_set_away; ret->get_info = msn_get_info; - ret->set_my_name = msn_set_my_name; ret->add_buddy = msn_add_buddy; ret->remove_buddy = msn_remove_buddy; ret->chat_msg = msn_chat_msg; @@ -356,6 +374,9 @@ void msn_initmodule() ret->rem_deny = msn_rem_deny; ret->send_typing = msn_send_typing; ret->handle_cmp = g_strcasecmp; + ret->buddy_data_add = msn_buddy_data_add; + ret->buddy_data_free = msn_buddy_data_free; + //ret->transfer_request = msn_ftp_transfer_request; register_protocol(ret); diff --git a/protocols/msn/msn.h b/protocols/msn/msn.h index 31683cb5..829bbd4c 100644 --- a/protocols/msn/msn.h +++ b/protocols/msn/msn.h @@ -1,7 +1,7 @@ /********************************************************************\ * BitlBee -- An IRC to other IM-networks gateway * * * - * Copyright 2002-2004 Wilmer van der Gaast and others * + * Copyright 2002-2010 Wilmer van der Gaast and others * \********************************************************************/ /* MSN module */ @@ -38,8 +38,23 @@ #define debug( text... ) #endif -#define QRY_NAME "msmsgs@msnmsgr.com" -#define QRY_CODE "Q1P7W2E4J9R8U3S5" +/* This should be MSN Messenger 7.0.0813 +#define MSNP11_PROD_KEY "CFHUR$52U_{VIX5T" +#define MSNP11_PROD_ID "PROD0101{0RM?UBW" +*/ + +#define MSN_NS_HOST "messenger.hotmail.com" +#define MSN_NS_PORT 1863 + +/* Some other version. +#define MSNP11_PROD_KEY "O4BG@C7BWLYQX?5G" +#define MSNP11_PROD_ID "PROD01065C%ZFN6F" +*/ + +#define MSNP11_PROD_KEY "ILTXC!4IXB5FB*PX" +#define MSNP11_PROD_ID "PROD0119GSJUC$18" +#define MSNP_VER "MSNP15" +#define MSNP_BUILD "8.5.1288" #define MSN_SB_NEW -24062002 @@ -60,31 +75,61 @@ #define PROFILE_URL "http://members.msn.com/" +typedef enum +{ + MSN_GOT_PROFILE = 1, + MSN_GOT_PROFILE_DN = 2, + MSN_DONE_ADL = 4, + MSN_REAUTHING = 8, +} msn_flags_t; + +struct msn_handler_data +{ + int fd, inpa; + int rxlen; + char *rxq; + + int msglen; + char *cmd_text; + + /* Either ic or sb */ + gpointer data; + + int (*exec_command) ( struct msn_handler_data *handler, char **cmd, int count ); + int (*exec_message) ( struct msn_handler_data *handler, char *msg, int msglen, char **cmd, int count ); +}; + struct msn_data { struct im_connection *ic; - int fd; - struct msn_handler_data *handler; + struct msn_handler_data ns[1]; + msn_flags_t flags; int trId; + char *tokens[4]; + char *lock_key, *pp_policy; - GSList *msgq, *grpq; + GSList *msgq, *grpq, *soapq; GSList *switchboards; int sb_failures; time_t first_sb_failure; - GSList *filetransfers; const struct msn_away_state *away_state; - int buddycount; - int groupcount; - char **grouplist; + GSList *groups; + + /* Mostly used for sending the ADL command; since MSNP13 the client + is responsible for downloading the contact list and then sending + it to the MSNP server. */ + GTree *domaintree; + int adl_todo; }; struct msn_switchboard { struct im_connection *ic; + /* The following two are also in the handler. TODO: Clean up. */ int fd; gint inp; struct msn_handler_data *handler; @@ -126,19 +171,26 @@ struct msn_groupadd char *group; }; -struct msn_handler_data +typedef enum { - int fd; - int rxlen; - char *rxq; - - int msglen; - char *cmd_text; - - gpointer data; - - int (*exec_command) ( gpointer data, char **cmd, int count ); - int (*exec_message) ( gpointer data, char *msg, int msglen, char **cmd, int count ); + MSN_BUDDY_FL = 1, /* Warning: FL,AL,BL *must* be 1,2,4. */ + MSN_BUDDY_AL = 2, + MSN_BUDDY_BL = 4, + MSN_BUDDY_RL = 8, + MSN_BUDDY_PL = 16, + MSN_BUDDY_ADL_SYNCED = 256, +} msn_buddy_flags_t; + +struct msn_buddy_data +{ + char *cid; + msn_buddy_flags_t flags; +}; + +struct msn_group +{ + char *name; + char *id; }; /* Bitfield values for msn_status_code.flags */ @@ -160,20 +212,27 @@ extern GSList *msn_connections; extern GSList *msn_switchboards; /* ns.c */ -gboolean msn_ns_connected( gpointer data, gint source, b_input_condition cond ); +int msn_ns_write( struct im_connection *ic, int fd, const char *fmt, ... ); +gboolean msn_ns_connect( struct im_connection *ic, struct msn_handler_data *handler, const char *host, int port ); +void msn_ns_close( struct msn_handler_data *handler ); +void msn_auth_got_passport_token( struct im_connection *ic, const char *token, const char *error ); +void msn_auth_got_contact_list( struct im_connection *ic ); +int msn_ns_finish_login( struct im_connection *ic ); /* msn_util.c */ -int msn_write( struct im_connection *ic, char *s, int len ); int msn_logged_in( struct im_connection *ic ); -int msn_buddy_list_add( struct im_connection *ic, const char *list, const char *who, const char *realname_, const char *group ); -int msn_buddy_list_remove( struct im_connection *ic, char *list, const char *who, const char *group ); -void msn_buddy_ask( struct im_connection *ic, char *handle, char *realname ); +int msn_buddy_list_add( struct im_connection *ic, msn_buddy_flags_t list, const char *who, const char *realname_, const char *group ); +int msn_buddy_list_remove( struct im_connection *ic, msn_buddy_flags_t list, const char *who, const char *group ); +void msn_buddy_ask( bee_user_t *bu ); char *msn_findheader( char *text, char *header, int len ); char **msn_linesplit( char *line ); int msn_handler( struct msn_handler_data *h ); -char *msn_http_encode( const char *input ); void msn_msgq_purge( struct im_connection *ic, GSList **list ); -gboolean msn_set_display_name( struct im_connection *ic, const char *rawname ); +char *msn_p11_challenge( char *challenge ); +gint msn_domaintree_cmp( gconstpointer a_, gconstpointer b_ ); +struct msn_group *msn_group_by_name( struct im_connection *ic, const char *name ); +struct msn_group *msn_group_by_id( struct im_connection *ic, const char *id ); +int msn_ns_set_display_name( struct im_connection *ic, const char *value ); /* tables.c */ const struct msn_away_state *msn_away_state_by_number( int number ); @@ -182,7 +241,7 @@ const struct msn_away_state *msn_away_state_by_name( char *name ); const struct msn_status_code *msn_status_by_number( int number ); /* sb.c */ -int msn_sb_write( struct msn_switchboard *sb, char *s, int len ); +int msn_sb_write( struct msn_switchboard *sb, const char *fmt, ... ); struct msn_switchboard *msn_sb_create( struct im_connection *ic, char *host, int port, char *key, int session ); struct msn_switchboard *msn_sb_by_handle( struct im_connection *ic, char *handle ); struct msn_switchboard *msn_sb_by_chat( struct groupchat *c ); @@ -195,7 +254,4 @@ int msn_sb_write_msg( struct im_connection *ic, struct msn_message *m ); void msn_sb_start_keepalives( struct msn_switchboard *sb, gboolean initial ); void msn_sb_stop_keepalives( struct msn_switchboard *sb ); -/* invitation.c */ -void msn_ftp_transfer_request( struct im_connection *ic, file_transfer_t *ft, char *who ); - #endif //_MSN_H diff --git a/protocols/msn/msn_util.c b/protocols/msn/msn_util.c index 23447403..7fa68915 100644 --- a/protocols/msn/msn_util.c +++ b/protocols/msn/msn_util.c @@ -1,7 +1,7 @@ /********************************************************************\ * BitlBee -- An IRC to other IM-networks gateway * * * - * Copyright 2002-2004 Wilmer van der Gaast and others * + * Copyright 2002-2010 Wilmer van der Gaast and others * \********************************************************************/ /* MSN module - Miscellaneous utilities */ @@ -25,37 +25,41 @@ #include "nogaim.h" #include "msn.h" +#include "md5.h" +#include "soap.h" #include <ctype.h> -int msn_write( struct im_connection *ic, char *s, int len ) +int msn_logged_in( struct im_connection *ic ) { - struct msn_data *md = ic->proto_data; - int st; - - st = write( md->fd, s, len ); - if( st != len ) - { - imcb_error( ic, "Short write() to main server" ); - imc_logout( ic, TRUE ); - return 0; - } + imcb_connected( ic ); - return 1; + return( 0 ); } -int msn_logged_in( struct im_connection *ic ) +static char *adlrml_entry( const char *handle_, msn_buddy_flags_t list ) { - imcb_connected( ic ); + char *domain, handle[strlen(handle_)+1]; - return( 0 ); + strcpy( handle, handle_ ); + if( ( domain = strchr( handle, '@' ) ) ) + *(domain++) = '\0'; + else + return NULL; + + return g_markup_printf_escaped( "<ml><d n=\"%s\"><c n=\"%s\" l=\"%d\" t=\"1\"/></d></ml>", + domain, handle, list ); } -int msn_buddy_list_add( struct im_connection *ic, const char *list, const char *who, const char *realname_, const char *group ) +int msn_buddy_list_add( struct im_connection *ic, msn_buddy_flags_t list, const char *who, const char *realname, const char *group ) { struct msn_data *md = ic->proto_data; - char buf[1024], *realname, groupid[8]; + char groupid[8]; + bee_user_t *bu; + struct msn_buddy_data *bd; + char *adl; *groupid = '\0'; +#if 0 if( group ) { int i; @@ -86,9 +90,10 @@ int msn_buddy_list_add( struct im_connection *ic, const char *list, const char * if( l == NULL ) { - char *groupname = msn_http_encode( group ); + char groupname[strlen(group)+1]; + strcpy( groupname, group ); + http_encode( groupname ); g_snprintf( buf, sizeof( buf ), "ADG %d %s %d\r\n", ++md->trId, groupname, 0 ); - g_free( groupname ); return msn_write( ic, buf, strlen( buf ) ); } else @@ -100,20 +105,42 @@ int msn_buddy_list_add( struct im_connection *ic, const char *list, const char * } } } +#endif + + if( !( ( bu = bee_user_by_handle( ic->bee, ic, who ) ) || + ( bu = bee_user_new( ic->bee, ic, who, 0 ) ) ) || + !( bd = bu->data ) || bd->flags & list ) + return 1; + + bd->flags |= list; - realname = msn_http_encode( realname_ ); - g_snprintf( buf, sizeof( buf ), "ADD %d %s %s %s%s\r\n", ++md->trId, list, who, realname, groupid ); - g_free( realname ); + if( list == MSN_BUDDY_FL ) + msn_soap_ab_contact_add( ic, bu ); + else + msn_soap_memlist_edit( ic, who, TRUE, list ); - return msn_write( ic, buf, strlen( buf ) ); + if( ( adl = adlrml_entry( who, list ) ) ) + { + int st = msn_ns_write( ic, -1, "ADL %d %zd\r\n%s", + ++md->trId, strlen( adl ), adl ); + g_free( adl ); + + return st; + } + + return 1; } -int msn_buddy_list_remove( struct im_connection *ic, char *list, const char *who, const char *group ) +int msn_buddy_list_remove( struct im_connection *ic, msn_buddy_flags_t list, const char *who, const char *group ) { struct msn_data *md = ic->proto_data; - char buf[1024], groupid[8]; + char groupid[8]; + bee_user_t *bu; + struct msn_buddy_data *bd; + char *adl; *groupid = '\0'; +#if 0 if( group ) { int i; @@ -124,12 +151,29 @@ int msn_buddy_list_remove( struct im_connection *ic, char *list, const char *who break; } } +#endif - g_snprintf( buf, sizeof( buf ), "REM %d %s %s%s\r\n", ++md->trId, list, who, groupid ); - if( msn_write( ic, buf, strlen( buf ) ) ) - return( 1 ); + if( !( bu = bee_user_by_handle( ic->bee, ic, who ) ) || + !( bd = bu->data ) || !( bd->flags & list ) ) + return 1; - return( 0 ); + bd->flags &= ~list; + + if( list == MSN_BUDDY_FL ) + msn_soap_ab_contact_del( ic, bu ); + else + msn_soap_memlist_edit( ic, who, FALSE, list ); + + if( ( adl = adlrml_entry( who, list ) ) ) + { + int st = msn_ns_write( ic, -1, "RML %d %zd\r\n%s", + ++md->trId, strlen( adl ), adl ); + g_free( adl ); + + return st; + } + + return 1; } struct msn_buddy_ask_data @@ -143,7 +187,7 @@ static void msn_buddy_ask_yes( void *data ) { struct msn_buddy_ask_data *bla = data; - msn_buddy_list_add( bla->ic, "AL", bla->handle, bla->realname, NULL ); + msn_buddy_list_add( bla->ic, MSN_BUDDY_AL, bla->handle, bla->realname, NULL ); imcb_ask_add( bla->ic, bla->handle, NULL ); @@ -156,26 +200,31 @@ static void msn_buddy_ask_no( void *data ) { struct msn_buddy_ask_data *bla = data; - msn_buddy_list_add( bla->ic, "BL", bla->handle, bla->realname, NULL ); + msn_buddy_list_add( bla->ic, MSN_BUDDY_BL, bla->handle, bla->realname, NULL ); g_free( bla->handle ); g_free( bla->realname ); g_free( bla ); } -void msn_buddy_ask( struct im_connection *ic, char *handle, char *realname ) +void msn_buddy_ask( bee_user_t *bu ) { - struct msn_buddy_ask_data *bla = g_new0( struct msn_buddy_ask_data, 1 ); + struct msn_buddy_ask_data *bla; + struct msn_buddy_data *bd = bu->data; char buf[1024]; - bla->ic = ic; - bla->handle = g_strdup( handle ); - bla->realname = g_strdup( realname ); + if( ( bd->flags & 30 ) != 8 && ( bd->flags & 30 ) != 16 ) + return; + + bla = g_new0( struct msn_buddy_ask_data, 1 ); + bla->ic = bu->ic; + bla->handle = g_strdup( bu->handle ); + bla->realname = g_strdup( bu->fullname ); g_snprintf( buf, sizeof( buf ), "The user %s (%s) wants to add you to his/her buddy list.", - handle, realname ); - imcb_ask( ic, buf, bla, msn_buddy_ask_yes, msn_buddy_ask_no ); + bu->handle, bu->fullname ); + imcb_ask( bu->ic, buf, bla, msn_buddy_ask_yes, msn_buddy_ask_no ); } char *msn_findheader( char *text, char *header, int len ) @@ -279,6 +328,12 @@ int msn_handler( struct msn_handler_data *h ) if( st <= 0 ) return( -1 ); + if( getenv( "BITLBEE_DEBUG" ) ) + { + write( 2, "->C:", 4 ); + write( 2, h->rxq + h->rxlen - st, st ); + } + while( st ) { int i; @@ -295,7 +350,7 @@ int msn_handler( struct msn_handler_data *h ) cmd_text = g_strndup( h->rxq, i ); cmd = msn_linesplit( cmd_text ); for( count = 0; cmd[count]; count ++ ); - st = h->exec_command( h->data, cmd, count ); + st = h->exec_command( h, cmd, count ); g_free( cmd_text ); /* If the connection broke, don't continue. We don't even exist anymore. */ @@ -330,7 +385,7 @@ int msn_handler( struct msn_handler_data *h ) cmd = msn_linesplit( h->cmd_text ); for( count = 0; cmd[count]; count ++ ); - st = h->exec_message( h->data, msg, h->msglen, cmd, count ); + st = h->exec_message( h, msg, h->msglen, cmd, count ); g_free( msg ); g_free( h->cmd_text ); h->cmd_text = NULL; @@ -366,32 +421,6 @@ int msn_handler( struct msn_handler_data *h ) return( 1 ); } -/* The difference between this function and the normal http_encode() function - is that this one escapes every 7-bit ASCII character because this is said - to avoid some lame server-side checks when setting a real-name. Also, - non-ASCII characters are not escaped because MSN servers don't seem to - appreciate that! */ -char *msn_http_encode( const char *input ) -{ - char *ret, *s; - int i; - - ret = s = g_new0( char, strlen( input ) * 3 + 1 ); - for( i = 0; input[i]; i ++ ) - if( input[i] & 128 ) - { - *s = input[i]; - s ++; - } - else - { - g_snprintf( s, 4, "%%%02X", input[i] ); - s += 3; - } - - return ret; -} - void msn_msgq_purge( struct im_connection *ic, GSList **list ) { struct msn_message *m; @@ -432,14 +461,130 @@ void msn_msgq_purge( struct im_connection *ic, GSList **list ) g_string_free( ret, TRUE ); } -gboolean msn_set_display_name( struct im_connection *ic, const char *rawname ) +/* Copied and heavily modified from http://tmsnc.sourceforge.net/chl.c */ +char *msn_p11_challenge( char *challenge ) +{ + char *output, buf[256]; + md5_state_t md5c; + unsigned char md5Hash[16], *newHash; + unsigned int *md5Parts, *chlStringParts, newHashParts[5]; + long long nHigh = 0, nLow = 0; + int i, n; + + /* Create the MD5 hash */ + md5_init(&md5c); + md5_append(&md5c, (unsigned char*) challenge, strlen(challenge)); + md5_append(&md5c, (unsigned char*) MSNP11_PROD_KEY, strlen(MSNP11_PROD_KEY)); + md5_finish(&md5c, md5Hash); + + /* Split it into four integers */ + md5Parts = (unsigned int *)md5Hash; + for (i = 0; i < 4; i ++) + { + md5Parts[i] = GUINT32_TO_LE(md5Parts[i]); + + /* & each integer with 0x7FFFFFFF */ + /* and save one unmodified array for later */ + newHashParts[i] = md5Parts[i]; + md5Parts[i] &= 0x7FFFFFFF; + } + + /* make a new string and pad with '0' */ + n = g_snprintf(buf, sizeof(buf)-5, "%s%s00000000", challenge, MSNP11_PROD_ID); + /* truncate at an 8-byte boundary */ + buf[n&=~7] = '\0'; + + /* split into integers */ + chlStringParts = (unsigned int *)buf; + + /* this is magic */ + for (i = 0; i < (n / 4) - 1; i += 2) + { + long long temp; + + chlStringParts[i] = GUINT32_TO_LE(chlStringParts[i]); + chlStringParts[i+1] = GUINT32_TO_LE(chlStringParts[i+1]); + + temp = (md5Parts[0] * (((0x0E79A9C1 * (long long)chlStringParts[i]) % 0x7FFFFFFF)+nHigh) + md5Parts[1])%0x7FFFFFFF; + nHigh = (md5Parts[2] * (((long long)chlStringParts[i+1]+temp) % 0x7FFFFFFF) + md5Parts[3]) % 0x7FFFFFFF; + nLow = nLow + nHigh + temp; + } + nHigh = (nHigh+md5Parts[1]) % 0x7FFFFFFF; + nLow = (nLow+md5Parts[3]) % 0x7FFFFFFF; + + newHashParts[0] ^= nHigh; + newHashParts[1] ^= nLow; + newHashParts[2] ^= nHigh; + newHashParts[3] ^= nLow; + + /* swap more bytes if big endian */ + for (i = 0; i < 4; i ++) + newHashParts[i] = GUINT32_TO_LE(newHashParts[i]); + + /* make a string of the parts */ + newHash = (unsigned char *)newHashParts; + + /* convert to hexadecimal */ + output = g_new(char, 33); + for (i = 0; i < 16; i ++) + sprintf(output + i * 2, "%02x", newHash[i]); + + return output; +} + +gint msn_domaintree_cmp( gconstpointer a_, gconstpointer b_ ) +{ + const char *a = a_, *b = b_; + gint ret; + + if( !( a = strchr( a, '@' ) ) || !( b = strchr( b, '@' ) ) || + ( ret = strcmp( a, b ) ) == 0 ) + ret = strcmp( a_, b_ ); + + return ret; +} + +struct msn_group *msn_group_by_name( struct im_connection *ic, const char *name ) { - char *fn = msn_http_encode( rawname ); struct msn_data *md = ic->proto_data; - char buf[1024]; + GSList *l; + + for( l = md->groups; l; l = l->next ) + { + struct msn_group *mg = l->data; + + if( g_strcasecmp( mg->name, name ) == 0 ) + return mg; + } + + return NULL; +} + +struct msn_group *msn_group_by_id( struct im_connection *ic, const char *id ) +{ + struct msn_data *md = ic->proto_data; + GSList *l; + + for( l = md->groups; l; l = l->next ) + { + struct msn_group *mg = l->data; + + if( g_strcasecmp( mg->id, id ) == 0 ) + return mg; + } + + return NULL; +} + +int msn_ns_set_display_name( struct im_connection *ic, const char *value ) +{ + struct msn_data *md = ic->proto_data; + char fn[strlen(value)*3+1]; - g_snprintf( buf, sizeof( buf ), "REA %d %s %s\r\n", ++md->trId, ic->acc->user, fn ); - g_free( fn ); + strcpy( fn, value ); + http_encode( fn ); - return msn_write( ic, buf, strlen( buf ) ) != 0; + /* Note: We don't actually know if the server accepted the new name, + and won't give proper feedback yet if it doesn't. */ + return msn_ns_write( ic, -1, "PRP %d MFN %s\r\n", ++md->trId, fn ); } diff --git a/protocols/msn/ns.c b/protocols/msn/ns.c index 0be9e727..d64a71ac 100644 --- a/protocols/msn/ns.c +++ b/protocols/msn/ns.c @@ -1,7 +1,7 @@ /********************************************************************\ * BitlBee -- An IRC to other IM-networks gateway * * * - * Copyright 2002-2004 Wilmer van der Gaast and others * + * Copyright 2002-2010 Wilmer van der Gaast and others * \********************************************************************/ /* MSN module - Notification server callbacks */ @@ -26,68 +26,122 @@ #include <ctype.h> #include "nogaim.h" #include "msn.h" -#include "passport.h" #include "md5.h" +#include "soap.h" +#include "xmltree.h" +static gboolean msn_ns_connected( gpointer data, gint source, b_input_condition cond ); static gboolean msn_ns_callback( gpointer data, gint source, b_input_condition cond ); -static int msn_ns_command( gpointer data, char **cmd, int num_parts ); -static int msn_ns_message( gpointer data, char *msg, int msglen, char **cmd, int num_parts ); +static int msn_ns_command( struct msn_handler_data *handler, char **cmd, int num_parts ); +static int msn_ns_message( struct msn_handler_data *handler, char *msg, int msglen, char **cmd, int num_parts ); -static void msn_auth_got_passport_token( struct msn_auth_data *mad ); -static gboolean msn_ns_got_display_name( struct im_connection *ic, char *name ); +static void msn_ns_send_adl_start( struct im_connection *ic ); +static void msn_ns_send_adl( struct im_connection *ic ); -gboolean msn_ns_connected( gpointer data, gint source, b_input_condition cond ) +int msn_ns_write( struct im_connection *ic, int fd, const char *fmt, ... ) { - struct im_connection *ic = data; - struct msn_data *md; - char s[1024]; + struct msn_data *md = ic->proto_data; + va_list params; + char *out; + size_t len; + int st; - if( !g_slist_find( msn_connections, ic ) ) - return FALSE; + va_start( params, fmt ); + out = g_strdup_vprintf( fmt, params ); + va_end( params ); - if( source == -1 ) + if( fd < 0 ) + fd = md->ns->fd; + + if( getenv( "BITLBEE_DEBUG" ) ) + fprintf( stderr, "->NS%d:%s", fd, out ); + + len = strlen( out ); + st = write( fd, out, len ); + g_free( out ); + if( st != len ) + { + imcb_error( ic, "Short write() to main server" ); + imc_logout( ic, TRUE ); + return 0; + } + + return 1; +} + +gboolean msn_ns_connect( struct im_connection *ic, struct msn_handler_data *handler, const char *host, int port ) +{ + if( handler->fd >= 0 ) + closesocket( handler->fd ); + + handler->exec_command = msn_ns_command; + handler->exec_message = msn_ns_message; + handler->data = ic; + handler->fd = proxy_connect( host, port, msn_ns_connected, handler ); + if( handler->fd < 0 ) { imcb_error( ic, "Could not connect to server" ); imc_logout( ic, TRUE ); return FALSE; } + return TRUE; +} + +static gboolean msn_ns_connected( gpointer data, gint source, b_input_condition cond ) +{ + struct msn_handler_data *handler = data; + struct im_connection *ic = handler->data; + struct msn_data *md; + + if( !g_slist_find( msn_connections, ic ) ) + return FALSE; + md = ic->proto_data; - if( !md->handler ) - { - md->handler = g_new0( struct msn_handler_data, 1 ); - md->handler->data = ic; - md->handler->exec_command = msn_ns_command; - md->handler->exec_message = msn_ns_message; - } - else + if( source == -1 ) { - if( md->handler->rxq ) - g_free( md->handler->rxq ); - - md->handler->rxlen = 0; + imcb_error( ic, "Could not connect to server" ); + imc_logout( ic, TRUE ); + return FALSE; } - md->handler->fd = md->fd; - md->handler->rxq = g_new0( char, 1 ); + g_free( handler->rxq ); + handler->rxlen = 0; + handler->rxq = g_new0( char, 1 ); - g_snprintf( s, sizeof( s ), "VER %d MSNP8 CVR0\r\n", ++md->trId ); - if( msn_write( ic, s, strlen( s ) ) ) + if( msn_ns_write( ic, source, "VER %d %s CVR0\r\n", ++md->trId, MSNP_VER ) ) { - ic->inpa = b_input_add( md->fd, B_EV_IO_READ, msn_ns_callback, ic ); + handler->inpa = b_input_add( handler->fd, B_EV_IO_READ, msn_ns_callback, handler ); imcb_log( ic, "Connected to server, waiting for reply" ); } return FALSE; } +void msn_ns_close( struct msn_handler_data *handler ) +{ + if( handler->fd >= 0 ) + { + closesocket( handler->fd ); + b_event_remove( handler->inpa ); + } + + handler->fd = handler->inpa = -1; + g_free( handler->rxq ); + g_free( handler->cmd_text ); + + handler->rxlen = 0; + handler->rxq = NULL; + handler->cmd_text = NULL; +} + static gboolean msn_ns_callback( gpointer data, gint source, b_input_condition cond ) { - struct im_connection *ic = data; - struct msn_data *md = ic->proto_data; + struct msn_handler_data *handler = data; + struct im_connection *ic = handler->data; - if( msn_handler( md->handler ) == -1 ) /* Don't do this on ret == 0, it's already done then. */ + if( msn_handler( handler ) == -1 ) /* Don't do this on ret == 0, it's already done then. */ { imcb_error( ic, "Error while reading from server" ); imc_logout( ic, TRUE ); @@ -98,11 +152,10 @@ static gboolean msn_ns_callback( gpointer data, gint source, b_input_condition c return TRUE; } -static int msn_ns_command( gpointer data, char **cmd, int num_parts ) +static int msn_ns_command( struct msn_handler_data *handler, char **cmd, int num_parts ) { - struct im_connection *ic = data; + struct im_connection *ic = handler->data; struct msn_data *md = ic->proto_data; - char buf[1024]; if( num_parts == 0 ) { @@ -112,33 +165,30 @@ static int msn_ns_command( gpointer data, char **cmd, int num_parts ) if( strcmp( cmd[0], "VER" ) == 0 ) { - if( cmd[2] && strncmp( cmd[2], "MSNP8", 5 ) != 0 ) + if( cmd[2] && strncmp( cmd[2], MSNP_VER, 5 ) != 0 ) { imcb_error( ic, "Unsupported protocol" ); imc_logout( ic, FALSE ); return( 0 ); } - g_snprintf( buf, sizeof( buf ), "CVR %d 0x0409 mac 10.2.0 ppc macmsgs 3.5.1 macmsgs %s\r\n", - ++md->trId, ic->acc->user ); - return( msn_write( ic, buf, strlen( buf ) ) ); + return( msn_ns_write( ic, handler->fd, "CVR %d 0x0409 mac 10.2.0 ppc macmsgs 3.5.1 macmsgs %s\r\n", + ++md->trId, ic->acc->user ) ); } else if( strcmp( cmd[0], "CVR" ) == 0 ) { /* We don't give a damn about the information we just received */ - g_snprintf( buf, sizeof( buf ), "USR %d TWN I %s\r\n", ++md->trId, ic->acc->user ); - return( msn_write( ic, buf, strlen( buf ) ) ); + return msn_ns_write( ic, handler->fd, "USR %d SSO I %s\r\n", ++md->trId, ic->acc->user ); } else if( strcmp( cmd[0], "XFR" ) == 0 ) { char *server; int port; - if( num_parts == 6 && strcmp( cmd[2], "NS" ) == 0 ) + if( num_parts >= 6 && strcmp( cmd[2], "NS" ) == 0 ) { - b_event_remove( ic->inpa ); - ic->inpa = 0; - closesocket( md->fd ); + b_event_remove( handler->inpa ); + handler->inpa = -1; server = strchr( cmd[3], ':' ); if( !server ) @@ -152,10 +202,9 @@ static int msn_ns_command( gpointer data, char **cmd, int num_parts ) server = cmd[3]; imcb_log( ic, "Transferring to other server" ); - - md->fd = proxy_connect( server, port, msn_ns_connected, ic ); + return msn_ns_connect( ic, handler, server, port ); } - else if( num_parts == 6 && strcmp( cmd[2], "SB" ) == 0 ) + else if( num_parts >= 6 && strcmp( cmd[2], "SB" ) == 0 ) { struct msn_switchboard *sb; @@ -219,27 +268,17 @@ static int msn_ns_command( gpointer data, char **cmd, int num_parts ) } else if( strcmp( cmd[0], "USR" ) == 0 ) { - if( num_parts == 5 && strcmp( cmd[2], "TWN" ) == 0 && strcmp( cmd[3], "S" ) == 0 ) + if( num_parts >= 6 && strcmp( cmd[2], "SSO" ) == 0 && + strcmp( cmd[3], "S" ) == 0 ) { - /* Time for some Passport black magic... */ - if( !passport_get_token( msn_auth_got_passport_token, ic, ic->acc->user, ic->acc->pass, cmd[4] ) ) - { - imcb_error( ic, "Error while contacting Passport server" ); - imc_logout( ic, TRUE ); - return( 0 ); - } + g_free( md->pp_policy ); + md->pp_policy = g_strdup( cmd[4] ); + msn_soap_passport_sso_request( ic, cmd[5] ); } - else if( num_parts >= 7 && strcmp( cmd[2], "OK" ) == 0 ) + else if( strcmp( cmd[2], "OK" ) == 0 ) { - if( num_parts == 7 ) - msn_ns_got_display_name( ic, cmd[4] ); - else - imcb_log( ic, "Warning: Friendly name in server response was corrupted" ); - imcb_log( ic, "Authenticated, getting buddy list" ); - - g_snprintf( buf, sizeof( buf ), "SYN %d 0\r\n", ++md->trId ); - return( msn_write( ic, buf, strlen( buf ) ) ); + msn_soap_memlist_request( ic ); } else { @@ -250,163 +289,76 @@ static int msn_ns_command( gpointer data, char **cmd, int num_parts ) } else if( strcmp( cmd[0], "MSG" ) == 0 ) { - if( num_parts != 4 ) + if( num_parts < 4 ) { imcb_error( ic, "Syntax error" ); imc_logout( ic, TRUE ); return( 0 ); } - md->handler->msglen = atoi( cmd[3] ); + handler->msglen = atoi( cmd[3] ); - if( md->handler->msglen <= 0 ) + if( handler->msglen <= 0 ) { imcb_error( ic, "Syntax error" ); imc_logout( ic, TRUE ); return( 0 ); } } - else if( strcmp( cmd[0], "SYN" ) == 0 ) + else if( strcmp( cmd[0], "BLP" ) == 0 ) { - if( num_parts == 5 ) - { - int i, groupcount; - - groupcount = atoi( cmd[4] ); - if( groupcount > 0 ) - { - /* valgrind says this is leaking memory, I'm guessing - that this happens during server redirects. */ - if( md->grouplist ) - { - for( i = 0; i < md->groupcount; i ++ ) - g_free( md->grouplist[i] ); - g_free( md->grouplist ); - } - - md->groupcount = groupcount; - md->grouplist = g_new0( char *, md->groupcount ); - } - - md->buddycount = atoi( cmd[3] ); - if( !*cmd[3] || md->buddycount == 0 ) - msn_logged_in( ic ); - } - else - { - /* Hrrm... This SYN reply doesn't really look like something we expected. - Let's assume everything is okay. */ - - msn_logged_in( ic ); - } + msn_ns_send_adl_start( ic ); + return msn_ns_finish_login( ic ); } - else if( strcmp( cmd[0], "LST" ) == 0 ) + else if( strcmp( cmd[0], "ADL" ) == 0 ) { - int list; - - if( num_parts != 4 && num_parts != 5 ) + if( num_parts >= 3 && strcmp( cmd[2], "OK" ) == 0 ) { - imcb_error( ic, "Syntax error" ); - imc_logout( ic, TRUE ); - return( 0 ); + msn_ns_send_adl( ic ); + return msn_ns_finish_login( ic ); } - - http_decode( cmd[2] ); - list = atoi( cmd[3] ); - - if( list & 1 ) /* FL */ + else if( num_parts >= 3 ) { - char *group = NULL; - int num; - - if( cmd[4] != NULL && sscanf( cmd[4], "%d", &num ) == 1 && num < md->groupcount ) - group = md->grouplist[num]; - - imcb_add_buddy( ic, cmd[1], group ); - imcb_rename_buddy( ic, cmd[1], cmd[2] ); - } - if( list & 2 ) /* AL */ - { - ic->permit = g_slist_append( ic->permit, g_strdup( cmd[1] ) ); - } - if( list & 4 ) /* BL */ - { - ic->deny = g_slist_append( ic->deny, g_strdup( cmd[1] ) ); - } - if( list & 8 ) /* RL */ - { - if( ( list & 6 ) == 0 ) - msn_buddy_ask( ic, cmd[1], cmd[2] ); - } - - if( --md->buddycount == 0 ) - { - if( ic->flags & OPT_LOGGED_IN ) - { - imcb_log( ic, "Successfully transferred to different server" ); - g_snprintf( buf, sizeof( buf ), "CHG %d %s %d\r\n", ++md->trId, md->away_state->code, 0 ); - return( msn_write( ic, buf, strlen( buf ) ) ); - } - else - { - msn_logged_in( ic ); - } + handler->msglen = atoi( cmd[2] ); } } - else if( strcmp( cmd[0], "LSG" ) == 0 ) + else if( strcmp( cmd[0], "PRP" ) == 0 ) { - int num; - - if( num_parts != 4 ) - { - imcb_error( ic, "Syntax error" ); - imc_logout( ic, TRUE ); - return( 0 ); - } - - http_decode( cmd[2] ); - num = atoi( cmd[1] ); - - if( num < md->groupcount ) - md->grouplist[num] = g_strdup( cmd[2] ); + imcb_connected( ic ); } else if( strcmp( cmd[0], "CHL" ) == 0 ) { - md5_state_t state; - md5_byte_t digest[16]; - int i; + char *resp; + int st; - if( num_parts != 3 ) + if( num_parts < 3 ) { imcb_error( ic, "Syntax error" ); imc_logout( ic, TRUE ); return( 0 ); } - md5_init( &state ); - md5_append( &state, (const md5_byte_t *) cmd[2], strlen( cmd[2] ) ); - md5_append( &state, (const md5_byte_t *) QRY_CODE, strlen( QRY_CODE ) ); - md5_finish( &state, digest ); + resp = msn_p11_challenge( cmd[2] ); - g_snprintf( buf, sizeof( buf ), "QRY %d %s %d\r\n", ++md->trId, QRY_NAME, 32 ); - for( i = 0; i < 16; i ++ ) - g_snprintf( buf + strlen( buf ), 3, "%02x", digest[i] ); - - return( msn_write( ic, buf, strlen( buf ) ) ); + st = msn_ns_write( ic, -1, "QRY %d %s %zd\r\n%s", + ++md->trId, MSNP11_PROD_ID, + strlen( resp ), resp ); + g_free( resp ); + return st; } else if( strcmp( cmd[0], "ILN" ) == 0 ) { const struct msn_away_state *st; - if( num_parts != 6 ) + if( num_parts < 6 ) { imcb_error( ic, "Syntax error" ); imc_logout( ic, TRUE ); return( 0 ); } - http_decode( cmd[4] ); - imcb_rename_buddy( ic, cmd[3], cmd[4] ); + http_decode( cmd[5] ); + imcb_rename_buddy( ic, cmd[3], cmd[5] ); st = msn_away_state_by_code( cmd[2] ); if( !st ) @@ -431,16 +383,18 @@ static int msn_ns_command( gpointer data, char **cmd, int num_parts ) else if( strcmp( cmd[0], "NLN" ) == 0 ) { const struct msn_away_state *st; + int cap; - if( num_parts != 5 ) + if( num_parts < 6 ) { imcb_error( ic, "Syntax error" ); imc_logout( ic, TRUE ); return( 0 ); } - http_decode( cmd[3] ); - imcb_rename_buddy( ic, cmd[2], cmd[3] ); + http_decode( cmd[4] ); + cap = atoi( cmd[5] ); + imcb_rename_buddy( ic, cmd[2], cmd[4] ); st = msn_away_state_by_code( cmd[1] ); if( !st ) @@ -450,7 +404,8 @@ static int msn_ns_command( gpointer data, char **cmd, int num_parts ) } imcb_buddy_status( ic, cmd[2], OPT_LOGGED_IN | - ( st != msn_away_state_list ? OPT_AWAY : 0 ), + ( st != msn_away_state_list ? OPT_AWAY : 0 ) | + ( cap & 1 ? OPT_MOBILE : 0 ), st->name, NULL ); msn_sb_stop_keepalives( msn_sb_by_handle( ic, cmd[2] ) ); @@ -461,7 +416,7 @@ static int msn_ns_command( gpointer data, char **cmd, int num_parts ) char *server; int session, port; - if( num_parts != 7 ) + if( num_parts < 7 ) { imcb_error( ic, "Syntax error" ); imc_logout( ic, TRUE ); @@ -503,46 +458,6 @@ static int msn_ns_command( gpointer data, char **cmd, int num_parts ) sb->who = g_strdup( cmd[5] ); } } - else if( strcmp( cmd[0], "ADD" ) == 0 ) - { - if( num_parts == 6 && strcmp( cmd[2], "RL" ) == 0 ) - { - GSList *l; - - http_decode( cmd[5] ); - - if( strchr( cmd[4], '@' ) == NULL ) - { - imcb_error( ic, "Syntax error" ); - imc_logout( ic, TRUE ); - return 0; - } - - /* We got added by someone. If we don't have this - person in permit/deny yet, inform the user. */ - for( l = ic->permit; l; l = l->next ) - if( g_strcasecmp( l->data, cmd[4] ) == 0 ) - return 1; - - for( l = ic->deny; l; l = l->next ) - if( g_strcasecmp( l->data, cmd[4] ) == 0 ) - return 1; - - msn_buddy_ask( ic, cmd[4], cmd[5] ); - } - else if( num_parts >= 6 && strcmp( cmd[2], "FL" ) == 0 ) - { - const char *group = NULL; - int num; - - if( cmd[6] != NULL && sscanf( cmd[6], "%d", &num ) == 1 && num < md->groupcount ) - group = md->grouplist[num]; - - http_decode( cmd[5] ); - imcb_add_buddy( ic, cmd[4], group ); - imcb_rename_buddy( ic, cmd[4], cmd[5] ); - } - } else if( strcmp( cmd[0], "OUT" ) == 0 ) { int allow_reconnect = TRUE; @@ -564,53 +479,20 @@ static int msn_ns_command( gpointer data, char **cmd, int num_parts ) imc_logout( ic, allow_reconnect ); return( 0 ); } -#if 0 - /* Discard this one completely for now since I don't care about the ack - and since MSN servers can apparently screw up the formatting. */ - else if( strcmp( cmd[0], "REA" ) == 0 ) - { - if( num_parts != 5 ) - { - imcb_error( ic, "Syntax error" ); - imc_logout( ic, TRUE ); - return( 0 ); - } - - if( g_strcasecmp( cmd[3], ic->acc->user ) == 0 ) - { - set_t *s; - - http_decode( cmd[4] ); - strncpy( ic->displayname, cmd[4], sizeof( ic->displayname ) ); - ic->displayname[sizeof(ic->displayname)-1] = 0; - - if( ( s = set_find( &ic->acc->set, "display_name" ) ) ) - { - g_free( s->value ); - s->value = g_strdup( cmd[4] ); - } - } - else - { - /* This is not supposed to happen, but let's handle it anyway... */ - http_decode( cmd[4] ); - imcb_rename_buddy( ic, cmd[3], cmd[4] ); - } - } -#endif else if( strcmp( cmd[0], "IPG" ) == 0 ) { imcb_error( ic, "Received IPG command, we don't handle them yet." ); - md->handler->msglen = atoi( cmd[1] ); + handler->msglen = atoi( cmd[1] ); - if( md->handler->msglen <= 0 ) + if( handler->msglen <= 0 ) { imcb_error( ic, "Syntax error" ); imc_logout( ic, TRUE ); return( 0 ); } } +#if 0 else if( strcmp( cmd[0], "ADG" ) == 0 ) { char *group = g_strdup( cmd[3] ); @@ -655,6 +537,26 @@ static int msn_ns_command( gpointer data, char **cmd, int num_parts ) } } } +#endif + else if( strcmp( cmd[0], "GCF" ) == 0 ) + { + /* Coming up is cmd[2] bytes of stuff we're supposed to + censore. Meh. */ + handler->msglen = atoi( cmd[2] ); + } + else if( strcmp( cmd[0], "UBX" ) == 0 ) + { + /* Status message. */ + if( num_parts >= 4 ) + handler->msglen = atoi( cmd[3] ); + } + else if( strcmp( cmd[0], "NOT" ) == 0 ) + { + /* Some kind of notification, poorly documented but + apparently used to announce address book changes. */ + if( num_parts >= 2 ) + handler->msglen = atoi( cmd[1] ); + } else if( isdigit( cmd[0][0] ) ) { int num = atoi( cmd[0] ); @@ -667,6 +569,10 @@ static int msn_ns_command( gpointer data, char **cmd, int num_parts ) imc_logout( ic, TRUE ); return( 0 ); } + + /* Oh yes, errors can have payloads too now. Discard them for now. */ + if( num_parts >= 3 ) + handler->msglen = atoi( cmd[2] ); } else { @@ -676,9 +582,9 @@ static int msn_ns_command( gpointer data, char **cmd, int num_parts ) return( 1 ); } -static int msn_ns_message( gpointer data, char *msg, int msglen, char **cmd, int num_parts ) +static int msn_ns_message( struct msn_handler_data *handler, char *msg, int msglen, char **cmd, int num_parts ) { - struct im_connection *ic = data; + struct im_connection *ic = handler->data; char *body; int blen = 0; @@ -764,13 +670,75 @@ static int msn_ns_message( gpointer data, char *msg, int msglen, char **cmd, int g_free( ct ); } } + else if( strcmp( cmd[0], "UBX" ) == 0 ) + { + struct xt_node *psm; + char *psm_text = NULL; + + psm = xt_from_string( msg ); + if( psm && strcmp( psm->name, "Data" ) == 0 && + ( psm = xt_find_node( psm->children, "PSM" ) ) ) + psm_text = psm->text; + + imcb_buddy_status_msg( ic, cmd[1], psm_text ); + xt_free_node( psm ); + } + else if( strcmp( cmd[0], "ADL" ) == 0 ) + { + struct xt_node *adl, *d, *c; + + if( !( adl = xt_from_string( msg ) ) ) + return 1; + + for( d = adl->children; d; d = d->next ) + { + char *dn; + if( strcmp( d->name, "d" ) != 0 || + ( dn = xt_find_attr( d, "n" ) ) == NULL ) + continue; + for( c = d->children; c; c = c->next ) + { + bee_user_t *bu; + struct msn_buddy_data *bd; + char *cn, *handle, *f, *l; + int flags; + + if( strcmp( c->name, "c" ) != 0 || + ( l = xt_find_attr( c, "l" ) ) == NULL || + ( cn = xt_find_attr( c, "n" ) ) == NULL ) + continue; + + handle = g_strdup_printf( "%s@%s", cn, dn ); + if( !( ( bu = bee_user_by_handle( ic->bee, ic, handle ) ) || + ( bu = bee_user_new( ic->bee, ic, handle, 0 ) ) ) ) + { + g_free( handle ); + continue; + } + g_free( handle ); + bd = bu->data; + + if( ( f = xt_find_attr( c, "f" ) ) ) + { + http_decode( f ); + imcb_rename_buddy( ic, bu->handle, f ); + } + + flags = atoi( l ) & 15; + if( bd->flags != flags ) + { + bd->flags = flags; + msn_buddy_ask( bu ); + } + } + } + } return( 1 ); } -static void msn_auth_got_passport_token( struct msn_auth_data *mad ) +void msn_auth_got_passport_token( struct im_connection *ic, const char *token, const char *error ) { - struct im_connection *ic = mad->data; struct msn_data *md; /* Dead connection? */ @@ -778,61 +746,129 @@ static void msn_auth_got_passport_token( struct msn_auth_data *mad ) return; md = ic->proto_data; - if( mad->token ) + + if( token ) { - char buf[1024]; - - g_snprintf( buf, sizeof( buf ), "USR %d TWN S %s\r\n", ++md->trId, mad->token ); - msn_write( ic, buf, strlen( buf ) ); + msn_ns_write( ic, -1, "USR %d SSO S %s %s\r\n", ++md->trId, md->tokens[0], token ); } else { - imcb_error( ic, "Error during Passport authentication: %s", mad->error ); + imcb_error( ic, "Error during Passport authentication: %s", error ); imc_logout( ic, TRUE ); } } -static gboolean msn_ns_got_display_name( struct im_connection *ic, char *name ) +void msn_auth_got_contact_list( struct im_connection *ic ) { - set_t *s; + struct msn_data *md; - if( ( s = set_find( &ic->acc->set, "display_name" ) ) == NULL ) - return FALSE; /* Shouldn't happen.. */ + /* Dead connection? */ + if( g_slist_find( msn_connections, ic ) == NULL ) + return; - http_decode( name ); + md = ic->proto_data; + msn_ns_write( ic, -1, "BLP %d %s\r\n", ++md->trId, "BL" ); +} + +static gboolean msn_ns_send_adl_1( gpointer key, gpointer value, gpointer data ) +{ + struct xt_node *adl = data, *d, *c; + struct bee_user *bu = value; + struct msn_buddy_data *bd = bu->data; + struct msn_data *md = bu->ic->proto_data; + char handle[strlen(bu->handle)]; + char *domain; + char l[4]; - if( s->value && strcmp( s->value, name ) == 0 ) + if( ( bd->flags & 7 ) == 0 || ( bd->flags & MSN_BUDDY_ADL_SYNCED ) ) + return FALSE; + + strcpy( handle, bu->handle ); + if( ( domain = strchr( handle, '@' ) ) == NULL ) /* WTF */ + return FALSE; + *domain = '\0'; + domain ++; + + if( ( d = adl->children ) == NULL || + g_strcasecmp( xt_find_attr( d, "n" ), domain ) != 0 ) { - return TRUE; - /* The names match, nothing to worry about. */ + d = xt_new_node( "d", NULL, NULL ); + xt_add_attr( d, "n", domain ); + xt_insert_child( adl, d ); } - else if( s->value != NULL && - ( strcmp( name, ic->acc->user ) == 0 || - set_getbool( &ic->acc->set, "local_display_name" ) ) ) - { - /* The server thinks our display name is our e-mail address - which is probably wrong, or the user *wants* us to do this: - Always use the locally set display_name. */ - return msn_set_display_name( ic, s->value ); + + g_snprintf( l, sizeof( l ), "%d", bd->flags & 7 ); + c = xt_new_node( "c", NULL, NULL ); + xt_add_attr( c, "n", handle ); + xt_add_attr( c, "l", l ); + xt_add_attr( c, "t", "1" ); /* 1 means normal, 4 means mobile? */ + xt_insert_child( d, c ); + + /* Do this in batches of 100. */ + bd->flags |= MSN_BUDDY_ADL_SYNCED; + return (--md->adl_todo % 140) == 0; +} + +static void msn_ns_send_adl( struct im_connection *ic ) +{ + struct xt_node *adl; + struct msn_data *md = ic->proto_data; + char *adls; + + adl = xt_new_node( "ml", NULL, NULL ); + xt_add_attr( adl, "l", "1" ); + g_tree_foreach( md->domaintree, msn_ns_send_adl_1, adl ); + if( adl->children == NULL ) + { + /* This tells the caller that we're done now. */ + md->adl_todo = -1; + xt_free_node( adl ); + return; } - else + + adls = xt_to_string( adl ); + msn_ns_write( ic, -1, "ADL %d %zd\r\n%s", ++md->trId, strlen( adls ), adls ); + g_free( adls ); +} + +static void msn_ns_send_adl_start( struct im_connection *ic ) +{ + struct msn_data *md; + GSList *l; + + /* Dead connection? */ + if( g_slist_find( msn_connections, ic ) == NULL ) + return; + + md = ic->proto_data; + md->adl_todo = 0; + for( l = ic->bee->users; l; l = l->next ) { - if( s->value && *s->value ) - imcb_log( ic, "BitlBee thinks your display name is `%s' but " - "the MSN server says it's `%s'. Using the MSN " - "server's name. Set local_display_name to true " - "to use the local name.", s->value, name ); + bee_user_t *bu = l->data; + struct msn_buddy_data *bd = bu->data; - if( g_utf8_validate( name, -1, NULL ) ) - { - g_free( s->value ); - s->value = g_strdup( name ); - } - else - { - imcb_log( ic, "Warning: Friendly name in server response was corrupted" ); - } + if( bu->ic != ic || ( bd->flags & 7 ) == 0 ) + continue; - return TRUE; + bd->flags &= ~MSN_BUDDY_ADL_SYNCED; + md->adl_todo++; } + + msn_ns_send_adl( ic ); +} + +int msn_ns_finish_login( struct im_connection *ic ) +{ + struct msn_data *md = ic->proto_data; + + if( ic->flags & OPT_LOGGED_IN ) + return 1; + + if( md->adl_todo < 0 ) + md->flags |= MSN_DONE_ADL; + + if( ( md->flags & MSN_DONE_ADL ) && ( md->flags & MSN_GOT_PROFILE ) ) + return msn_ns_set_display_name( ic, set_getstr( &ic->acc->set, "display_name" ) ); + else + return 1; } diff --git a/protocols/msn/passport.c b/protocols/msn/passport.c deleted file mode 100644 index 7c896db1..00000000 --- a/protocols/msn/passport.c +++ /dev/null @@ -1,172 +0,0 @@ -/** passport.c - * - * Functions to login to Microsoft Passport service for Messenger - * Copyright (C) 2004-2008 Wilmer van der Gaast <wilmer@gaast.net> - * - * This program is free software; you can redistribute it and/or modify - * it under the terms of the GNU General Public License version 2 - * as published by the Free Software Foundation - * - * This program is distributed in the hope that is will be useful, - * bit WITHOU ANY WARRANTY; without even the implied warranty of - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the - * GNU General Public License for more details. - * - * You should have received a copy of the GNU General Public License - * along with this program; if not, write to the Free Software - * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA - * - */ - -#include "http_client.h" -#include "passport.h" -#include "msn.h" -#include "bitlbee.h" -#include "url.h" -#include "misc.h" -#include "xmltree.h" -#include <ctype.h> -#include <errno.h> - -static int passport_get_token_real( struct msn_auth_data *mad ); -static void passport_get_token_ready( struct http_request *req ); - -int passport_get_token( gpointer func, gpointer data, char *username, char *password, char *cookie ) -{ - struct msn_auth_data *mad = g_new0( struct msn_auth_data, 1 ); - int i; - - mad->username = g_strdup( username ); - mad->password = g_strdup( password ); - mad->cookie = g_strdup( cookie ); - - mad->callback = func; - mad->data = data; - - mad->url = g_strdup( SOAP_AUTHENTICATION_URL ); - mad->ttl = 3; /* Max. # of redirects. */ - - /* HTTP-escape stuff and s/,/&/ */ - http_decode( mad->cookie ); - for( i = 0; mad->cookie[i]; i ++ ) - if( mad->cookie[i] == ',' ) - mad->cookie[i] = '&'; - - /* Microsoft doesn't allow password longer than 16 chars and silently - fails authentication if you give the "full version" of your passwd. */ - if( strlen( mad->password ) > MAX_PASSPORT_PWLEN ) - mad->password[MAX_PASSPORT_PWLEN] = 0; - - return passport_get_token_real( mad ); -} - -static int passport_get_token_real( struct msn_auth_data *mad ) -{ - char *post_payload, *post_request; - struct http_request *req; - url_t url; - - url_set( &url, mad->url ); - - post_payload = g_markup_printf_escaped( SOAP_AUTHENTICATION_PAYLOAD, - mad->username, - mad->password, - mad->cookie ); - - post_request = g_strdup_printf( SOAP_AUTHENTICATION_REQUEST, - url.file, url.host, - (int) strlen( post_payload ), - post_payload ); - - req = http_dorequest( url.host, url.port, 1, post_request, - passport_get_token_ready, mad ); - - g_free( post_request ); - g_free( post_payload ); - - return req != NULL; -} - -static xt_status passport_xt_extract_token( struct xt_node *node, gpointer data ); -static xt_status passport_xt_handle_fault( struct xt_node *node, gpointer data ); - -static const struct xt_handler_entry passport_xt_handlers[] = { - { "wsse:BinarySecurityToken", "wst:RequestedSecurityToken", passport_xt_extract_token }, - { "S:Fault", "S:Envelope", passport_xt_handle_fault }, - { NULL, NULL, NULL } -}; - -static void passport_get_token_ready( struct http_request *req ) -{ - struct msn_auth_data *mad = req->data; - struct xt_parser *parser; - - g_free( mad->url ); - g_free( mad->error ); - mad->url = mad->error = NULL; - - if( req->status_code == 200 ) - { - parser = xt_new( passport_xt_handlers, mad ); - xt_feed( parser, req->reply_body, req->body_size ); - xt_handle( parser, NULL, -1 ); - xt_free( parser ); - } - else - { - mad->error = g_strdup_printf( "HTTP error %d (%s)", req->status_code, - req->status_string ? req->status_string : "unknown" ); - } - - if( mad->error == NULL && mad->token == NULL ) - mad->error = g_strdup( "Could not parse Passport server response" ); - - if( mad->url && mad->token == NULL ) - { - passport_get_token_real( mad ); - } - else - { - mad->callback( mad ); - - g_free( mad->url ); - g_free( mad->username ); - g_free( mad->password ); - g_free( mad->cookie ); - g_free( mad->token ); - g_free( mad->error ); - g_free( mad ); - } -} - -static xt_status passport_xt_extract_token( struct xt_node *node, gpointer data ) -{ - struct msn_auth_data *mad = data; - char *s; - - if( ( s = xt_find_attr( node, "Id" ) ) && - ( strncmp( s, "Compact", 7 ) == 0 || - strncmp( s, "PPToken", 7 ) == 0 ) ) - mad->token = g_memdup( node->text, node->text_len + 1 ); - - return XT_HANDLED; -} - -static xt_status passport_xt_handle_fault( struct xt_node *node, gpointer data ) -{ - struct msn_auth_data *mad = data; - struct xt_node *code = xt_find_node( node->children, "faultcode" ); - struct xt_node *string = xt_find_node( node->children, "faultstring" ); - struct xt_node *redirect = xt_find_node( node->children, "psf:redirectUrl" ); - - if( redirect && redirect->text_len && mad->ttl-- > 0 ) - mad->url = g_memdup( redirect->text, redirect->text_len + 1 ); - - if( code == NULL || code->text_len == 0 ) - mad->error = g_strdup( "Unknown error" ); - else - mad->error = g_strdup_printf( "%s (%s)", code->text, string && string->text_len ? - string->text : "no description available" ); - - return XT_HANDLED; -} diff --git a/protocols/msn/passport.h b/protocols/msn/passport.h deleted file mode 100644 index 517d2e91..00000000 --- a/protocols/msn/passport.h +++ /dev/null @@ -1,113 +0,0 @@ -/* passport.h - * - * Functions to login to Microsoft Passport service for Messenger - * Copyright (C) 2004-2008 Wilmer van der Gaast <wilmer@gaast.net> - * - * This program is free software; you can redistribute it and/or modify - * it under the terms of the GNU General Public License version 2 - * as published by the Free Software Foundation - * - * This program is distributed in the hope that is will be useful, - * bit WITHOU ANY WARRANTY; without even the implied warranty of - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the - * GNU General Public License for more details. - * - * You should have received a copy of the GNU General Public License - * along with this program; if not, write to the Free Software - * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA - */ - -/* Thanks to http://msnpiki.msnfanatic.com/index.php/MSNP13:SOAPTweener - for the specs! */ - -#ifndef __PASSPORT_H__ -#define __PASSPORT_H__ - -#include <stdio.h> -#include <stdlib.h> -#include <string.h> -#include <sys/types.h> -#ifndef _WIN32 -#include <sys/socket.h> -#include <netinet/in.h> -#include <arpa/inet.h> -#include <unistd.h> -#endif -#include "nogaim.h" - -#define MAX_PASSPORT_PWLEN 16 - -struct msn_auth_data -{ - char *url; - int ttl; - - char *username; - char *password; - char *cookie; - - /* The end result, the only thing we'll really be interested in - once finished. */ - char *token; - char *error; /* Yeah, or that... */ - - void (*callback)( struct msn_auth_data *mad ); - gpointer data; -}; - -#define SOAP_AUTHENTICATION_URL "https://loginnet.passport.com/RST.srf" - -#define SOAP_AUTHENTICATION_REQUEST \ -"POST %s HTTP/1.0\r\n" \ -"Accept: text/*\r\n" \ -"User-Agent: BitlBee " BITLBEE_VERSION "\r\n" \ -"Host: %s\r\n" \ -"Content-Length: %d\r\n" \ -"Cache-Control: no-cache\r\n" \ -"\r\n" \ -"%s" - -#define SOAP_AUTHENTICATION_PAYLOAD \ -"<?xml version=\"1.0\" encoding=\"UTF-8\"?>" \ -"<Envelope xmlns=\"http://schemas.xmlsoap.org/soap/envelope/\" xmlns:wsse=\"http://schemas.xmlsoap.org/ws/2003/06/secext\" xmlns:saml=\"urn:oasis:names:tc:SAML:1.0:assertion\" xmlns:wsp=\"http://schemas.xmlsoap.org/ws/2002/12/policy\" xmlns:wsu=\"http://docs.oasis-open.org/wss/2004/01/oasis-200401-wss-wssecurity-utility-1.0.xsd\" xmlns:wsa=\"http://schemas.xmlsoap.org/ws/2004/03/addressing\" xmlns:wssc=\"http://schemas.xmlsoap.org/ws/2004/04/sc\" xmlns:wst=\"http://schemas.xmlsoap.org/ws/2004/04/trust\">" \ - "<Header>" \ - "<ps:AuthInfo xmlns:ps=\"http://schemas.microsoft.com/Passport/SoapServices/PPCRL\" Id=\"PPAuthInfo\">" \ - "<ps:HostingApp>{7108E71A-9926-4FCB-BCC9-9A9D3F32E423}</ps:HostingApp>" \ - "<ps:BinaryVersion>4</ps:BinaryVersion>" \ - "<ps:UIVersion>1</ps:UIVersion>" \ - "<ps:Cookies></ps:Cookies>" \ - "<ps:RequestParams>AQAAAAIAAABsYwQAAAAzMDg0</ps:RequestParams>" \ - "</ps:AuthInfo>" \ - "<wsse:Security>" \ - "<wsse:UsernameToken Id=\"user\">" \ - "<wsse:Username>%s</wsse:Username>" \ - "<wsse:Password>%s</wsse:Password>" \ - "</wsse:UsernameToken>" \ - "</wsse:Security>" \ - "</Header>" \ - "<Body>" \ - "<ps:RequestMultipleSecurityTokens xmlns:ps=\"http://schemas.microsoft.com/Passport/SoapServices/PPCRL\" Id=\"RSTS\">" \ - "<wst:RequestSecurityToken Id=\"RST0\">" \ - "<wst:RequestType>http://schemas.xmlsoap.org/ws/2004/04/security/trust/Issue</wst:RequestType>" \ - "<wsp:AppliesTo>" \ - "<wsa:EndpointReference>" \ - "<wsa:Address>http://Passport.NET/tb</wsa:Address>" \ - "</wsa:EndpointReference>" \ - "</wsp:AppliesTo>" \ - "</wst:RequestSecurityToken>" \ - "<wst:RequestSecurityToken Id=\"RST1\">" \ - "<wst:RequestType>http://schemas.xmlsoap.org/ws/2004/04/security/trust/Issue</wst:RequestType>" \ - "<wsp:AppliesTo>" \ - "<wsa:EndpointReference>" \ - "<wsa:Address>messenger.msn.com</wsa:Address>" \ - "</wsa:EndpointReference>" \ - "</wsp:AppliesTo>" \ - "<wsse:PolicyReference URI=\"?%s\"></wsse:PolicyReference>" \ - "</wst:RequestSecurityToken>" \ - "</ps:RequestMultipleSecurityTokens>" \ - "</Body>" \ -"</Envelope>" - -int passport_get_token( gpointer func, gpointer data, char *username, char *password, char *cookie ); - -#endif /* __PASSPORT_H__ */ diff --git a/protocols/msn/sb.c b/protocols/msn/sb.c index cb5789b8..898fb34f 100644 --- a/protocols/msn/sb.c +++ b/protocols/msn/sb.c @@ -1,7 +1,7 @@ /********************************************************************\ * BitlBee -- An IRC to other IM-networks gateway * * * - * Copyright 2002-2005 Wilmer van der Gaast and others * + * Copyright 2002-2010 Wilmer van der Gaast and others * \********************************************************************/ /* MSN module - Switchboard server callbacks and utilities */ @@ -26,33 +26,44 @@ #include <ctype.h> #include "nogaim.h" #include "msn.h" -#include "passport.h" #include "md5.h" +#include "soap.h" #include "invitation.h" static gboolean msn_sb_callback( gpointer data, gint source, b_input_condition cond ); -static int msn_sb_command( gpointer data, char **cmd, int num_parts ); -static int msn_sb_message( gpointer data, char *msg, int msglen, char **cmd, int num_parts ); +static int msn_sb_command( struct msn_handler_data *handler, char **cmd, int num_parts ); +static int msn_sb_message( struct msn_handler_data *handler, char *msg, int msglen, char **cmd, int num_parts ); -int msn_sb_write( struct msn_switchboard *sb, char *s, int len ) +int msn_sb_write( struct msn_switchboard *sb, const char *fmt, ... ) { + va_list params; + char *out; + size_t len; int st; - st = write( sb->fd, s, len ); + va_start( params, fmt ); + out = g_strdup_vprintf( fmt, params ); + va_end( params ); + + if( getenv( "BITLBEE_DEBUG" ) ) + fprintf( stderr, "->SB%d:%s", sb->fd, out ); + + len = strlen( out ); + st = write( sb->fd, out, len ); + g_free( out ); if( st != len ) { msn_sb_destroy( sb ); - return( 0 ); + return 0; } - return( 1 ); + return 1; } int msn_sb_write_msg( struct im_connection *ic, struct msn_message *m ) { struct msn_data *md = ic->proto_data; struct msn_switchboard *sb; - char buf[1024]; /* FIXME: *CHECK* the reliability of using spare sb's! */ if( ( sb = msn_sb_spare( ic ) ) ) @@ -60,8 +71,7 @@ int msn_sb_write_msg( struct im_connection *ic, struct msn_message *m ) debug( "Trying to use a spare switchboard to message %s", m->who ); sb->who = g_strdup( m->who ); - g_snprintf( buf, sizeof( buf ), "CAL %d %s\r\n", ++sb->trId, m->who ); - if( msn_sb_write( sb, buf, strlen( buf ) ) ) + if( msn_sb_write( sb, "CAL %d %s\r\n", ++sb->trId, m->who ) ) { /* He/She should join the switchboard soon, let's queue the message. */ sb->msgq = g_slist_append( sb->msgq, m ); @@ -72,8 +82,7 @@ int msn_sb_write_msg( struct im_connection *ic, struct msn_message *m ) debug( "Creating a new switchboard to message %s", m->who ); /* If we reach this line, there was no spare switchboard, so let's make one. */ - g_snprintf( buf, sizeof( buf ), "XFR %d SB\r\n", ++md->trId ); - if( !msn_write( ic, buf, strlen( buf ) ) ) + if( !msn_ns_write( ic, -1, "XFR %d SB\r\n", ++md->trId ) ) { g_free( m->who ); g_free( m->text ); @@ -164,7 +173,7 @@ int msn_sb_sendmessage( struct msn_switchboard *sb, char *text ) { if( sb->ready ) { - char *packet, *buf; + char *buf; int i, j; /* Build the message. Convert LF to CR-LF for normal messages. */ @@ -200,17 +209,15 @@ int msn_sb_sendmessage( struct msn_switchboard *sb, char *text ) } /* Build the final packet (MSG command + the message). */ - packet = g_strdup_printf( "MSG %d N %d\r\n%s", ++sb->trId, i, buf ); - g_free( buf ); - if( msn_sb_write( sb, packet, strlen( packet ) ) ) + if( msn_sb_write( sb, "MSG %d N %d\r\n%s", ++sb->trId, i, buf ) ) { - g_free( packet ); - return( 1 ); + g_free( buf ); + return 1; } else { - g_free( packet ); - return( 0 ); + g_free( buf ); + return 0; } } else if( sb->who ) @@ -325,7 +332,7 @@ gboolean msn_sb_connected( gpointer data, gint source, b_input_condition cond ) else g_snprintf( buf, sizeof( buf ), "ANS %d %s %s %d\r\n", ++sb->trId, ic->acc->user, sb->key, sb->session ); - if( msn_sb_write( sb, buf, strlen( buf ) ) ) + if( msn_sb_write( sb, "%s", buf ) ) sb->inp = b_input_add( sb->fd, B_EV_IO_READ, msn_sb_callback, sb ); else debug( "Error %d while connecting to switchboard server", 2 ); @@ -345,7 +352,6 @@ static gboolean msn_sb_callback( gpointer data, gint source, b_input_condition c if( sb->msgq != NULL ) { time_t now = time( NULL ); - char buf[1024]; if( now - md->first_sb_failure > 600 ) { @@ -377,8 +383,7 @@ static gboolean msn_sb_callback( gpointer data, gint source, b_input_condition c debug( "Moved queued messages back to the main queue, " "creating a new switchboard to retry." ); - g_snprintf( buf, sizeof( buf ), "XFR %d SB\r\n", ++md->trId ); - if( !msn_write( ic, buf, strlen( buf ) ) ) + if( !msn_ns_write( ic, -1, "XFR %d SB\r\n", ++md->trId ) ) return FALSE; } @@ -386,11 +391,10 @@ static gboolean msn_sb_callback( gpointer data, gint source, b_input_condition c return FALSE; } -static int msn_sb_command( gpointer data, char **cmd, int num_parts ) +static int msn_sb_command( struct msn_handler_data *handler, char **cmd, int num_parts ) { - struct msn_switchboard *sb = data; + struct msn_switchboard *sb = handler->data; struct im_connection *ic = sb->ic; - char buf[1024]; if( !num_parts ) { @@ -406,7 +410,7 @@ static int msn_sb_command( gpointer data, char **cmd, int num_parts ) } else if( strcmp( cmd[0], "USR" ) == 0 ) { - if( num_parts != 5 ) + if( num_parts < 5 ) { msn_sb_destroy( sb ); return( 0 ); @@ -419,20 +423,15 @@ static int msn_sb_command( gpointer data, char **cmd, int num_parts ) } if( sb->who ) - { - g_snprintf( buf, sizeof( buf ), "CAL %d %s\r\n", ++sb->trId, sb->who ); - return( msn_sb_write( sb, buf, strlen( buf ) ) ); - } + return msn_sb_write( sb, "CAL %d %s\r\n", ++sb->trId, sb->who ); else - { debug( "Just created a switchboard, but I don't know what to do with it." ); - } } else if( strcmp( cmd[0], "IRO" ) == 0 ) { int num, tot; - if( num_parts != 6 ) + if( num_parts < 6 ) { msn_sb_destroy( sb ); return( 0 ); @@ -469,7 +468,7 @@ static int msn_sb_command( gpointer data, char **cmd, int num_parts ) } else if( strcmp( cmd[0], "ANS" ) == 0 ) { - if( num_parts != 3 ) + if( num_parts < 3 ) { msn_sb_destroy( sb ); return( 0 ); @@ -488,7 +487,7 @@ static int msn_sb_command( gpointer data, char **cmd, int num_parts ) } else if( strcmp( cmd[0], "CAL" ) == 0 ) { - if( num_parts != 4 || !isdigit( cmd[3][0] ) ) + if( num_parts < 4 || !isdigit( cmd[3][0] ) ) { msn_sb_destroy( sb ); return( 0 ); @@ -498,7 +497,7 @@ static int msn_sb_command( gpointer data, char **cmd, int num_parts ) } else if( strcmp( cmd[0], "JOI" ) == 0 ) { - if( num_parts != 3 ) + if( num_parts < 3 ) { msn_sb_destroy( sb ); return( 0 ); @@ -559,7 +558,7 @@ static int msn_sb_command( gpointer data, char **cmd, int num_parts ) } else if( strcmp( cmd[0], "MSG" ) == 0 ) { - if( num_parts != 4 ) + if( num_parts < 4 ) { msn_sb_destroy( sb ); return( 0 ); @@ -624,7 +623,12 @@ static int msn_sb_command( gpointer data, char **cmd, int num_parts ) int num = atoi( cmd[0] ); const struct msn_status_code *err = msn_status_by_number( num ); - imcb_error( ic, "Error reported by switchboard server: %s", err->text ); + /* If the person is offline, send an offline message instead, + and don't report an error. */ + if( num == 217 ) + msn_soap_oim_send_queue( ic, &sb->msgq ); + else + imcb_error( ic, "Error reported by switchboard server: %s", err->text ); if( err->flags & STATUS_SB_FATAL ) { @@ -660,9 +664,9 @@ static int msn_sb_command( gpointer data, char **cmd, int num_parts ) return( 1 ); } -static int msn_sb_message( gpointer data, char *msg, int msglen, char **cmd, int num_parts ) +static int msn_sb_message( struct msn_handler_data *handler, char *msg, int msglen, char **cmd, int num_parts ) { - struct msn_switchboard *sb = data; + struct msn_switchboard *sb = handler->data; struct im_connection *ic = sb->ic; char *body; int blen = 0; @@ -740,8 +744,8 @@ static int msn_sb_message( gpointer data, char *msg, int msglen, char **cmd, int #endif else if( g_strncasecmp( ct, "application/x-msnmsgrp2p", 24 ) == 0 ) { - imcb_error( sb->ic, "Cannot receive file from %s: BitlBee does not " - "support msnmsgrp2p yet.", sb->who ); + /* Not currently implemented. Don't warn about it since + this seems to be used for avatars now. */ g_free( ct ); } else if( g_strncasecmp( ct, "text/x-msmsgscontrol", 20 ) == 0 ) diff --git a/protocols/msn/soap.c b/protocols/msn/soap.c new file mode 100644 index 00000000..a1b5be76 --- /dev/null +++ b/protocols/msn/soap.c @@ -0,0 +1,1076 @@ + /********************************************************************\ + * BitlBee -- An IRC to other IM-networks gateway * + * * + * Copyright 2002-2010 Wilmer van der Gaast and others * + \********************************************************************/ + +/* MSN module - All the SOAPy XML stuff. + Some manager at Microsoft apparently thought MSNP wasn't XMLy enough so + someone stepped up and changed that. This is the result. Kilobytes and + more kilobytes of XML vomit to transfer tiny bits of informaiton. */ + +/* + This program is free software; you can redistribute it and/or modify + it under the terms of the GNU General Public License as published by + the Free Software Foundation; either version 2 of the License, or + (at your option) any later version. + + This program is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU General Public License for more details. + + You should have received a copy of the GNU General Public License with + the Debian GNU/Linux distribution in /usr/share/common-licenses/GPL; + if not, write to the Free Software Foundation, Inc., 59 Temple Place, + Suite 330, Boston, MA 02111-1307 USA +*/ + +#include "http_client.h" +#include "soap.h" +#include "msn.h" +#include "bitlbee.h" +#include "url.h" +#include "misc.h" +#include "sha1.h" +#include "base64.h" +#include "xmltree.h" +#include <ctype.h> +#include <errno.h> + +/* This file tries to make SOAP stuff pretty simple to do by letting you just + provide a function to build a request, a few functions to parse various + parts of the response, and a function to run when the full response was + received and parsed. See the various examples below. */ + +typedef enum +{ + MSN_SOAP_OK, + MSN_SOAP_RETRY, + MSN_SOAP_REAUTH, + MSN_SOAP_ABORT, +} msn_soap_result_t; + +struct msn_soap_req_data; +typedef int (*msn_soap_func) ( struct msn_soap_req_data * ); + +struct msn_soap_req_data +{ + void *data; + struct im_connection *ic; + int ttl; + + char *url, *action, *payload; + struct http_request *http_req; + + const struct xt_handler_entry *xml_parser; + msn_soap_func build_request, handle_response, free_data; +}; + +static int msn_soap_send_request( struct msn_soap_req_data *req ); +static void msn_soap_free( struct msn_soap_req_data *soap_req ); +static void msn_soap_debug_print( const char *headers, const char *payload ); + +static int msn_soap_start( struct im_connection *ic, + void *data, + msn_soap_func build_request, + const struct xt_handler_entry *xml_parser, + msn_soap_func handle_response, + msn_soap_func free_data ) +{ + struct msn_soap_req_data *req = g_new0( struct msn_soap_req_data, 1 ); + + req->ic = ic; + req->data = data; + req->xml_parser = xml_parser; + req->build_request = build_request; + req->handle_response = handle_response; + req->free_data = free_data; + req->ttl = 3; + + return msn_soap_send_request( req ); +} + +static void msn_soap_handle_response( struct http_request *http_req ); + +static int msn_soap_send_request( struct msn_soap_req_data *soap_req ) +{ + char *http_req; + char *soap_action = NULL; + url_t url; + + soap_req->build_request( soap_req ); + + if( soap_req->action ) + soap_action = g_strdup_printf( "SOAPAction: \"%s\"\r\n", soap_req->action ); + + url_set( &url, soap_req->url ); + http_req = g_strdup_printf( SOAP_HTTP_REQUEST, url.file, url.host, + soap_action ? soap_action : "", + strlen( soap_req->payload ), soap_req->payload ); + + msn_soap_debug_print( http_req, soap_req->payload ); + + soap_req->http_req = http_dorequest( url.host, url.port, url.proto == PROTO_HTTPS, + http_req, msn_soap_handle_response, soap_req ); + + g_free( http_req ); + g_free( soap_action ); + + return soap_req->http_req != NULL; +} + +static void msn_soap_handle_response( struct http_request *http_req ) +{ + struct msn_soap_req_data *soap_req = http_req->data; + int st; + + if( g_slist_find( msn_connections, soap_req->ic ) == NULL ) + { + msn_soap_free( soap_req ); + return; + } + + msn_soap_debug_print( http_req->reply_headers, http_req->reply_body ); + + if( http_req->body_size > 0 ) + { + struct xt_parser *parser; + struct xt_node *err; + + parser = xt_new( soap_req->xml_parser, soap_req ); + xt_feed( parser, http_req->reply_body, http_req->body_size ); + if( http_req->status_code == 500 && + ( err = xt_find_path( parser->root, "soap:Body/soap:Fault/detail/errorcode" ) ) && + err->text_len > 0 ) + { + if( strcmp( err->text, "PassportAuthFail" ) == 0 ) + { + xt_free( parser ); + st = MSN_SOAP_REAUTH; + goto fail; + } + /* TODO: Handle/report other errors. */ + } + + xt_handle( parser, NULL, -1 ); + xt_free( parser ); + } + + st = soap_req->handle_response( soap_req ); + +fail: + g_free( soap_req->url ); + g_free( soap_req->action ); + g_free( soap_req->payload ); + soap_req->url = soap_req->action = soap_req->payload = NULL; + + if( st == MSN_SOAP_RETRY && --soap_req->ttl ) + { + msn_soap_send_request( soap_req ); + } + else if( st == MSN_SOAP_REAUTH ) + { + struct msn_data *md = soap_req->ic->proto_data; + + if( !( md->flags & MSN_REAUTHING ) ) + { + /* Nonce shouldn't actually be touched for re-auths. */ + msn_soap_passport_sso_request( soap_req->ic, "blaataap" ); + md->flags |= MSN_REAUTHING; + } + md->soapq = g_slist_append( md->soapq, soap_req ); + } + else + { + soap_req->free_data( soap_req ); + g_free( soap_req ); + } +} + +static char *msn_soap_abservice_build( const char *body_fmt, const char *scenario, const char *ticket, ... ) +{ + va_list params; + char *ret, *format, *body; + + format = g_markup_printf_escaped( SOAP_ABSERVICE_PAYLOAD, scenario, ticket ); + + va_start( params, ticket ); + body = g_strdup_vprintf( body_fmt, params ); + va_end( params ); + + ret = g_strdup_printf( format, body ); + g_free( body ); + g_free( format ); + + return ret; +} + +static void msn_soap_debug_print( const char *headers, const char *payload ) +{ + char *s; + + if( !getenv( "BITLBEE_DEBUG" ) ) + return; + + if( ( s = strstr( headers, "\r\n\r\n" ) ) ) + write( 1, s, s - headers + 4 ); + else + write( 1, headers, strlen( headers ) ); + +#ifdef DEBUG + { + struct xt_node *xt = xt_from_string( payload ); + if( xt ) + xt_print( xt ); + xt_free_node( xt ); + } +#endif +} + +int msn_soapq_flush( struct im_connection *ic, gboolean resend ) +{ + struct msn_data *md = ic->proto_data; + + while( md->soapq ) + { + if( resend ) + msn_soap_send_request( (struct msn_soap_req_data*) md->soapq->data ); + else + msn_soap_free( (struct msn_soap_req_data*) md->soapq->data ); + md->soapq = g_slist_remove( md->soapq, md->soapq->data ); + } + + return MSN_SOAP_OK; +} + +static void msn_soap_free( struct msn_soap_req_data *soap_req ) +{ + soap_req->free_data( soap_req ); + g_free( soap_req->url ); + g_free( soap_req->action ); + g_free( soap_req->payload ); + g_free( soap_req ); +} + + +/* passport_sso: Authentication MSNP15+ */ + +struct msn_soap_passport_sso_data +{ + char *nonce; + char *secret; + char *error; + char *redirect; +}; + +static int msn_soap_passport_sso_build_request( struct msn_soap_req_data *soap_req ) +{ + struct msn_soap_passport_sso_data *sd = soap_req->data; + struct im_connection *ic = soap_req->ic; + struct msn_data *md = ic->proto_data; + + if( sd->redirect ) + { + soap_req->url = sd->redirect; + sd->redirect = NULL; + } + else if( g_str_has_suffix( ic->acc->user, "@msn.com" ) ) + soap_req->url = g_strdup( SOAP_PASSPORT_SSO_URL_MSN ); + else + soap_req->url = g_strdup( SOAP_PASSPORT_SSO_URL ); + + soap_req->payload = g_markup_printf_escaped( SOAP_PASSPORT_SSO_PAYLOAD, + ic->acc->user, ic->acc->pass, md->pp_policy ); + + return MSN_SOAP_OK; +} + +static xt_status msn_soap_passport_sso_token( struct xt_node *node, gpointer data ) +{ + struct msn_soap_req_data *soap_req = data; + struct msn_soap_passport_sso_data *sd = soap_req->data; + struct msn_data *md = soap_req->ic->proto_data; + struct xt_node *p; + char *id; + + if( ( id = xt_find_attr( node, "Id" ) ) == NULL ) + return XT_HANDLED; + id += strlen( id ) - 1; + if( *id == '1' && + ( p = xt_find_path( node, "../../wst:RequestedProofToken/wst:BinarySecret" ) ) && + p->text ) + sd->secret = g_strdup( p->text ); + + *id -= '1'; + if( *id >= 0 && *id < sizeof( md->tokens ) / sizeof( md->tokens[0] ) ) + { + g_free( md->tokens[(int)*id] ); + md->tokens[(int)*id] = g_strdup( node->text ); + } + + return XT_HANDLED; +} + +static xt_status msn_soap_passport_failure( struct xt_node *node, gpointer data ) +{ + struct msn_soap_req_data *soap_req = data; + struct msn_soap_passport_sso_data *sd = soap_req->data; + struct xt_node *code = xt_find_node( node->children, "faultcode" ); + struct xt_node *string = xt_find_node( node->children, "faultstring" ); + struct xt_node *url; + + if( code == NULL || code->text_len == 0 ) + sd->error = g_strdup( "Unknown error" ); + else if( strcmp( code->text, "psf:Redirect" ) == 0 && + ( url = xt_find_node( node->children, "psf:redirectUrl" ) ) && + url->text_len > 0 ) + sd->redirect = g_strdup( url->text ); + else + sd->error = g_strdup_printf( "%s (%s)", code->text, string && string->text_len ? + string->text : "no description available" ); + + return XT_HANDLED; +} + +static const struct xt_handler_entry msn_soap_passport_sso_parser[] = { + { "wsse:BinarySecurityToken", "wst:RequestedSecurityToken", msn_soap_passport_sso_token }, + { "S:Fault", "S:Envelope", msn_soap_passport_failure }, + { NULL, NULL, NULL } +}; + +static char *msn_key_fuckery( char *key, int key_len, char *type ) +{ + unsigned char hash1[20+strlen(type)+1]; + unsigned char hash2[20]; + char *ret; + + sha1_hmac( key, key_len, type, 0, hash1 ); + strcpy( (char*) hash1 + 20, type ); + sha1_hmac( key, key_len, (char*) hash1, sizeof( hash1 ) - 1, hash2 ); + + /* This is okay as hash1 is read completely before it's overwritten. */ + sha1_hmac( key, key_len, (char*) hash1, 20, hash1 ); + sha1_hmac( key, key_len, (char*) hash1, sizeof( hash1 ) - 1, hash1 ); + + ret = g_malloc( 24 ); + memcpy( ret, hash2, 20 ); + memcpy( ret + 20, hash1, 4 ); + return ret; +} + +static int msn_soap_passport_sso_handle_response( struct msn_soap_req_data *soap_req ) +{ + struct msn_soap_passport_sso_data *sd = soap_req->data; + struct im_connection *ic = soap_req->ic; + struct msn_data *md = ic->proto_data; + char *key1, *key2, *key3, *blurb64; + int key1_len; + unsigned char *padnonce, *des3res; + struct + { + unsigned int uStructHeaderSize; // 28. Does not count data + unsigned int uCryptMode; // CRYPT_MODE_CBC (1) + unsigned int uCipherType; // TripleDES (0x6603) + unsigned int uHashType; // SHA1 (0x8004) + unsigned int uIVLen; // 8 + unsigned int uHashLen; // 20 + unsigned int uCipherLen; // 72 + unsigned char iv[8]; + unsigned char hash[20]; + unsigned char cipherbytes[72]; + } blurb = { + GUINT32_TO_LE( 28 ), + GUINT32_TO_LE( 1 ), + GUINT32_TO_LE( 0x6603 ), + GUINT32_TO_LE( 0x8004 ), + GUINT32_TO_LE( 8 ), + GUINT32_TO_LE( 20 ), + GUINT32_TO_LE( 72 ), + }; + + if( sd->redirect ) + return MSN_SOAP_RETRY; + + if( md->soapq ) + return msn_soapq_flush( ic, TRUE ); + + if( sd->secret == NULL ) + { + msn_auth_got_passport_token( ic, NULL, sd->error ); + return MSN_SOAP_OK; + } + + key1_len = base64_decode( sd->secret, (unsigned char**) &key1 ); + + key2 = msn_key_fuckery( key1, key1_len, "WS-SecureConversationSESSION KEY HASH" ); + key3 = msn_key_fuckery( key1, key1_len, "WS-SecureConversationSESSION KEY ENCRYPTION" ); + + sha1_hmac( key2, 24, sd->nonce, 0, blurb.hash ); + padnonce = g_malloc( strlen( sd->nonce ) + 8 ); + strcpy( (char*) padnonce, sd->nonce ); + memset( padnonce + strlen( sd->nonce ), 8, 8 ); + + random_bytes( blurb.iv, 8 ); + + ssl_des3_encrypt( (unsigned char*) key3, 24, padnonce, strlen( sd->nonce ) + 8, blurb.iv, &des3res ); + memcpy( blurb.cipherbytes, des3res, 72 ); + + blurb64 = base64_encode( (unsigned char*) &blurb, sizeof( blurb ) ); + msn_auth_got_passport_token( ic, blurb64, NULL ); + + g_free( padnonce ); + g_free( blurb64 ); + g_free( des3res ); + g_free( key1 ); + g_free( key2 ); + g_free( key3 ); + + return MSN_SOAP_OK; +} + +static int msn_soap_passport_sso_free_data( struct msn_soap_req_data *soap_req ) +{ + struct msn_soap_passport_sso_data *sd = soap_req->data; + + g_free( sd->nonce ); + g_free( sd->secret ); + g_free( sd->error ); + g_free( sd->redirect ); + + return MSN_SOAP_OK; +} + +int msn_soap_passport_sso_request( struct im_connection *ic, const char *nonce ) +{ + struct msn_soap_passport_sso_data *sd = g_new0( struct msn_soap_passport_sso_data, 1 ); + + sd->nonce = g_strdup( nonce ); + + return msn_soap_start( ic, sd, msn_soap_passport_sso_build_request, + msn_soap_passport_sso_parser, + msn_soap_passport_sso_handle_response, + msn_soap_passport_sso_free_data ); +} + + +/* oim_send: Sending offline messages */ + +struct msn_soap_oim_send_data +{ + char *to; + char *msg; + int number; + msn_soap_result_t need_retry; +}; + +static int msn_soap_oim_build_request( struct msn_soap_req_data *soap_req ) +{ + struct msn_soap_oim_send_data *oim = soap_req->data; + struct im_connection *ic = soap_req->ic; + struct msn_data *md = ic->proto_data; + char *display_name_b64; + + display_name_b64 = tobase64( set_getstr( &ic->acc->set, "display_name" ) ); + + soap_req->url = g_strdup( SOAP_OIM_SEND_URL ); + soap_req->action = g_strdup( SOAP_OIM_SEND_ACTION ); + soap_req->payload = g_markup_printf_escaped( SOAP_OIM_SEND_PAYLOAD, + ic->acc->user, display_name_b64, MSNP_VER, MSNP_BUILD, + oim->to, md->tokens[2], + MSNP11_PROD_ID, md->lock_key ? md->lock_key : "", + oim->number, oim->number, oim->msg ); + + g_free( display_name_b64 ); + oim->need_retry = MSN_SOAP_OK; + + return MSN_SOAP_OK; +} + +static xt_status msn_soap_oim_reauth( struct xt_node *node, gpointer data ) +{ + struct msn_soap_req_data *soap_req = data; + struct msn_soap_oim_send_data *oim = soap_req->data; + struct im_connection *ic = soap_req->ic; + struct msn_data *md = ic->proto_data; + struct xt_node *c; + + if( ( c = xt_find_node( node->children, "LockKeyChallenge" ) ) && c->text_len > 0 ) + { + g_free( md->lock_key ); + md->lock_key = msn_p11_challenge( c->text ); + oim->need_retry = MSN_SOAP_RETRY; + } + if( xt_find_node( node->children, "RequiredAuthPolicy" ) ) + { + oim->need_retry = MSN_SOAP_REAUTH; + } + + return XT_HANDLED; +} + +static const struct xt_handler_entry msn_soap_oim_send_parser[] = { + { "detail", "soap:Fault", msn_soap_oim_reauth }, + { NULL, NULL, NULL } +}; + +static int msn_soap_oim_handle_response( struct msn_soap_req_data *soap_req ) +{ + struct msn_soap_oim_send_data *oim = soap_req->data; + + if( soap_req->http_req->status_code == 500 && oim->need_retry && soap_req->ttl > 0 ) + { + return oim->need_retry; + } + else if( soap_req->http_req->status_code == 200 ) + { + imcb_log( soap_req->ic, "Offline message successfully delivered to %s", oim->to ); + return MSN_SOAP_OK; + } + else + { + imcb_log( soap_req->ic, "Failed to deliver offline message to %s:\n%s", oim->to, oim->msg ); + return MSN_SOAP_ABORT; + } +} + +static int msn_soap_oim_free_data( struct msn_soap_req_data *soap_req ) +{ + struct msn_soap_oim_send_data *oim = soap_req->data; + + g_free( oim->to ); + g_free( oim->msg ); + g_free( oim ); + + return MSN_SOAP_OK; +} + +int msn_soap_oim_send( struct im_connection *ic, const char *to, const char *msg ) +{ + struct msn_soap_oim_send_data *data; + + data = g_new0( struct msn_soap_oim_send_data, 1 ); + data->to = g_strdup( to ); + data->msg = tobase64( msg ); + data->number = 1; + + return msn_soap_start( ic, data, msn_soap_oim_build_request, + msn_soap_oim_send_parser, + msn_soap_oim_handle_response, + msn_soap_oim_free_data ); +} + +int msn_soap_oim_send_queue( struct im_connection *ic, GSList **msgq ) +{ + GSList *l; + char *n = NULL; + + for( l = *msgq; l; l = l->next ) + { + struct msn_message *m = l->data; + + if( n == NULL ) + n = m->who; + if( strcmp( n, m->who ) == 0 ) + msn_soap_oim_send( ic, m->who, m->text ); + } + + while( *msgq != NULL ) + { + struct msn_message *m = (*msgq)->data; + + g_free( m->who ); + g_free( m->text ); + g_free( m ); + + *msgq = g_slist_remove( *msgq, m ); + } + + return 1; +} + + +/* memlist: Fetching the membership list (NOT address book) */ + +static int msn_soap_memlist_build_request( struct msn_soap_req_data *soap_req ) +{ + struct msn_data *md = soap_req->ic->proto_data; + + soap_req->url = g_strdup( SOAP_MEMLIST_URL ); + soap_req->action = g_strdup( SOAP_MEMLIST_ACTION ); + soap_req->payload = msn_soap_abservice_build( SOAP_MEMLIST_PAYLOAD, "Initial", md->tokens[1] ); + + return 1; +} + +static xt_status msn_soap_memlist_member( struct xt_node *node, gpointer data ) +{ + bee_user_t *bu; + struct msn_buddy_data *bd; + struct xt_node *p; + char *role = NULL, *handle = NULL; + struct msn_soap_req_data *soap_req = data; + struct im_connection *ic = soap_req->ic; + + if( ( p = xt_find_path( node, "../../MemberRole" ) ) ) + role = p->text; + + if( ( p = xt_find_node( node->children, "PassportName" ) ) ) + handle = p->text; + + if( !role || !handle || + !( ( bu = bee_user_by_handle( ic->bee, ic, handle ) ) || + ( bu = bee_user_new( ic->bee, ic, handle, 0 ) ) ) ) + return XT_HANDLED; + + bd = bu->data; + if( strcmp( role, "Allow" ) == 0 ) + { + bd->flags |= MSN_BUDDY_AL; + ic->permit = g_slist_prepend( ic->permit, g_strdup( handle ) ); + } + else if( strcmp( role, "Block" ) == 0 ) + { + bd->flags |= MSN_BUDDY_BL; + ic->deny = g_slist_prepend( ic->deny, g_strdup( handle ) ); + } + else if( strcmp( role, "Reverse" ) == 0 ) + bd->flags |= MSN_BUDDY_RL; + else if( strcmp( role, "Pending" ) == 0 ) + bd->flags |= MSN_BUDDY_PL; + + if( getenv( "BITLBEE_DEBUG" ) ) + printf( "%p %s %d\n", bu, handle, bd->flags ); + + return XT_HANDLED; +} + +static const struct xt_handler_entry msn_soap_memlist_parser[] = { + { "Member", "Members", msn_soap_memlist_member }, + { NULL, NULL, NULL } +}; + +static int msn_soap_memlist_handle_response( struct msn_soap_req_data *soap_req ) +{ + msn_soap_addressbook_request( soap_req->ic ); + + return MSN_SOAP_OK; +} + +static int msn_soap_memlist_free_data( struct msn_soap_req_data *soap_req ) +{ + return 0; +} + +int msn_soap_memlist_request( struct im_connection *ic ) +{ + return msn_soap_start( ic, NULL, msn_soap_memlist_build_request, + msn_soap_memlist_parser, + msn_soap_memlist_handle_response, + msn_soap_memlist_free_data ); +} + +/* Variant: Adding/Removing people */ +struct msn_soap_memlist_edit_data +{ + char *handle; + gboolean add; + msn_buddy_flags_t list; +}; + +static int msn_soap_memlist_edit_build_request( struct msn_soap_req_data *soap_req ) +{ + struct msn_data *md = soap_req->ic->proto_data; + struct msn_soap_memlist_edit_data *med = soap_req->data; + char *add, *scenario, *list; + + soap_req->url = g_strdup( SOAP_MEMLIST_URL ); + if( med->add ) + { + soap_req->action = g_strdup( SOAP_MEMLIST_ADD_ACTION ); + add = "Add"; + } + else + { + soap_req->action = g_strdup( SOAP_MEMLIST_DEL_ACTION ); + add = "Delete"; + } + switch( med->list ) + { + case MSN_BUDDY_AL: + scenario = "BlockUnblock"; + list = "Allow"; + break; + case MSN_BUDDY_BL: + scenario = "BlockUnblock"; + list = "Block"; + break; + case MSN_BUDDY_RL: + scenario = "Timer"; + list = "Reverse"; + break; + case MSN_BUDDY_PL: + default: + scenario = "Timer"; + list = "Pending"; + break; + } + soap_req->payload = msn_soap_abservice_build( SOAP_MEMLIST_EDIT_PAYLOAD, + scenario, md->tokens[1], add, list, med->handle, add ); + + return 1; +} + +static int msn_soap_memlist_edit_handle_response( struct msn_soap_req_data *soap_req ) +{ + return MSN_SOAP_OK; +} + +static int msn_soap_memlist_edit_free_data( struct msn_soap_req_data *soap_req ) +{ + struct msn_soap_memlist_edit_data *med = soap_req->data; + + g_free( med->handle ); + g_free( med ); + + return 0; +} + +int msn_soap_memlist_edit( struct im_connection *ic, const char *handle, gboolean add, int list ) +{ + struct msn_soap_memlist_edit_data *med; + + med = g_new0( struct msn_soap_memlist_edit_data, 1 ); + med->handle = g_strdup( handle ); + med->add = add; + med->list = list; + + return msn_soap_start( ic, med, msn_soap_memlist_edit_build_request, + NULL, + msn_soap_memlist_edit_handle_response, + msn_soap_memlist_edit_free_data ); +} + + +/* addressbook: Fetching the membership list (NOT address book) */ + +static int msn_soap_addressbook_build_request( struct msn_soap_req_data *soap_req ) +{ + struct msn_data *md = soap_req->ic->proto_data; + + soap_req->url = g_strdup( SOAP_ADDRESSBOOK_URL ); + soap_req->action = g_strdup( SOAP_ADDRESSBOOK_ACTION ); + soap_req->payload = msn_soap_abservice_build( SOAP_ADDRESSBOOK_PAYLOAD, "Initial", md->tokens[1] ); + + return 1; +} + +static xt_status msn_soap_addressbook_group( struct xt_node *node, gpointer data ) +{ + struct xt_node *p; + char *id = NULL, *name = NULL; + struct msn_soap_req_data *soap_req = data; + struct msn_data *md = soap_req->ic->proto_data; + + if( ( p = xt_find_path( node, "../groupId" ) ) ) + id = p->text; + + if( ( p = xt_find_node( node->children, "name" ) ) ) + name = p->text; + + if( id && name ) + { + struct msn_group *mg = g_new0( struct msn_group, 1 ); + mg->id = g_strdup( id ); + mg->name = g_strdup( name ); + md->groups = g_slist_prepend( md->groups, mg ); + } + + if( getenv( "BITLBEE_DEBUG" ) ) + printf( "%s %s\n", id, name ); + + return XT_HANDLED; +} + +static xt_status msn_soap_addressbook_contact( struct xt_node *node, gpointer data ) +{ + bee_user_t *bu; + struct msn_buddy_data *bd; + struct xt_node *p; + char *id = NULL, *type = NULL, *handle = NULL, *is_msgr = "false", + *display_name = NULL, *group_id = NULL; + struct msn_soap_req_data *soap_req = data; + struct im_connection *ic = soap_req->ic; + struct msn_group *group; + + if( ( p = xt_find_path( node, "../contactId" ) ) ) + id = p->text; + if( ( p = xt_find_node( node->children, "contactType" ) ) ) + type = p->text; + if( ( p = xt_find_node( node->children, "passportName" ) ) ) + handle = p->text; + if( ( p = xt_find_node( node->children, "displayName" ) ) ) + display_name = p->text; + if( ( p = xt_find_node( node->children, "isMessengerUser" ) ) ) + is_msgr = p->text; + if( ( p = xt_find_path( node, "groupIds/guid" ) ) ) + group_id = p->text; + + if( type && g_strcasecmp( type, "me" ) == 0 ) + { + set_t *set = set_find( &ic->acc->set, "display_name" ); + g_free( set->value ); + set->value = g_strdup( display_name ); + + /* Try to fetch the profile; if the user has one, that's where + we can find the persistent display_name. */ + if( ( p = xt_find_node( node->children, "CID" ) ) && p->text ) + msn_soap_profile_get( ic, p->text ); + + return XT_HANDLED; + } + + if( !bool2int( is_msgr ) || handle == NULL ) + return XT_HANDLED; + + if( !( bu = bee_user_by_handle( ic->bee, ic, handle ) ) && + !( bu = bee_user_new( ic->bee, ic, handle, 0 ) ) ) + return XT_HANDLED; + + bd = bu->data; + bd->flags |= MSN_BUDDY_FL; + g_free( bd->cid ); + bd->cid = g_strdup( id ); + + imcb_rename_buddy( ic, handle, display_name ); + + if( group_id && ( group = msn_group_by_id( ic, group_id ) ) ) + imcb_add_buddy( ic, handle, group->name ); + + if( getenv( "BITLBEE_DEBUG" ) ) + printf( "%s %s %s %s\n", id, type, handle, display_name ); + + return XT_HANDLED; +} + +static const struct xt_handler_entry msn_soap_addressbook_parser[] = { + { "contactInfo", "Contact", msn_soap_addressbook_contact }, + { "groupInfo", "Group", msn_soap_addressbook_group }, + { NULL, NULL, NULL } +}; + +static int msn_soap_addressbook_handle_response( struct msn_soap_req_data *soap_req ) +{ + GSList *l; + + for( l = soap_req->ic->bee->users; l; l = l->next ) + { + struct bee_user *bu = l->data; + + if( bu->ic == soap_req->ic ) + msn_buddy_ask( bu ); + } + + msn_auth_got_contact_list( soap_req->ic ); + + return MSN_SOAP_OK; +} + +static int msn_soap_addressbook_free_data( struct msn_soap_req_data *soap_req ) +{ + return 0; +} + +int msn_soap_addressbook_request( struct im_connection *ic ) +{ + return msn_soap_start( ic, NULL, msn_soap_addressbook_build_request, + msn_soap_addressbook_parser, + msn_soap_addressbook_handle_response, + msn_soap_addressbook_free_data ); +} + +/* Variant: Change our display name. */ +static int msn_soap_ab_namechange_build_request( struct msn_soap_req_data *soap_req ) +{ + struct msn_data *md = soap_req->ic->proto_data; + + soap_req->url = g_strdup( SOAP_ADDRESSBOOK_URL ); + soap_req->action = g_strdup( SOAP_AB_NAMECHANGE_ACTION ); + soap_req->payload = msn_soap_abservice_build( SOAP_AB_NAMECHANGE_PAYLOAD, + "Timer", md->tokens[1], (char *) soap_req->data ); + + return 1; +} + +static int msn_soap_ab_namechange_handle_response( struct msn_soap_req_data *soap_req ) +{ + /* TODO: Ack the change? Not sure what the NAKs look like.. */ + return MSN_SOAP_OK; +} + +static int msn_soap_ab_namechange_free_data( struct msn_soap_req_data *soap_req ) +{ + g_free( soap_req->data ); + return 0; +} + +int msn_soap_addressbook_set_display_name( struct im_connection *ic, const char *new ) +{ + return msn_soap_start( ic, g_strdup( new ), + msn_soap_ab_namechange_build_request, + NULL, + msn_soap_ab_namechange_handle_response, + msn_soap_ab_namechange_free_data ); +} + +/* Add a contact. */ +static int msn_soap_ab_contact_add_build_request( struct msn_soap_req_data *soap_req ) +{ + struct msn_data *md = soap_req->ic->proto_data; + bee_user_t *bu = soap_req->data; + + soap_req->url = g_strdup( SOAP_ADDRESSBOOK_URL ); + soap_req->action = g_strdup( SOAP_AB_CONTACT_ADD_ACTION ); + soap_req->payload = msn_soap_abservice_build( SOAP_AB_CONTACT_ADD_PAYLOAD, + "ContactSave", md->tokens[1], bu->handle, bu->fullname ? bu->fullname : bu->handle ); + + return 1; +} + +static xt_status msn_soap_ab_contact_add_cid( struct xt_node *node, gpointer data ) +{ + struct msn_soap_req_data *soap_req = data; + bee_user_t *bu = soap_req->data; + struct msn_buddy_data *bd = bu->data; + + g_free( bd->cid ); + bd->cid = g_strdup( node->text ); + + return XT_HANDLED; +} + +static const struct xt_handler_entry msn_soap_ab_contact_add_parser[] = { + { "guid", "ABContactAddResult", msn_soap_ab_contact_add_cid }, + { NULL, NULL, NULL } +}; + +static int msn_soap_ab_contact_add_handle_response( struct msn_soap_req_data *soap_req ) +{ + /* TODO: Ack the change? Not sure what the NAKs look like.. */ + return MSN_SOAP_OK; +} + +static int msn_soap_ab_contact_add_free_data( struct msn_soap_req_data *soap_req ) +{ + return 0; +} + +int msn_soap_ab_contact_add( struct im_connection *ic, bee_user_t *bu ) +{ + return msn_soap_start( ic, bu, + msn_soap_ab_contact_add_build_request, + msn_soap_ab_contact_add_parser, + msn_soap_ab_contact_add_handle_response, + msn_soap_ab_contact_add_free_data ); +} + +/* Remove a contact. */ +static int msn_soap_ab_contact_del_build_request( struct msn_soap_req_data *soap_req ) +{ + struct msn_data *md = soap_req->ic->proto_data; + const char *cid = soap_req->data; + + soap_req->url = g_strdup( SOAP_ADDRESSBOOK_URL ); + soap_req->action = g_strdup( SOAP_AB_CONTACT_DEL_ACTION ); + soap_req->payload = msn_soap_abservice_build( SOAP_AB_CONTACT_DEL_PAYLOAD, + "Timer", md->tokens[1], cid ); + + return 1; +} + +static int msn_soap_ab_contact_del_handle_response( struct msn_soap_req_data *soap_req ) +{ + /* TODO: Ack the change? Not sure what the NAKs look like.. */ + return MSN_SOAP_OK; +} + +static int msn_soap_ab_contact_del_free_data( struct msn_soap_req_data *soap_req ) +{ + g_free( soap_req->data ); + return 0; +} + +int msn_soap_ab_contact_del( struct im_connection *ic, bee_user_t *bu ) +{ + struct msn_buddy_data *bd = bu->data; + + return msn_soap_start( ic, g_strdup( bd->cid ), + msn_soap_ab_contact_del_build_request, + NULL, + msn_soap_ab_contact_del_handle_response, + msn_soap_ab_contact_del_free_data ); +} + + + +/* Storage stuff: Fetch profile. */ +static int msn_soap_profile_get_build_request( struct msn_soap_req_data *soap_req ) +{ + struct msn_data *md = soap_req->ic->proto_data; + + soap_req->url = g_strdup( SOAP_STORAGE_URL ); + soap_req->action = g_strdup( SOAP_PROFILE_GET_ACTION ); + soap_req->payload = g_markup_printf_escaped( SOAP_PROFILE_GET_PAYLOAD, + md->tokens[3], (char*) soap_req->data ); + + return 1; +} + +static xt_status msn_soap_profile_get_result( struct xt_node *node, gpointer data ) +{ + struct msn_soap_req_data *soap_req = data; + struct im_connection *ic = soap_req->ic; + struct msn_data *md = soap_req->ic->proto_data; + struct xt_node *dn; + + if( ( dn = xt_find_node( node->children, "DisplayName" ) ) && dn->text ) + { + set_t *set = set_find( &ic->acc->set, "display_name" ); + g_free( set->value ); + set->value = g_strdup( dn->text ); + + md->flags |= MSN_GOT_PROFILE_DN; + } + + return XT_HANDLED; +} + +static const struct xt_handler_entry msn_soap_profile_get_parser[] = { + { "ExpressionProfile", "GetProfileResult", msn_soap_profile_get_result }, + { NULL, NULL, NULL } +}; + +static int msn_soap_profile_get_handle_response( struct msn_soap_req_data *soap_req ) +{ + struct msn_data *md = soap_req->ic->proto_data; + + md->flags |= MSN_GOT_PROFILE; + msn_ns_finish_login( soap_req->ic ); + + return MSN_SOAP_OK; +} + +static int msn_soap_profile_get_free_data( struct msn_soap_req_data *soap_req ) +{ + g_free( soap_req->data ); + return 0; +} + +int msn_soap_profile_get( struct im_connection *ic, const char *cid ) +{ + return msn_soap_start( ic, g_strdup( cid ), + msn_soap_profile_get_build_request, + msn_soap_profile_get_parser, + msn_soap_profile_get_handle_response, + msn_soap_profile_get_free_data ); +} diff --git a/protocols/msn/soap.h b/protocols/msn/soap.h new file mode 100644 index 00000000..f49c86c4 --- /dev/null +++ b/protocols/msn/soap.h @@ -0,0 +1,349 @@ + /********************************************************************\ + * BitlBee -- An IRC to other IM-networks gateway * + * * + * Copyright 2002-2010 Wilmer van der Gaast and others * + \********************************************************************/ + +/* MSN module - All the SOAPy XML stuff. + Some manager at Microsoft apparently thought MSNP wasn't XMLy enough so + someone stepped up and changed that. This is the result. Kilobytes and + more kilobytes of XML vomit to transfer tiny bits of informaiton. */ + +/* + This program is free software; you can redistribute it and/or modify + it under the terms of the GNU General Public License as published by + the Free Software Foundation; either version 2 of the License, or + (at your option) any later version. + + This program is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU General Public License for more details. + + You should have received a copy of the GNU General Public License with + the Debian GNU/Linux distribution in /usr/share/common-licenses/GPL; + if not, write to the Free Software Foundation, Inc., 59 Temple Place, + Suite 330, Boston, MA 02111-1307 USA +*/ + +/* Thanks to http://msnpiki.msnfanatic.com/ for lots of info on this! */ + +#ifndef __SOAP_H__ +#define __SOAP_H__ + +#include <stdio.h> +#include <stdlib.h> +#include <string.h> +#include <sys/types.h> +#ifndef _WIN32 +#include <sys/socket.h> +#include <netinet/in.h> +#include <arpa/inet.h> +#include <unistd.h> +#endif +#include "nogaim.h" + + +int msn_soapq_flush( struct im_connection *ic, gboolean resend ); + + +#define SOAP_HTTP_REQUEST \ +"POST %s HTTP/1.0\r\n" \ +"Host: %s\r\n" \ +"Accept: */*\r\n" \ +"User-Agent: BitlBee " BITLBEE_VERSION "\r\n" \ +"Content-Type: text/xml; charset=utf-8\r\n" \ +"%s" \ +"Content-Length: %zd\r\n" \ +"Cache-Control: no-cache\r\n" \ +"\r\n" \ +"%s" + + +#define SOAP_PASSPORT_SSO_URL "https://login.live.com/RST.srf" +#define SOAP_PASSPORT_SSO_URL_MSN "https://msnia.login.live.com/pp800/RST.srf" + +#define SOAP_PASSPORT_SSO_PAYLOAD \ +"<Envelope xmlns=\"http://schemas.xmlsoap.org/soap/envelope/\" " \ + "xmlns:wsse=\"http://schemas.xmlsoap.org/ws/2003/06/secext\" " \ + "xmlns:saml=\"urn:oasis:names:tc:SAML:1.0:assertion\" " \ + "xmlns:wsp=\"http://schemas.xmlsoap.org/ws/2002/12/policy\" " \ + "xmlns:wsu=\"http://docs.oasis-open.org/wss/2004/01/oasis-200401-wss-wssecurity-utility-1.0.xsd\" " \ + "xmlns:wsa=\"http://schemas.xmlsoap.org/ws/2004/03/addressing\" " \ + "xmlns:wssc=\"http://schemas.xmlsoap.org/ws/2004/04/sc\" " \ + "xmlns:wst=\"http://schemas.xmlsoap.org/ws/2004/04/trust\">" \ + "<Header>" \ + "<ps:AuthInfo " \ + "xmlns:ps=\"http://schemas.microsoft.com/Passport/SoapServices/PPCRL\" " \ + "Id=\"PPAuthInfo\">" \ + "<ps:HostingApp>{7108E71A-9926-4FCB-BCC9-9A9D3F32E423}</ps:HostingApp>" \ + "<ps:BinaryVersion>4</ps:BinaryVersion>" \ + "<ps:UIVersion>1</ps:UIVersion>" \ + "<ps:Cookies></ps:Cookies>" \ + "<ps:RequestParams>AQAAAAIAAABsYwQAAAAxMDMz</ps:RequestParams>" \ + "</ps:AuthInfo>" \ + "<wsse:Security>" \ + "<wsse:UsernameToken Id=\"user\">" \ + "<wsse:Username>%s</wsse:Username>" \ + "<wsse:Password>%s</wsse:Password>" \ + "</wsse:UsernameToken>" \ + "</wsse:Security>" \ + "</Header>" \ + "<Body>" \ + "<ps:RequestMultipleSecurityTokens " \ + "xmlns:ps=\"http://schemas.microsoft.com/Passport/SoapServices/PPCRL\" " \ + "Id=\"RSTS\">" \ + "<wst:RequestSecurityToken Id=\"RST0\">" \ + "<wst:RequestType>http://schemas.xmlsoap.org/ws/2004/04/security/trust/Issue</wst:RequestType>" \ + "<wsp:AppliesTo>" \ + "<wsa:EndpointReference>" \ + "<wsa:Address>http://Passport.NET/tb</wsa:Address>" \ + "</wsa:EndpointReference>" \ + "</wsp:AppliesTo>" \ + "</wst:RequestSecurityToken>" \ + "<wst:RequestSecurityToken Id=\"RST1\">" \ + "<wst:RequestType>http://schemas.xmlsoap.org/ws/2004/04/security/trust/Issue</wst:RequestType>" \ + "<wsp:AppliesTo>" \ + "<wsa:EndpointReference>" \ + "<wsa:Address>messengerclear.live.com</wsa:Address>" \ + "</wsa:EndpointReference>" \ + "</wsp:AppliesTo>" \ + "<wsse:PolicyReference URI=\"%s\"></wsse:PolicyReference>" \ + "</wst:RequestSecurityToken>" \ + "<wst:RequestSecurityToken Id=\"RST2\">" \ + "<wst:RequestType>http://schemas.xmlsoap.org/ws/2004/04/security/trust/Issue</wst:RequestType>" \ + "<wsp:AppliesTo>" \ + "<wsa:EndpointReference>" \ + "<wsa:Address>contacts.msn.com</wsa:Address>" \ + "</wsa:EndpointReference>" \ + "</wsp:AppliesTo>" \ + "<wsse:PolicyReference xmlns=\"http://schemas.xmlsoap.org/ws/2003/06/secext\" URI=\"MBI\"></wsse:PolicyReference>" \ + "</wst:RequestSecurityToken>" \ + "<wst:RequestSecurityToken Id=\"RST3\">" \ + "<wst:RequestType>http://schemas.xmlsoap.org/ws/2004/04/security/trust/Issue</wst:RequestType>" \ + "<wsp:AppliesTo>" \ + "<wsa:EndpointReference>" \ + "<wsa:Address>messengersecure.live.com</wsa:Address>" \ + "</wsa:EndpointReference>" \ + "</wsp:AppliesTo>" \ + "<wsse:PolicyReference xmlns=\"http://schemas.xmlsoap.org/ws/2003/06/secext\" URI=\"MBI_SSL\"></wsse:PolicyReference>" \ + "</wst:RequestSecurityToken>" \ + "<wst:RequestSecurityToken Id=\"RST4\">" \ + "<wst:RequestType>http://schemas.xmlsoap.org/ws/2004/04/security/trust/Issue</wst:RequestType>" \ + "<wsp:AppliesTo>" \ + "<wsa:EndpointReference>" \ + "<wsa:Address>storage.msn.com</wsa:Address>" \ + "</wsa:EndpointReference>" \ + "</wsp:AppliesTo>" \ + "<wsse:PolicyReference xmlns=\"http://schemas.xmlsoap.org/ws/2003/06/secext\" URI=\"MBI_SSL\"></wsse:PolicyReference>" \ + "</wst:RequestSecurityToken>" \ + "</ps:RequestMultipleSecurityTokens>" \ + "</Body>" \ +"</Envelope>" + +int msn_soap_passport_sso_request( struct im_connection *ic, const char *nonce ); + + +#define SOAP_OIM_SEND_URL "https://ows.messenger.msn.com/OimWS/oim.asmx" +#define SOAP_OIM_SEND_ACTION "http://messenger.live.com/ws/2006/09/oim/Store2" + +#define SOAP_OIM_SEND_PAYLOAD \ +"<?xml version=\"1.0\" encoding=\"utf-8\"?>" \ +"<soap:Envelope xmlns:xsi=\"http://www.w3.org/2001/XMLSchema-instance\" xmlns:xsd=\"http://www.w3.org/2001/XMLSchema\" xmlns:soap=\"http://schemas.xmlsoap.org/soap/envelope/\">" \ +"<soap:Header>" \ + "<From memberName=\"%s\" friendlyName=\"=?utf-8?B?%s?=\" xml:lang=\"nl-nl\" proxy=\"MSNMSGR\" xmlns=\"http://messenger.msn.com/ws/2004/09/oim/\" msnpVer=\"%s\" buildVer=\"%s\"/>" \ + "<To memberName=\"%s\" xmlns=\"http://messenger.msn.com/ws/2004/09/oim/\"/>" \ + "<Ticket passport=\"%s\" appid=\"%s\" lockkey=\"%s\" xmlns=\"http://messenger.msn.com/ws/2004/09/oim/\"/>" \ + "<Sequence xmlns=\"http://schemas.xmlsoap.org/ws/2003/03/rm\">" \ + "<Identifier xmlns=\"http://schemas.xmlsoap.org/ws/2002/07/utility\">http://messenger.msn.com</Identifier>" \ + "<MessageNumber>%d</MessageNumber>" \ + "</Sequence>" \ +"</soap:Header>" \ +"<soap:Body>" \ + "<MessageType xmlns=\"http://messenger.msn.com/ws/2004/09/oim/\">text</MessageType>" \ + "<Content xmlns=\"http://messenger.msn.com/ws/2004/09/oim/\">" \ + "MIME-Version: 1.0\r\n" \ + "Content-Type: text/plain; charset=UTF-8\r\n" \ + "Content-Transfer-Encoding: base64\r\n" \ + "X-OIM-Message-Type: OfflineMessage\r\n" \ + "X-OIM-Run-Id: {F9A6C9DD-0D94-4E85-9CC6-F9D118CC1CAF}\r\n" \ + "X-OIM-Sequence-Num: %d\r\n" \ + "\r\n" \ + "%s" \ + "</Content>" \ +"</soap:Body>" \ +"</soap:Envelope>" + +int msn_soap_oim_send( struct im_connection *ic, const char *to, const char *msg ); +int msn_soap_oim_send_queue( struct im_connection *ic, GSList **msgq ); + + +#define SOAP_ABSERVICE_PAYLOAD \ +"<?xml version=\"1.0\" encoding=\"utf-8\"?>" \ +"<soap:Envelope xmlns:soap=\"http://schemas.xmlsoap.org/soap/envelope/\">" \ + "<soap:Header xmlns:soap=\"http://schemas.xmlsoap.org/soap/envelope/\">" \ + "<ABApplicationHeader xmlns=\"http://www.msn.com/webservices/AddressBook\">" \ + "<ApplicationId xmlns=\"http://www.msn.com/webservices/AddressBook\">CFE80F9D-180F-4399-82AB-413F33A1FA11</ApplicationId>" \ + "<IsMigration xmlns=\"http://www.msn.com/webservices/AddressBook\">false</IsMigration>" \ + "<PartnerScenario xmlns=\"http://www.msn.com/webservices/AddressBook\">%s</PartnerScenario>" \ + "</ABApplicationHeader>" \ + "<ABAuthHeader xmlns=\"http://www.msn.com/webservices/AddressBook\">" \ + "<ManagedGroupRequest xmlns=\"http://www.msn.com/webservices/AddressBook\">false</ManagedGroupRequest>" \ + "<TicketToken>%s</TicketToken>" \ + "</ABAuthHeader>" \ + "</soap:Header>" \ + "<soap:Body xmlns:soap=\"http://schemas.xmlsoap.org/soap/envelope/\">" \ + "%%s" \ + "</soap:Body>" \ +"</soap:Envelope>" + +#define SOAP_MEMLIST_URL "http://contacts.msn.com/abservice/SharingService.asmx" +#define SOAP_MEMLIST_ACTION "http://www.msn.com/webservices/AddressBook/FindMembership" + +#define SOAP_MEMLIST_PAYLOAD \ + "<FindMembership xmlns=\"http://www.msn.com/webservices/AddressBook\"><serviceFilter xmlns=\"http://www.msn.com/webservices/AddressBook\"><Types xmlns=\"http://www.msn.com/webservices/AddressBook\"><ServiceType xmlns=\"http://www.msn.com/webservices/AddressBook\">Messenger</ServiceType><ServiceType xmlns=\"http://www.msn.com/webservices/AddressBook\">Invitation</ServiceType><ServiceType xmlns=\"http://www.msn.com/webservices/AddressBook\">SocialNetwork</ServiceType><ServiceType xmlns=\"http://www.msn.com/webservices/AddressBook\">Space</ServiceType><ServiceType xmlns=\"http://www.msn.com/webservices/AddressBook\">Profile</ServiceType></Types></serviceFilter>" \ + "</FindMembership>" + +#define SOAP_MEMLIST_ADD_ACTION "http://www.msn.com/webservices/AddressBook/AddMember" +#define SOAP_MEMLIST_DEL_ACTION "http://www.msn.com/webservices/AddressBook/DeleteMember" + +#define SOAP_MEMLIST_EDIT_PAYLOAD \ + "<%sMember xmlns=\"http://www.msn.com/webservices/AddressBook\">" \ + "<serviceHandle>" \ + "<Id>0</Id>" \ + "<Type>Messenger</Type>" \ + "<ForeignId></ForeignId>" \ + "</serviceHandle>" \ + "<memberships>" \ + "<Membership>" \ + "<MemberRole>%s</MemberRole>" \ + "<Members>" \ + "<Member xsi:type=\"PassportMember\" xmlns:xsi=\"http://www.w3.org/2001/XMLSchema-instance\">" \ + "<Type>Passport</Type>" \ + "<State>Accepted</State>" \ + "<PassportName>%s</PassportName>" \ + "</Member>" \ + "</Members>" \ + "</Membership>" \ + "</memberships>" \ + "</%sMember>" + +int msn_soap_memlist_request( struct im_connection *ic ); +int msn_soap_memlist_edit( struct im_connection *ic, const char *handle, gboolean add, int list ); + + +#define SOAP_ADDRESSBOOK_URL "http://contacts.msn.com/abservice/abservice.asmx" +#define SOAP_ADDRESSBOOK_ACTION "http://www.msn.com/webservices/AddressBook/ABFindAll" + +#define SOAP_ADDRESSBOOK_PAYLOAD \ + "<ABFindAll xmlns=\"http://www.msn.com/webservices/AddressBook\">" \ + "<abId>00000000-0000-0000-0000-000000000000</abId>" \ + "<abView>Full</abView>" \ + "<deltasOnly>false</deltasOnly>" \ + "<lastChange>0001-01-01T00:00:00.0000000-08:00</lastChange>" \ + "</ABFindAll>" + +#define SOAP_AB_NAMECHANGE_ACTION "http://www.msn.com/webservices/AddressBook/ABContactUpdate" + +#define SOAP_AB_NAMECHANGE_PAYLOAD \ + "<ABContactUpdate xmlns=\"http://www.msn.com/webservices/AddressBook\">" \ + "<abId>00000000-0000-0000-0000-000000000000</abId>" \ + "<contacts>" \ + "<Contact xmlns=\"http://www.msn.com/webservices/AddressBook\">" \ + "<contactInfo>" \ + "<contactType>Me</contactType>" \ + "<displayName>%s</displayName>" \ + "</contactInfo>" \ + "<propertiesChanged>DisplayName</propertiesChanged>" \ + "</Contact>" \ + "</contacts>" \ + "</ABContactUpdate>" + +#define SOAP_AB_CONTACT_ADD_ACTION "http://www.msn.com/webservices/AddressBook/ABContactAdd" + +#define SOAP_AB_CONTACT_ADD_PAYLOAD \ + "<ABContactAdd xmlns=\"http://www.msn.com/webservices/AddressBook\">" \ + "<abId>00000000-0000-0000-0000-000000000000</abId>" \ + "<contacts>" \ + "<Contact xmlns=\"http://www.msn.com/webservices/AddressBook\">" \ + "<contactInfo>" \ + "<contactType>LivePending</contactType>" \ + "<passportName>%s</passportName>" \ + "<isMessengerUser>true</isMessengerUser>" \ + "<MessengerMemberInfo>" \ + "<DisplayName>%s</DisplayName>" \ + "</MessengerMemberInfo>" \ + "</contactInfo>" \ + "</Contact>" \ + "</contacts>" \ + "<options>" \ + "<EnableAllowListManagement>true</EnableAllowListManagement>" \ + "</options>" \ + "</ABContactAdd>" + +#define SOAP_AB_CONTACT_DEL_ACTION "http://www.msn.com/webservices/AddressBook/ABContactDelete" + +#define SOAP_AB_CONTACT_DEL_PAYLOAD \ + "<ABContactDelete xmlns=\"http://www.msn.com/webservices/AddressBook\">" \ + "<abId>00000000-0000-0000-0000-000000000000</abId>" \ + "<contacts>" \ + "<Contact xmlns=\"http://www.msn.com/webservices/AddressBook\">" \ + "<contactId>%s</contactId>" \ + "</Contact>" \ + "</contacts>" \ + "</ABContactDelete>" + +int msn_soap_addressbook_request( struct im_connection *ic ); +int msn_soap_addressbook_set_display_name( struct im_connection *ic, const char *new ); +int msn_soap_ab_contact_add( struct im_connection *ic, bee_user_t *bu ); +int msn_soap_ab_contact_del( struct im_connection *ic, bee_user_t *bu ); + + +#define SOAP_STORAGE_URL "https://storage.msn.com/storageservice/SchematizedStore.asmx" +#define SOAP_PROFILE_GET_ACTION "http://www.msn.com/webservices/storage/w10/GetProfile" + +#define SOAP_PROFILE_GET_PAYLOAD \ +"<?xml version=\"1.0\" encoding=\"utf-8\"?>" \ +"<soap:Envelope xmlns:soap=\"http://schemas.xmlsoap.org/soap/envelope/\">" \ + "<soap:Header xmlns:soap=\"http://schemas.xmlsoap.org/soap/envelope/\">" \ + "<StorageApplicationHeader xmlns=\"http://www.msn.com/webservices/storage/w10\">" \ + "<ApplicationID>Messenger Client 9.0</ApplicationID>" \ + "<Scenario>Initial</Scenario>" \ + "</StorageApplicationHeader>" \ + "<StorageUserHeader xmlns=\"http://www.msn.com/webservices/storage/w10\">" \ + "<Puid>0</Puid>" \ + "<TicketToken>%s</TicketToken>" \ + "</StorageUserHeader>" \ + "</soap:Header>" \ + "<soap:Body xmlns:soap=\"http://schemas.xmlsoap.org/soap/envelope/\">" \ + "<GetProfile xmlns=\"http://www.msn.com/webservices/storage/w10\">" \ + "<profileHandle>" \ + "<Alias>" \ + "<Name>%s</Name>" \ + "<NameSpace>MyCidStuff</NameSpace>" \ + "</Alias>" \ + "<RelationshipName>MyProfile</RelationshipName>" \ + "</profileHandle>" \ + "<profileAttributes>" \ + "<ResourceID>true</ResourceID>" \ + "<DateModified>true</DateModified>" \ + "<ExpressionProfileAttributes>" \ + "<ResourceID>true</ResourceID>" \ + "<DateModified>true</DateModified>" \ + "<DisplayName>true</DisplayName>" \ + "<DisplayNameLastModified>true</DisplayNameLastModified>" \ + "<PersonalStatus>true</PersonalStatus>" \ + "<PersonalStatusLastModified>true</PersonalStatusLastModified>" \ + "<StaticUserTilePublicURL>true</StaticUserTilePublicURL>" \ + "<Photo>true</Photo>" \ + "<Flags>true</Flags>" \ + "</ExpressionProfileAttributes>" \ + "</profileAttributes>" \ + "</GetProfile>" \ + "</soap:Body>" \ +"</soap:Envelope>" + +int msn_soap_profile_get( struct im_connection *ic, const char *cid ); + + +#endif /* __SOAP_H__ */ diff --git a/protocols/msn/tables.c b/protocols/msn/tables.c index 42b12aa9..273d291e 100644 --- a/protocols/msn/tables.c +++ b/protocols/msn/tables.c @@ -1,7 +1,7 @@ /********************************************************************\ * BitlBee -- An IRC to other IM-networks gateway * * * - * Copyright 2002-2004 Wilmer van der Gaast and others * + * Copyright 2002-2010 Wilmer van der Gaast and others * \********************************************************************/ /* MSN module - Some tables with useful data */ @@ -82,6 +82,8 @@ const struct msn_status_code msn_status_code_list[] = { 229, "Group name too long", 0 }, { 230, "Cannot remove that group", 0 }, { 231, "Invalid group", 0 }, + { 240, "ADL/RML command with corrupted payload", STATUS_FATAL }, + { 241, "ADL/RML command with invalid modification", 0 }, { 280, "Switchboard failed", STATUS_SB_FATAL }, { 281, "Transfer to switchboard failed", 0 }, diff --git a/protocols/nogaim.c b/protocols/nogaim.c index 7943e026..aa3ad5bb 100644 --- a/protocols/nogaim.c +++ b/protocols/nogaim.c @@ -251,7 +251,7 @@ void imcb_error( struct im_connection *ic, char *format, ... ) if( ic->flags & OPT_LOGGED_IN ) serv_got_crap( ic, "Error: %s", text ); else - serv_got_crap( ic, "Couldn't log in: %s", text ); + serv_got_crap( ic, "Login error: %s", text ); g_free( text ); } @@ -325,14 +325,6 @@ void imc_logout( struct im_connection *ic, int allow_reconnect ) imcb_log( ic, "Signing off.." ); - b_event_remove( ic->keepalive ); - ic->keepalive = 0; - ic->acc->prpl->logout( ic ); - b_event_remove( ic->inpa ); - - g_free( ic->away ); - ic->away = NULL; - for( l = bee->users; l; ) { bee_user_t *bu = l->data; @@ -344,6 +336,14 @@ void imc_logout( struct im_connection *ic, int allow_reconnect ) l = next; } + b_event_remove( ic->keepalive ); + ic->keepalive = 0; + ic->acc->prpl->logout( ic ); + b_event_remove( ic->inpa ); + + g_free( ic->away ); + ic->away = NULL; + query_del_by_conn( (irc_t*) ic->bee->ui_data, ic ); for( a = bee->accounts; a; a = a->next ) diff --git a/protocols/nogaim.h b/protocols/nogaim.h index 693b5d16..2069cf6b 100644 --- a/protocols/nogaim.h +++ b/protocols/nogaim.h @@ -194,6 +194,8 @@ struct prpl { /* Request profile info. Free-formatted stuff, the IM module gives back this info via imcb_log(). Implementing these are optional. */ void (* get_info) (struct im_connection *, char *who); + /* set_my_name is *DEPRECATED*, not used by the UI anymore. Use the + display_name setting instead. */ void (* set_my_name) (struct im_connection *, char *name); void (* set_name) (struct im_connection *, char *who, char *name); diff --git a/storage_xml.c b/storage_xml.c index 1d6757ae..5035e214 100644 --- a/storage_xml.c +++ b/storage_xml.c @@ -319,6 +319,16 @@ static void xml_text( GMarkupParseContext *ctx, const gchar *text_orig, gsize te } else if( g_strcasecmp( g_markup_parse_context_get_element( ctx ), "setting" ) == 0 && xd->current_setting ) { + if( xd->current_account ) + { + set_t *s = set_find( xd->current_set_head, xd->current_setting ); + if( s && ( s->flags & ACC_SET_ONLINE_ONLY ) ) + { + g_free( xd->current_setting ); + xd->current_setting = NULL; + return; + } + } set_setstr( xd->current_set_head, xd->current_setting, (char*) text ); g_free( xd->current_setting ); xd->current_setting = NULL; |