Changes in / [3dc9d46:e617b35]
- Files:
-
- 8 added
- 28 deleted
- 11 edited
Legend:
- Unmodified
- Added
- Removed
-
configure
r3dc9d46 re617b35 318 318 319 319 echo 'SSL_CLIENT=ssl_'$ssl'.o' >> Makefile.settings 320 321 for i in /lib /usr/lib /usr/local/lib; do 322 if [ -e $i/libresolv.a ]; then 323 echo '#define HAVE_RESOLV_A' >> config.h 324 echo 'EFLAGS+='$i'/libresolv.a' >> Makefile.settings 325 break 326 fi 327 done 320 328 321 329 STORAGES="text xml" -
doc/user-guide/commands.xml
r3dc9d46 re617b35 30 30 <description> 31 31 <para> 32 Note that the servertag argument is optional. You only have to use it if the part after the @ in your handle isn't the hostname of your Jabber server, or if you want to use SSL/connect to a non-standard port number. The format is simple: [<servername>[:<portnumber>][:ssl]]. 32 The handle should be a full handle, including the domain name. You can specify a servername if necessary. Normally BitlBee doesn't need this though, since it's able to find out the server by doing DNS SRV lookups. 33 </para> 34 35 <para> 36 In previous versions it was also possible to specify port numbers and/or SSL in the server tag. This is deprecated and should now be done using the <emphasis>account set</emphasis> command. This also applies to specifying a resource in the handle (like <emphasis>wilmer@bitlbee.org/work</emphasis>). 33 37 </para> 34 38 </description> 35 36 <description>37 <para>38 Google Talk uses the Jabber protocol. Please note that Google talk is SSL-only, but officially reachable over both port 5222 and 5223. Usually BitlBee users have to connect via port 5223, for example like this:39 </para>40 </description>41 42 <ircexample>43 <ircline nick="wilmer">account add jabber example@gmail.com hobbelmeeuw talk.google.com:5223:ssl</ircline>44 <ircline nick="root">Account successfully added</ircline>45 </ircexample>46 39 </bitlbee-command> 47 40 … … 517 510 </bitlbee-setting> 518 511 512 <bitlbee-setting name="priority" type="integer" scope="account"> 513 <default>0</default> 514 515 <description> 516 <para> 517 Can be set for Jabber connections. When connecting to one account from multiple places, this priority value will help the server to determine where to deliver incoming messages (that aren't addressed to a specific resource already). 518 </para> 519 520 <para> 521 According to RFC 3921 servers will always deliver messages to the server with the highest priority value. Mmessages will not be delivered to resources with a negative priority setting (and should be saved as an off-line message if all available resources have a negative priority value). 522 </para> 523 </description> 524 </bitlbee-setting> 525 519 526 <bitlbee-setting name="private" type="boolean" scope="global"> 520 527 <default>true</default> … … 556 563 </bitlbee-setting> 557 564 565 <bitlbee-setting name="resource_select" type="string" scope="account"> 566 <default>priority</default> 567 <possible-values>priority, time</possible-values> 568 569 <description> 570 <para> 571 Because the IRC interface makes it pretty hard to specify the resource to talk to (when a buddy is online through different resources), this setting was added. 572 </para> 573 574 <para> 575 Normally it's set to <emphasis>priority</emphasis> which means messages will always be delivered to the buddy's resource with the highest priority. If the setting is set to <emphasis>time</emphasis>, messages will be delivered to the resource that was last used to send you a message (or the resource that most recently connected). 576 </para> 577 </description> 578 </bitlbee-setting> 579 558 580 <bitlbee-setting name="save_on_quit" type="boolean" scope="global"> 559 581 <default>true</default> … … 593 615 <para> 594 616 If BitlBee fails to detect this sometimes (most likely in AIM messages over an ICQ connection), you can set this setting to <emphasis>always</emphasis>, but this might sometimes accidentally strip non-HTML things too. 617 </para> 618 </description> 619 </bitlbee-setting> 620 621 <bitlbee-setting name="tls" type="boolean" scope="account"> 622 <default>try</default> 623 624 <description> 625 <para> 626 Newer Jabber servers allow clients to convert a plain-text session to a TLS/SSL-encrypted session. Normally (with this setting set to <emphasis>try</emphasis>) BitlBee will do this, if possible. 627 </para> 628 629 <para> 630 If you want to force BitlBee to use TLS sessions only (and to give up if that doesn't seem to be possible) you can set this setting to <emphasis>true</emphasis>. Set it to <emphasis>false</emphasis> if you want the session to remain plain-text. 595 631 </para> 596 632 </description> -
lib/misc.c
r3dc9d46 re617b35 39 39 #include <glib.h> 40 40 #include <time.h> 41 42 #ifdef HAVE_RESOLV_A 43 #include <arpa/nameser.h> 44 #include <resolv.h> 45 #endif 41 46 42 47 void strip_linefeed(gchar *text) … … 488 493 return 0; 489 494 } 495 496 struct ns_srv_reply *srv_lookup( char *service, char *protocol, char *domain ) 497 { 498 struct ns_srv_reply *reply = NULL; 499 #ifdef HAVE_RESOLV_A 500 char name[1024]; 501 unsigned char querybuf[1024]; 502 const unsigned char *buf; 503 ns_msg nsh; 504 ns_rr rr; 505 int i, len, size; 506 507 g_snprintf( name, sizeof( name ), "_%s._%s.%s", service, protocol, domain ); 508 509 if( ( size = res_query( name, ns_c_in, ns_t_srv, querybuf, sizeof( querybuf ) ) ) <= 0 ) 510 return NULL; 511 512 if( ns_initparse( querybuf, size, &nsh ) != 0 ) 513 return NULL; 514 515 if( ns_parserr( &nsh, ns_s_an, 0, &rr ) != 0 ) 516 return NULL; 517 518 size = ns_rr_rdlen( rr ); 519 buf = ns_rr_rdata( rr ); 520 521 len = 0; 522 for( i = 6; i < size && buf[i]; i += buf[i] + 1 ) 523 len += buf[i] + 1; 524 525 if( i > size ) 526 return NULL; 527 528 reply = g_malloc( sizeof( struct ns_srv_reply ) + len ); 529 memcpy( reply->name, buf + 7, len ); 530 531 for( i = buf[6]; i < len && buf[7+i]; i += buf[7+i] + 1 ) 532 reply->name[i] = '.'; 533 534 if( i > len ) 535 { 536 g_free( reply ); 537 return NULL; 538 } 539 540 reply->prio = ( buf[0] << 8 ) | buf[1]; 541 reply->weight = ( buf[2] << 8 ) | buf[3]; 542 reply->port = ( buf[4] << 8 ) | buf[5]; 543 #endif 544 545 return reply; 546 } -
lib/misc.h
r3dc9d46 re617b35 30 30 #include <time.h> 31 31 32 struct ns_srv_reply 33 { 34 int prio; 35 int weight; 36 int port; 37 char name[]; 38 }; 39 32 40 G_MODULE_EXPORT void strip_linefeed( gchar *text ); 33 41 G_MODULE_EXPORT char *add_cr( char *text ); … … 54 62 G_MODULE_EXPORT int bool2int( char *value ); 55 63 64 G_MODULE_EXPORT struct ns_srv_reply *srv_lookup( char *service, char *protocol, char *domain ); 65 56 66 #endif -
lib/ssl_client.h
r3dc9d46 re617b35 52 52 G_MODULE_EXPORT void *ssl_connect( char *host, int port, ssl_input_function func, gpointer data ); 53 53 54 /* Start an SSL session on an existing fd. Useful for STARTTLS functionality, 55 for example in Jabber. */ 56 G_MODULE_EXPORT void *ssl_starttls( int fd, ssl_input_function func, gpointer data ); 57 54 58 /* Obviously you need special read/write functions to read data. */ 55 59 G_MODULE_EXPORT int ssl_read( void *conn, char *buf, int len ); -
lib/ssl_gnutls.c
r3dc9d46 re617b35 49 49 50 50 static gboolean ssl_connected( gpointer data, gint source, b_input_condition cond ); 51 static gboolean ssl_starttls_real( gpointer data, gint source, b_input_condition cond ); 52 static gboolean ssl_handshake( gpointer data, gint source, b_input_condition cond ); 51 53 52 54 … … 63 65 { 64 66 g_free( conn ); 65 return( NULL ); 67 return NULL; 68 } 69 70 return conn; 71 } 72 73 void *ssl_starttls( int fd, ssl_input_function func, gpointer data ) 74 { 75 struct scd *conn = g_new0( struct scd, 1 ); 76 77 conn->fd = fd; 78 conn->func = func; 79 conn->data = data; 80 conn->inpa = -1; 81 82 /* This function should be called via a (short) timeout instead of 83 directly from here, because these SSL calls are *supposed* to be 84 *completely* asynchronous and not ready yet when this function 85 (or *_connect, for examle) returns. Also, errors are reported via 86 the callback function, not via this function's return value. 87 88 In short, doing things like this makes the rest of the code a lot 89 simpler. */ 90 91 b_timeout_add( 1, ssl_starttls_real, conn ); 92 93 return conn; 94 } 95 96 static gboolean ssl_starttls_real( gpointer data, gint source, b_input_condition cond ) 97 { 98 struct scd *conn = data; 99 100 return ssl_connected( conn, conn->fd, GAIM_INPUT_WRITE ); 101 } 102 103 static gboolean ssl_connected( gpointer data, gint source, b_input_condition cond ) 104 { 105 struct scd *conn = data; 106 107 if( source == -1 ) 108 { 109 conn->func( conn->data, NULL, cond ); 110 111 g_free( conn ); 112 113 return FALSE; 66 114 } 67 115 … … 77 125 gnutls_set_default_priority( conn->session ); 78 126 gnutls_credentials_set( conn->session, GNUTLS_CRD_CERTIFICATE, conn->xcred ); 79 80 return( conn );81 }82 83 static gboolean ssl_handshake( gpointer data, gint source, b_input_condition cond );84 85 static gboolean ssl_connected( gpointer data, gint source, b_input_condition cond )86 {87 struct scd *conn = data;88 89 if( source == -1 )90 {91 conn->func( conn->data, NULL, cond );92 93 gnutls_deinit( conn->session );94 gnutls_certificate_free_credentials( conn->xcred );95 96 g_free( conn );97 98 return FALSE;99 }100 127 101 128 sock_make_nonblocking( conn->fd ); -
protocols/jabber/Makefile
r3dc9d46 re617b35 10 10 11 11 # [SH] Program variables 12 objects = expat.o hashtable.o jid.o jpacket.o jutil.o log.o pool.o str.o xmlnode.o xmlparse.o xmlrole.o xmltok.o jabber.o12 objects = io.o iq.o jabber.o jabber_util.o message.o presence.o sasl.o xmltree.o 13 13 14 14 CFLAGS += -Wall -
protocols/jabber/jabber.c
r3dc9d46 re617b35 1 /* -*- Mode: C; tab-width: 8; indent-tabs-mode: t; c-basic-offset: 8 -*- */ 2 /* 3 * gaim 4 * 5 * Some code copyright (C) 1998-1999, Mark Spencer <markster@marko.net> 6 * libfaim code copyright 1998, 1999 Adam Fritzler <afritz@auk.cx> 7 * 8 * This program is free software; you can redistribute it and/or modify 9 * it under the terms of the GNU General Public License as published by 10 * the Free Software Foundation; either version 2 of the License, or 11 * (at your option) any later version. 12 * 13 * This program is distributed in the hope that it will be useful, 14 * but WITHOUT ANY WARRANTY; without even the implied warranty of 15 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the 16 * GNU General Public License for more details. 17 * 18 * You should have received a copy of the GNU General Public License 19 * along with this program; if not, write to the Free Software 20 * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA 21 * 22 */ 23 24 #ifndef _WIN32 25 #include <sys/utsname.h> 26 #endif 27 #include <errno.h> 1 /***************************************************************************\ 2 * * 3 * BitlBee - An IRC to IM gateway * 4 * Jabber module - Main file * 5 * * 6 * Copyright 2006 Wilmer van der Gaast <wilmer@gaast.net> * 7 * * 8 * This program is free software; you can redistribute it and/or modify * 9 * it under the terms of the GNU General Public License as published by * 10 * the Free Software Foundation; either version 2 of the License, or * 11 * (at your option) any later version. * 12 * * 13 * This program is distributed in the hope that it will be useful, * 14 * but WITHOUT ANY WARRANTY; without even the implied warranty of * 15 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * 16 * GNU General Public License for more details. * 17 * * 18 * You should have received a copy of the GNU General Public License along * 19 * with this program; if not, write to the Free Software Foundation, Inc., * 20 * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA. * 21 * * 22 \***************************************************************************/ 23 24 #include <glib.h> 28 25 #include <string.h> 29 #include <stdlib.h> 26 #include <unistd.h> 27 #include <ctype.h> 30 28 #include <stdio.h> 31 #include <time.h> 32 #include <sys/stat.h> 29 30 #include "ssl_client.h" 31 #include "xmltree.h" 32 #include "bitlbee.h" 33 33 #include "jabber.h" 34 #include "nogaim.h" 35 #include "bitlbee.h" 36 #include "proxy.h" 37 #include "ssl_client.h" 38 39 /* The priv member of gjconn's is a gaim_connection for now. */ 40 #define GJ_GC(x) ((struct gaim_connection *)(x)->priv) 41 42 #define IQID_AUTH "__AUTH__" 43 44 #define IQ_NONE -1 45 #define IQ_AUTH 0 46 #define IQ_ROSTER 1 47 48 #define UC_AWAY (0x02 | UC_UNAVAILABLE) 49 #define UC_CHAT 0x04 50 #define UC_XA (0x08 | UC_UNAVAILABLE) 51 #define UC_DND (0x10 | UC_UNAVAILABLE) 52 53 #define DEFAULT_SERVER "jabber.org" 54 #define DEFAULT_GROUPCHAT "conference.jabber.org" 55 #define DEFAULT_PORT 5222 56 #define DEFAULT_PORT_SSL 5223 57 #define JABBER_PORT_MIN 5220 58 #define JABBER_PORT_MAX 5229 59 60 #define JABBER_GROUP "Friends" 61 62 /* i18n disabled - Bitlbee */ 63 #define N_(String) String 64 65 /* 66 * Note: "was_connected" may seem redundant, but it was needed and I 67 * didn't want to touch the Jabber state stuff not specific to Gaim. 68 */ 69 typedef struct gjconn_struct { 70 /* Core structure */ 71 pool p; /* Memory allocation pool */ 72 int state; /* Connection state flag */ 73 int was_connected; /* We were once connected */ 74 int fd; /* Connection file descriptor */ 75 void *ssl; /* SSL connection */ 76 jid user; /* User info */ 77 char *pass; /* User passwd */ 78 79 /* Stream stuff */ 80 int id; /* id counter for jab_getid() function */ 81 char idbuf[9]; /* temporary storage for jab_getid() */ 82 char *sid; /* stream id from server, for digest auth */ 83 XML_Parser parser; /* Parser instance */ 84 xmlnode current; /* Current node in parsing instance.. */ 85 86 /* Event callback ptrs */ 87 void (*on_state)(struct gjconn_struct *gjc, int state); 88 void (*on_packet)(struct gjconn_struct *gjc, jpacket p); 89 90 GHashTable *queries; /* query tracker */ 91 92 void *priv; 93 } *gjconn, gjconn_struct; 94 95 typedef void (*gjconn_state_h)(gjconn gjc, int state); 96 typedef void (*gjconn_packet_h)(gjconn gjc, jpacket p); 97 98 static gjconn gjab_new(char *user, char *pass, void *priv); 99 static void gjab_delete(gjconn gjc); 100 static void gjab_state_handler(gjconn gjc, gjconn_state_h h); 101 static void gjab_packet_handler(gjconn gjc, gjconn_packet_h h); 102 static void gjab_start(gjconn gjc); 103 static void gjab_stop(gjconn gjc); 104 /* 105 static int gjab_getfd(gjconn gjc); 106 static jid gjab_getjid(gjconn gjc); 107 static char *gjab_getsid(gjconn gjc); 108 */ 109 static char *gjab_getid(gjconn gjc); 110 static void gjab_send(gjconn gjc, xmlnode x); 111 static void gjab_send_raw(gjconn gjc, const char *str); 112 static void gjab_recv(gjconn gjc); 113 static void gjab_auth(gjconn gjc); 114 115 /* 116 * It is *this* to which we point the gaim_connection proto_data 117 */ 118 struct jabber_data { 119 gjconn gjc; 120 gboolean did_import; 121 GSList *chats; 122 GHashTable *hash; 123 time_t idle; 124 gboolean die; 125 }; 126 127 /* 128 * Jabber "chat group" info. Pointers to these go in jabber_data 129 * pending and existing chats lists. 130 */ 131 struct jabber_chat { 132 jid Jid; 133 struct gaim_connection *gc; 134 struct conversation *b; 135 int id; 136 int state; 137 }; 138 139 /* 140 * Jabber chat states... 141 * 142 * Note: due to a bug in one version of the Jabber server, subscriptions 143 * to chat groups aren't (always?) properly removed at the server. The 144 * result is clients receive Jabber "presence" notifications for JIDs 145 * they no longer care about. The problem with such vestigial notifies is 146 * that we really have no way of telling if it's vestigial or if it's a 147 * valid "buddy" presence notification. So we keep jabber_chat structs 148 * around after leaving a chat group and simply mark them "closed." That 149 * way we can test for such errant presence notifications. I.e.: if we 150 * get a presence notfication from a JID that matches a chat group JID, 151 * we disregard it. 152 */ 153 #define JCS_PENDING 1 /* pending */ 154 #define JCS_ACTIVE 2 /* active */ 155 #define JCS_CLOSED 3 /* closed */ 156 157 158 #define STATE_EVT(arg) if(gjc->on_state) { (gjc->on_state)(gjc, (arg) ); } 159 160 static void jabber_remove_buddy(struct gaim_connection *gc, char *name, char *group); 161 static void jabber_handlevcard(gjconn gjc, xmlnode querynode, char *from); 162 163 static char *create_valid_jid(const char *given, char *server, char *resource) 164 { 165 char *valid; 166 167 if (!strchr(given, '@')) 168 valid = g_strdup_printf("%s@%s/%s", given, server, resource); 169 else if (!strchr(strchr(given, '@'), '/')) 170 valid = g_strdup_printf("%s/%s", given, resource); 171 else 172 valid = g_strdup(given); 173 174 return valid; 175 } 176 177 static gjconn gjab_new(char *user, char *pass, void *priv) 178 { 179 pool p; 180 gjconn gjc; 181 182 if (!user) 183 return (NULL); 184 185 p = pool_new(); 186 if (!p) 187 return (NULL); 188 gjc = pmalloc_x(p, sizeof(gjconn_struct), 0); 189 if (!gjc) { 190 pool_free(p); /* no need for this anymore! */ 191 return (NULL); 192 } 193 gjc->p = p; 194 195 if((gjc->user = jid_new(p, user)) == NULL) { 196 pool_free(p); /* no need for this anymore! */ 197 return (NULL); 198 } 199 gjc->pass = pstrdup(p, pass); 200 201 gjc->state = JCONN_STATE_OFF; 202 gjc->was_connected = 0; 203 gjc->id = 1; 204 gjc->fd = -1; 205 206 gjc->priv = priv; 207 208 return gjc; 209 } 210 211 static void gjab_delete(gjconn gjc) 212 { 213 if (!gjc) 214 return; 215 216 gjab_stop(gjc); 217 pool_free(gjc->p); 218 } 219 220 static void gjab_state_handler(gjconn gjc, gjconn_state_h h) 221 { 222 if (!gjc) 223 return; 224 225 gjc->on_state = h; 226 } 227 228 static void gjab_packet_handler(gjconn gjc, gjconn_packet_h h) 229 { 230 if (!gjc) 231 return; 232 233 gjc->on_packet = h; 234 } 235 236 static void gjab_stop(gjconn gjc) 237 { 238 if (!gjc || gjc->state == JCONN_STATE_OFF) 239 return; 240 241 gjab_send_raw(gjc, "</stream:stream>"); 242 gjc->state = JCONN_STATE_OFF; 243 gjc->was_connected = 0; 244 if (gjc->ssl) { 245 ssl_disconnect(gjc->ssl); 246 gjc->ssl = NULL; 247 } else { 248 closesocket(gjc->fd); 249 } 250 gjc->fd = -1; 251 XML_ParserFree(gjc->parser); 252 gjc->parser = NULL; 253 } 254 255 /* 256 static int gjab_getfd(gjconn gjc) 257 { 258 if (gjc) 259 return gjc->fd; 260 else 261 return -1; 262 } 263 264 static jid gjab_getjid(gjconn gjc) 265 { 266 if (gjc) 267 return (gjc->user); 268 else 269 return NULL; 270 } 271 272 static char *gjab_getsid(gjconn gjc) 273 { 274 if (gjc) 275 return (gjc->sid); 276 else 277 return NULL; 278 } 279 */ 280 281 static char *gjab_getid(gjconn gjc) 282 { 283 g_snprintf(gjc->idbuf, 8, "%d", gjc->id++); 284 return &gjc->idbuf[0]; 285 } 286 287 static void gjab_send(gjconn gjc, xmlnode x) 288 { 289 if (gjc && gjc->state != JCONN_STATE_OFF) { 290 char *buf = xmlnode2str(x); 291 if (!buf) 292 return; 293 else if (gjc->ssl) 294 ssl_write(gjc->ssl, buf, strlen(buf)); 295 else 296 write(gjc->fd, buf, strlen(buf)); 297 } 298 } 299 300 static void gjab_send_raw(gjconn gjc, const char *str) 301 { 302 if (gjc && gjc->state != JCONN_STATE_OFF) { 303 int len; 304 305 /* 306 * JFIXME: No error detection?!?! 307 */ 308 if (gjc->ssl) 309 len = ssl_write(gjc->ssl, str, strlen(str)); 310 else 311 len = write(gjc->fd, str, strlen(str)); 312 313 if(len < 0) { 314 /* Do NOT write to stdout/stderr directly, IRC clients 315 might get confused, and we don't want that... 316 fprintf(stderr, "DBG: Problem sending. Error: %d\n", errno); 317 fflush(stderr); */ 318 } 319 } 320 } 321 322 static void gjab_reqroster(gjconn gjc) 323 { 324 xmlnode x; 325 326 x = jutil_iqnew(JPACKET__GET, NS_ROSTER); 327 xmlnode_put_attrib(x, "id", gjab_getid(gjc)); 328 329 gjab_send(gjc, x); 330 xmlnode_free(x); 331 } 332 333 static void gjab_reqauth(gjconn gjc) 334 { 335 xmlnode x, y, z; 336 char *user; 337 338 if (!gjc) 339 return; 340 341 x = jutil_iqnew(JPACKET__GET, NS_AUTH); 342 xmlnode_put_attrib(x, "id", IQID_AUTH); 343 y = xmlnode_get_tag(x, "query"); 344 345 user = gjc->user->user; 346 347 if (user) { 348 z = xmlnode_insert_tag(y, "username"); 349 xmlnode_insert_cdata(z, user, -1); 350 } 351 352 gjab_send(gjc, x); 353 xmlnode_free(x); 354 } 355 356 static void gjab_auth(gjconn gjc) 357 { 358 xmlnode x, y, z; 359 char *hash, *user; 360 361 if (!gjc) 362 return; 363 364 x = jutil_iqnew(JPACKET__SET, NS_AUTH); 365 xmlnode_put_attrib(x, "id", IQID_AUTH); 366 y = xmlnode_get_tag(x, "query"); 367 368 user = gjc->user->user; 369 370 if (user) { 371 z = xmlnode_insert_tag(y, "username"); 372 xmlnode_insert_cdata(z, user, -1); 373 } 374 375 z = xmlnode_insert_tag(y, "resource"); 376 xmlnode_insert_cdata(z, gjc->user->resource, -1); 377 378 if (gjc->sid) { 379 z = xmlnode_insert_tag(y, "digest"); 380 hash = pmalloc(x->p, strlen(gjc->sid) + strlen(gjc->pass) + 1); 381 strcpy(hash, gjc->sid); 382 strcat(hash, gjc->pass); 383 hash = shahash(hash); 384 xmlnode_insert_cdata(z, hash, 40); 385 } else { 386 z = xmlnode_insert_tag(y, "password"); 387 xmlnode_insert_cdata(z, gjc->pass, -1); 388 } 389 390 gjab_send(gjc, x); 391 xmlnode_free(x); 392 393 return; 394 } 395 396 static void gjab_recv(gjconn gjc) 397 { 398 static char buf[4096]; 399 int len; 400 401 if (!gjc || gjc->state == JCONN_STATE_OFF) 402 return; 403 404 if (gjc->ssl) 405 len = ssl_read(gjc->ssl, buf, sizeof(buf) - 1); 406 else 407 len = read(gjc->fd, buf, sizeof(buf) - 1); 408 409 if (len > 0) { 410 struct jabber_data *jd = GJ_GC(gjc)->proto_data; 411 buf[len] = '\0'; 412 XML_Parse(gjc->parser, buf, len, 0); 413 if (jd->die) 414 signoff(GJ_GC(gjc)); 415 } else if (len == 0 || (len < 0 && (!sockerr_again() || gjc->ssl))) { 416 STATE_EVT(JCONN_STATE_OFF) 417 } 418 } 419 420 static void startElement(void *userdata, const char *name, const char **attribs) 421 { 422 xmlnode x; 423 gjconn gjc = (gjconn) userdata; 424 425 if (gjc->current) { 426 /* Append the node to the current one */ 427 x = xmlnode_insert_tag(gjc->current, name); 428 xmlnode_put_expat_attribs(x, attribs); 429 430 gjc->current = x; 431 } else { 432 x = xmlnode_new_tag(name); 433 xmlnode_put_expat_attribs(x, attribs); 434 if (strcmp(name, "stream:stream") == 0) { 435 /* special case: name == stream:stream */ 436 /* id attrib of stream is stored for digest auth */ 437 gjc->sid = g_strdup(xmlnode_get_attrib(x, "id")); 438 /* STATE_EVT(JCONN_STATE_AUTH) */ 439 xmlnode_free(x); 440 } else { 441 gjc->current = x; 442 } 443 } 444 } 445 446 static void endElement(void *userdata, const char *name) 447 { 448 gjconn gjc = (gjconn) userdata; 449 xmlnode x; 450 jpacket p; 451 452 if (gjc->current == NULL) { 453 /* we got </stream:stream> */ 454 STATE_EVT(JCONN_STATE_OFF) 455 return; 456 } 457 458 x = xmlnode_get_parent(gjc->current); 459 460 if (!x) { 461 /* it is time to fire the event */ 462 p = jpacket_new(gjc->current); 463 464 if (gjc->on_packet) 465 (gjc->on_packet) (gjc, p); 466 else 467 xmlnode_free(gjc->current); 468 } 469 470 gjc->current = x; 471 } 472 473 static gboolean jabber_callback(gpointer data, gint source, b_input_condition condition) 474 { 475 struct gaim_connection *gc = (struct gaim_connection *)data; 476 struct jabber_data *jd = (struct jabber_data *)gc->proto_data; 477 478 gjab_recv(jd->gjc); 479 480 return TRUE; 481 } 482 483 static void charData(void *userdata, const char *s, int slen) 484 { 485 gjconn gjc = (gjconn) userdata; 486 487 if (gjc->current) 488 xmlnode_insert_cdata(gjc->current, s, slen); 489 } 490 491 static gboolean gjab_connected(gpointer data, gint source, b_input_condition cond) 492 { 493 xmlnode x; 494 char *t, *t2; 495 struct gaim_connection *gc = data; 496 struct jabber_data *jd; 497 gjconn gjc; 498 499 if (!g_slist_find(get_connections(), gc)) { 500 closesocket(source); 501 return FALSE; 502 } 503 504 jd = gc->proto_data; 505 gjc = jd->gjc; 506 507 if (gjc->fd != source) 508 gjc->fd = source; 509 510 if (source == -1) { 511 STATE_EVT(JCONN_STATE_OFF) 512 return FALSE; 513 } 514 515 gjc->state = JCONN_STATE_CONNECTED; 516 STATE_EVT(JCONN_STATE_CONNECTED) 517 518 /* start stream */ 519 x = jutil_header(NS_CLIENT, gjc->user->server); 520 t = xmlnode2str(x); 521 /* this is ugly, we can create the string here instead of jutil_header */ 522 /* what do you think about it? -madcat */ 523 t2 = strstr(t, "/>"); 524 *t2++ = '>'; 525 *t2 = '\0'; 526 gjab_send_raw(gjc, "<?xml version='1.0'?>"); 527 gjab_send_raw(gjc, t); 528 xmlnode_free(x); 529 530 gjc->state = JCONN_STATE_ON; 531 STATE_EVT(JCONN_STATE_ON); 532 533 gc = GJ_GC(gjc); 534 gc->inpa = b_input_add(gjc->fd, GAIM_INPUT_READ, jabber_callback, gc); 535 536 return FALSE; 537 } 538 539 static gboolean gjab_connected_ssl(gpointer data, void *source, b_input_condition cond) 540 { 541 struct gaim_connection *gc = data; 542 struct jabber_data *jd; 543 gjconn gjc; 544 545 jd = gc->proto_data; 546 gjc = jd->gjc; 547 548 if (source == NULL) { 549 STATE_EVT(JCONN_STATE_OFF) 550 return FALSE; 551 } 552 553 if (!g_slist_find(get_connections(), gc)) { 554 ssl_disconnect(source); 555 return FALSE; 556 } 557 558 return gjab_connected(data, gjc->fd, cond); 559 } 560 561 static void gjab_start(gjconn gjc) 562 { 563 account_t *acc; 564 int port = -1, ssl = 0; 565 char *server = NULL; 566 567 if (!gjc || gjc->state != JCONN_STATE_OFF) 568 return; 569 570 acc = GJ_GC(gjc)->acc; 571 server = acc->server; 572 port = set_getint(&acc->set, "port"); 573 ssl = set_getbool(&acc->set, "ssl"); 574 575 if (port < JABBER_PORT_MIN || port > JABBER_PORT_MAX) { 576 serv_got_crap(GJ_GC(gjc), "For security reasons, the Jabber port number must be in the %d-%d range.", JABBER_PORT_MIN, JABBER_PORT_MAX); 577 STATE_EVT(JCONN_STATE_OFF) 578 return; 579 } 580 581 if (server == NULL) 582 server = g_strdup(gjc->user->server); 583 584 gjc->parser = XML_ParserCreate(NULL); 585 XML_SetUserData(gjc->parser, (void *)gjc); 586 XML_SetElementHandler(gjc->parser, startElement, endElement); 587 XML_SetCharacterDataHandler(gjc->parser, charData); 588 589 if (ssl) { 590 if ((gjc->ssl = ssl_connect(server, port, gjab_connected_ssl, GJ_GC(gjc)))) 591 gjc->fd = ssl_getfd(gjc->ssl); 592 else 593 gjc->fd = -1; 594 } else { 595 gjc->fd = proxy_connect(server, port, gjab_connected, GJ_GC(gjc)); 596 } 597 598 if (!acc->gc || (gjc->fd < 0)) { 599 STATE_EVT(JCONN_STATE_OFF) 600 return; 601 } 602 } 603 604 /* 605 * Find existing/active Jabber chat 606 */ 607 static struct jabber_chat *find_existing_chat(struct gaim_connection *gc, jid chat) 608 { 609 GSList *jcs = ((struct jabber_data *)gc->proto_data)->chats; 610 struct jabber_chat *jc = NULL; 611 612 while (jcs) { 613 jc = jcs->data; 614 if (jc->state == JCS_ACTIVE && !jid_cmpx(chat, jc->Jid, JID_USER | JID_SERVER)) 615 break; 616 jc = NULL; 617 jcs = jcs->next; 618 } 619 620 return jc; 621 } 622 623 /* 624 * Find pending chat 625 */ 626 static struct jabber_chat *find_pending_chat(struct gaim_connection *gc, jid chat) 627 { 628 GSList *jcs = ((struct jabber_data *)gc->proto_data)->chats; 629 struct jabber_chat *jc = NULL; 630 631 while (jcs) { 632 jc = jcs->data; 633 if (jc->state == JCS_PENDING && !jid_cmpx(chat, jc->Jid, JID_USER | JID_SERVER)) 634 break; 635 jc = NULL; 636 jcs = jcs->next; 637 } 638 639 return jc; 640 } 641 642 static gboolean find_chat_buddy(struct conversation *b, char *name) 643 { 644 GList *m = b->in_room; 645 646 while (m) { 647 if (!strcmp(m->data, name)) 648 return TRUE; 649 m = m->next; 650 } 651 652 return FALSE; 653 } 654 655 /* 656 * Remove a buddy from the (gaim) buddylist (if he's on it) 657 */ 658 static void jabber_remove_gaim_buddy(struct gaim_connection *gc, char *buddyname) 659 { 660 struct buddy *b; 661 662 if ((b = find_buddy(gc, buddyname)) != NULL) { 663 /* struct group *group; 664 665 group = find_group_by_buddy(gc, buddyname); 666 remove_buddy(gc, group, b); */ 667 jabber_remove_buddy(gc, b->name, JABBER_GROUP); 668 } 669 } 670 671 /* 672 * keep track of away msg same as yahoo plugin 673 */ 674 static void jabber_track_away(gjconn gjc, jpacket p, char *name, char *type) 675 { 676 struct jabber_data *jd = GJ_GC(gjc)->proto_data; 677 gpointer val = g_hash_table_lookup(jd->hash, name); 678 char *show; 679 char *vshow = NULL; 680 char *status = NULL; 681 char *msg = NULL; 682 683 if (type && (g_strcasecmp(type, "unavailable") == 0)) { 684 vshow = _("Unavailable"); 685 } else { 686 if((show = xmlnode_get_tag_data(p->x, "show")) != NULL) { 687 if (!g_strcasecmp(show, "away")) { 688 vshow = _("Away"); 689 } else if (!g_strcasecmp(show, "chat")) { 690 vshow = _("Online"); 691 } else if (!g_strcasecmp(show, "xa")) { 692 vshow = _("Extended Away"); 693 } else if (!g_strcasecmp(show, "dnd")) { 694 vshow = _("Do Not Disturb"); 695 } 696 } 697 } 698 699 status = xmlnode_get_tag_data(p->x, "status"); 700 701 if(vshow != NULL || status != NULL ) { 702 /* kinda hokey, but it works :-) */ 703 msg = g_strdup_printf("%s%s%s", 704 (vshow == NULL? "" : vshow), 705 (vshow == NULL || status == NULL? "" : ": "), 706 (status == NULL? "" : status)); 707 } else { 708 msg = g_strdup(_("Online")); 709 } 710 711 if (val) { 712 g_free(val); 713 g_hash_table_insert(jd->hash, name, msg); 714 } else { 715 g_hash_table_insert(jd->hash, g_strdup(name), msg); 716 } 717 } 718 719 static time_t iso8601_to_time(char *timestamp) 720 { 721 struct tm t; 722 time_t retval = 0; 723 724 if(sscanf(timestamp,"%04d%02d%02dT%02d:%02d:%02d", 725 &t.tm_year, &t.tm_mon, &t.tm_mday, &t.tm_hour, &t.tm_min, &t.tm_sec)) 726 { 727 t.tm_year -= 1900; 728 t.tm_mon -= 1; 729 t.tm_isdst = 0; 730 retval = mktime(&t); 731 # ifdef HAVE_TM_GMTOFF 732 retval += t.tm_gmtoff; 733 # else 734 # ifdef HAVE_TIMEZONE 735 tzset(); /* making sure */ 736 retval -= timezone; 737 # endif 738 # endif 739 } 740 741 return retval; 742 } 743 744 static void jabber_handlemessage(gjconn gjc, jpacket p) 745 { 746 xmlnode y, xmlns, z; 747 time_t time_sent = time(NULL); 748 749 char *from = NULL, *msg = NULL, *type = NULL; 750 char m[BUF_LONG * 2]; 751 752 type = xmlnode_get_attrib(p->x, "type"); 753 754 z = xmlnode_get_firstchild(p->x); 755 756 while(z) 757 { 758 if(NSCHECK(z,NS_DELAY)) 759 { 760 char *timestamp = xmlnode_get_attrib(z,"stamp"); 761 time_sent = iso8601_to_time(timestamp); 762 } 763 z = xmlnode_get_nextsibling(z); 764 } 765 766 if (!type || !g_strcasecmp(type, "normal") || !g_strcasecmp(type, "chat")) { 767 768 /* XXX namespaces could be handled better. (mid) */ 769 if ((xmlns = xmlnode_get_tag(p->x, "x"))) 770 type = xmlnode_get_attrib(xmlns, "xmlns"); 771 772 from = jid_full(p->from); 773 /* 774 if ((y = xmlnode_get_tag(p->x, "html"))) { 775 msg = xmlnode_get_data(y); 776 } else 777 */ 778 if ((y = xmlnode_get_tag(p->x, "body"))) { 779 msg = xmlnode_get_data(y); 780 } 781 782 783 if (!from) 784 return; 785 786 if (type && !g_strcasecmp(type, "jabber:x:conference")) { 787 /* do nothing */ 788 } else if (msg) { /* whisper */ 789 struct jabber_chat *jc; 790 g_snprintf(m, sizeof(m), "%s", msg); 791 if (((jc = find_existing_chat(GJ_GC(gjc), p->from)) != NULL) && jc->b) 792 serv_got_chat_in(GJ_GC(gjc), jc->b->id, p->from->resource, 1, m, time_sent); 793 else { 794 int flags = 0; 795 796 if(p->from->user) { 797 from = g_strdup_printf("%s@%s", p->from->user, p->from->server); 798 } else { 799 /* server message? */ 800 from = g_strdup(p->from->server); 801 } 802 serv_got_im(GJ_GC(gjc), from, m, flags, time_sent, -1); 803 g_free(from); 804 } 805 } 806 807 } else if (!g_strcasecmp(type, "error")) { 808 if ((y = xmlnode_get_tag(p->x, "error"))) { 809 type = xmlnode_get_attrib(y, "code"); 810 msg = xmlnode_get_data(y); 811 } 812 813 if (msg) { 814 from = g_strdup_printf("Error %s", type ? type : ""); 815 do_error_dialog(GJ_GC(gjc), msg, from); 816 g_free(from); 817 } 818 } else if (!g_strcasecmp(type, "headline")) { 819 char *subject, *body, *url; 820 821 y = xmlnode_get_tag( p->x, "body" ); 822 body = y ? g_strdup( xmlnode_get_data( y ) ) : NULL; 823 824 y = xmlnode_get_tag( p->x, "subject" ); 825 subject = y ? g_strdup( xmlnode_get_data( y ) ) : NULL; 826 827 url = NULL; 828 z = xmlnode_get_firstchild(p->x); 829 while( z ) 830 { 831 char *xtype = xmlnode_get_attrib( z, "xmlns" ); 832 833 if( xtype && g_strcasecmp( xtype, "jabber:x:oob" ) == 0 && 834 ( y = xmlnode_get_tag( z, "url" ) ) ) 835 { 836 url = g_strdup( xmlnode_get_data( y ) ); 837 break; 838 } 839 840 z = xmlnode_get_nextsibling( z ); 841 } 842 843 g_snprintf( m, BUF_LONG, "Subject: %s\nURL: %s\nMessage:\n%s", subject ? subject : "(none)", 844 url ? url : "(none)", body ? body : "(none)" ); 845 846 if( p->from->user ) 847 from = g_strdup_printf( "%s@%s", p->from->user, p->from->server ); 848 else 849 from = g_strdup( p->from->server ); 850 851 serv_got_im( GJ_GC(gjc), from, m, 0, time_sent, -1 ); 852 853 g_free( from ); 854 g_free( subject ); 855 g_free( body ); 856 g_free( url ); 857 } 858 } 859 860 static void jabber_handlepresence(gjconn gjc, jpacket p) 861 { 862 char *to, *from, *type; 863 struct buddy *b = NULL; 864 jid who; 865 char *buddy; 866 xmlnode y; 867 char *show; 868 int state = 0; 869 GSList *resources; 870 char *res; 871 struct conversation *cnv = NULL; 872 struct jabber_chat *jc = NULL; 873 874 to = xmlnode_get_attrib(p->x, "to"); 875 from = xmlnode_get_attrib(p->x, "from"); 876 type = xmlnode_get_attrib(p->x, "type"); 877 878 if (type && g_strcasecmp(type, "error") == 0) { 879 return; 880 } 881 else if ((y = xmlnode_get_tag(p->x, "show"))) { 882 show = xmlnode_get_data(y); 883 if (!show) { 884 state = 0; 885 } else if (!g_strcasecmp(show, "away")) { 886 state = UC_AWAY; 887 } else if (!g_strcasecmp(show, "chat")) { 888 state = UC_CHAT; 889 } else if (!g_strcasecmp(show, "xa")) { 890 state = UC_XA; 891 } else if (!g_strcasecmp(show, "dnd")) { 892 state = UC_DND; 893 } 894 } else { 895 state = 0; 896 } 897 898 who = jid_new(gjc->p, from); 899 if (who->user == NULL) { 900 /* FIXME: transport */ 901 return; 902 } 903 904 buddy = g_strdup_printf("%s@%s", who->user, who->server); 905 906 /* um. we're going to check if it's a chat. if it isn't, and there are pending 907 * chats, create the chat. if there aren't pending chats and we don't have the 908 * buddy on our list, simply bail out. */ 909 if ((cnv = NULL) == NULL) { 910 static int i = 0x70; 911 if ((jc = find_pending_chat(GJ_GC(gjc), who)) != NULL) { 912 jc->b = cnv = serv_got_joined_chat(GJ_GC(gjc), i++, who->user); 913 jc->id = jc->b->id; 914 jc->state = JCS_ACTIVE; 915 } else if ((b = find_buddy(GJ_GC(gjc), buddy)) == NULL) { 916 g_free(buddy); 917 return; 918 } 919 } 920 921 if (!cnv) { 922 resources = b->proto_data; 923 res = who->resource; 924 if (res) 925 while (resources) { 926 if (!strcmp(res, resources->data)) 927 break; 928 resources = resources->next; 929 } 930 931 /* keep track of away msg same as yahoo plugin */ 932 jabber_track_away(gjc, p, normalize(b->name), type); 933 934 if (type && (g_strcasecmp(type, "unavailable") == 0)) { 935 if (resources) { 936 g_free(resources->data); 937 b->proto_data = g_slist_remove(b->proto_data, resources->data); 938 } 939 if (!b->proto_data) { 940 serv_got_update(GJ_GC(gjc), buddy, 0, 0, 0, 0, 0, 0); 941 } 942 } else { 943 if (!resources) { 944 b->proto_data = g_slist_append(b->proto_data, g_strdup(res)); 945 } 946 947 serv_got_update(GJ_GC(gjc), buddy, 1, 0, b->signon, b->idle, state, 0); 948 949 } 950 } else { 951 if (who->resource) { 952 char *buf; 953 954 buf = g_strdup_printf("%s@%s/%s", who->user, who->server, who->resource); 955 jabber_track_away(gjc, p, buf, type); 956 g_free(buf); 957 958 if (type && !g_strcasecmp(type, "unavailable")) { 959 struct jabber_data *jd; 960 if (!jc && !(jc = find_existing_chat(GJ_GC(gjc), who))) { 961 g_free(buddy); 962 return; 963 } 964 jd = jc->gc->proto_data; 965 /* if it's not ourselves...*/ 966 if (strcmp(who->resource, jc->Jid->resource) && jc->b) { 967 remove_chat_buddy(jc->b, who->resource, NULL); 968 g_free(buddy); 969 return; 970 } 971 972 jc->state = JCS_CLOSED; 973 serv_got_chat_left(GJ_GC(gjc), jc->id); 974 /* 975 * TBD: put back some day? 976 jd->chats = g_slist_remove(jd->chats, jc); 977 g_free(jc); 978 */ 979 } else { 980 if ((!jc && !(jc = find_existing_chat(GJ_GC(gjc), who))) || !jc->b) { 981 g_free(buddy); 982 return; 983 } 984 if (!find_chat_buddy(jc->b, who->resource)) { 985 add_chat_buddy(jc->b, who->resource); 986 } 987 } 988 } 989 } 990 991 g_free(buddy); 992 993 return; 994 } 995 996 /* 997 * Used only by Jabber accept/deny add stuff just below 998 */ 999 struct jabber_add_permit { 1000 gjconn gjc; 1001 gchar *user; 1002 }; 1003 1004 /* 1005 * Common part for Jabber accept/deny adds 1006 * 1007 * "type" says whether we'll permit/deny the subscribe request 1008 */ 1009 static void jabber_accept_deny_add(struct jabber_add_permit *jap, const char *type) 1010 { 1011 xmlnode g = xmlnode_new_tag("presence"); 1012 1013 xmlnode_put_attrib(g, "to", jap->user); 1014 xmlnode_put_attrib(g, "type", type); 1015 gjab_send(jap->gjc, g); 1016 1017 xmlnode_free(g); 1018 } 1019 1020 /* 1021 * Callback from "accept" in do_ask_dialog() invoked by jabber_handles10n() 1022 */ 1023 static void jabber_accept_add(gpointer w, struct jabber_add_permit *jap) 1024 { 1025 jabber_accept_deny_add(jap, "subscribed"); 1026 /* 1027 * If we don't already have the buddy on *our* buddylist, 1028 * ask if we want him or her added. 1029 */ 1030 if(find_buddy(GJ_GC(jap->gjc), jap->user) == NULL) { 1031 show_got_added(GJ_GC(jap->gjc), jap->user, NULL); 1032 } 1033 g_free(jap->user); 1034 g_free(jap); 1035 } 1036 1037 /* 1038 * Callback from "deny/cancel" in do_ask_dialog() invoked by jabber_handles10n() 1039 */ 1040 static void jabber_deny_add(gpointer w, struct jabber_add_permit *jap) 1041 { 1042 jabber_accept_deny_add(jap, "unsubscribed"); 1043 g_free(jap->user); 1044 g_free(jap); 1045 } 1046 1047 /* 1048 * Handle subscription requests 1049 */ 1050 static void jabber_handles10n(gjconn gjc, jpacket p) 1051 { 1052 xmlnode g; 1053 char *Jid = xmlnode_get_attrib(p->x, "from"); 1054 char *type = xmlnode_get_attrib(p->x, "type"); 1055 1056 g = xmlnode_new_tag("presence"); 1057 xmlnode_put_attrib(g, "to", Jid); 1058 1059 if (!strcmp(type, "subscribe")) { 1060 /* 1061 * A "subscribe to us" request was received - put up the approval dialog 1062 */ 1063 struct jabber_add_permit *jap = g_new0(struct jabber_add_permit, 1); 1064 gchar *msg = g_strdup_printf(_("The user %s wants to add you to his/her buddy list."), 1065 Jid); 1066 1067 jap->gjc = gjc; 1068 jap->user = g_strdup(Jid); 1069 do_ask_dialog(GJ_GC(gjc), msg, jap, jabber_accept_add, jabber_deny_add); 1070 1071 g_free(msg); 1072 xmlnode_free(g); /* Never needed it here anyway */ 1073 return; 1074 1075 } else if (!strcmp(type, "unsubscribe")) { 1076 /* 1077 * An "unsubscribe to us" was received - simply "approve" it 1078 */ 1079 xmlnode_put_attrib(g, "type", "unsubscribed"); 1080 } else { 1081 /* 1082 * Did we attempt to subscribe to somebody and they do not exist? 1083 */ 1084 if (!strcmp(type, "unsubscribed")) { 1085 xmlnode y; 1086 char *status; 1087 if((y = xmlnode_get_tag(p->x, "status")) && (status = xmlnode_get_data(y)) && 1088 !strcmp(status, "Not Found")) { 1089 char *msg = g_strdup_printf("%s: \"%s\"", _("No such user"), 1090 xmlnode_get_attrib(p->x, "from")); 1091 do_error_dialog(GJ_GC(gjc), msg, _("Jabber Error")); 1092 g_free(msg); 1093 } 1094 } 1095 1096 xmlnode_free(g); 1097 return; 1098 } 1099 1100 gjab_send(gjc, g); 1101 xmlnode_free(g); 1102 } 1103 1104 /* 1105 * Pending subscription to a buddy? 1106 */ 1107 #define BUD_SUB_TO_PEND(sub, ask) ((!g_strcasecmp((sub), "none") || !g_strcasecmp((sub), "from")) && \ 1108 (ask) != NULL && !g_strcasecmp((ask), "subscribe")) 1109 1110 /* 1111 * Subscribed to a buddy? 1112 */ 1113 #define BUD_SUBD_TO(sub, ask) ((!g_strcasecmp((sub), "to") || !g_strcasecmp((sub), "both")) && \ 1114 ((ask) == NULL || !g_strcasecmp((ask), "subscribe"))) 1115 1116 /* 1117 * Pending unsubscription to a buddy? 1118 */ 1119 #define BUD_USUB_TO_PEND(sub, ask) ((!g_strcasecmp((sub), "to") || !g_strcasecmp((sub), "both")) && \ 1120 (ask) != NULL && !g_strcasecmp((ask), "unsubscribe")) 1121 1122 /* 1123 * Unsubscribed to a buddy? 1124 */ 1125 #define BUD_USUBD_TO(sub, ask) ((!g_strcasecmp((sub), "none") || !g_strcasecmp((sub), "from")) && \ 1126 ((ask) == NULL || !g_strcasecmp((ask), "unsubscribe"))) 1127 1128 /* 1129 * If a buddy is added or removed from the roster on another resource 1130 * jabber_handlebuddy is called 1131 * 1132 * Called with roster item node. 1133 */ 1134 static void jabber_handlebuddy(gjconn gjc, xmlnode x) 1135 { 1136 xmlnode g; 1137 char *Jid, *name, *sub, *ask; 1138 jid who; 1139 struct buddy *b = NULL; 1140 char *buddyname, *groupname = NULL; 1141 1142 Jid = xmlnode_get_attrib(x, "jid"); 1143 name = xmlnode_get_attrib(x, "name"); 1144 sub = xmlnode_get_attrib(x, "subscription"); 1145 ask = xmlnode_get_attrib(x, "ask"); 1146 who = jid_new(gjc->p, Jid); 1147 1148 /* JFIXME: jabber_handleroster() had a "FIXME: transport" at this 1149 * equivilent point. So... 1150 * 1151 * We haven't allocated any memory or done anything interesting to 1152 * this point, so we'll violate Good Coding Structure here by 1153 * simply bailing out. 1154 */ 1155 if (!who || !who->user) { 1156 return; 1157 } 1158 1159 buddyname = g_strdup_printf("%s@%s", who->user, who->server); 1160 1161 if((g = xmlnode_get_tag(x, "group")) != NULL) { 1162 groupname = xmlnode_get_data(g); 1163 } 1164 1165 /* 1166 * Add or remove a buddy? Change buddy's alias or group? 1167 */ 1168 if (BUD_SUB_TO_PEND(sub, ask) || BUD_SUBD_TO(sub, ask)) { 1169 if ((b = find_buddy(GJ_GC(gjc), buddyname)) == NULL) { 1170 add_buddy(GJ_GC(gjc), groupname ? groupname : _("Buddies"), buddyname, 1171 name ? name : buddyname); 1172 } else { 1173 /* struct group *c_grp = find_group_by_buddy(GJ_GC(gjc), buddyname); */ 1174 1175 /* 1176 * If the buddy's in a new group or his/her alias is changed... 1177 */ 1178 if(groupname) { 1179 int present = b->present; /* save presence state */ 1180 int uc = b->uc; /* and away state (?) */ 1181 int idle = b->idle; 1182 int signon = b->signon; 1183 1184 /* 1185 * seems rude, but it seems to be the only way... 1186 */ 1187 /* remove_buddy(GJ_GC(gjc), c_grp, b); */ 1188 jabber_remove_buddy(GJ_GC(gjc), b->name, JABBER_GROUP); 1189 1190 add_buddy(GJ_GC(gjc), groupname, buddyname, 1191 name ? name : buddyname); 1192 if(present) { 1193 serv_got_update(GJ_GC(gjc), buddyname, 1, 0, signon, idle, uc, 0); 1194 } 1195 } else if(name != NULL && strcmp(b->show, name)) { 1196 strncpy(b->show, name, BUDDY_ALIAS_MAXLEN); 1197 b->show[BUDDY_ALIAS_MAXLEN - 1] = '\0'; /* cheap safety feature */ 1198 serv_buddy_rename(GJ_GC(gjc), buddyname, b->show); 1199 } 1200 } 1201 } else if (BUD_USUB_TO_PEND(sub, ask) || BUD_USUBD_TO(sub, ask) || !g_strcasecmp(sub, "remove")) { 1202 jabber_remove_gaim_buddy(GJ_GC(gjc), buddyname); 1203 } 1204 g_free(buddyname); 1205 1206 } 1207 1208 static void jabber_handleroster(gjconn gjc, xmlnode querynode) 1209 { 1210 xmlnode x; 1211 1212 x = xmlnode_get_firstchild(querynode); 1213 while (x) { 1214 jabber_handlebuddy(gjc, x); 1215 x = xmlnode_get_nextsibling(x); 1216 } 1217 1218 account_online(GJ_GC(gjc)); 1219 } 1220 1221 static void jabber_handleauthresp(gjconn gjc, jpacket p) 1222 { 1223 if (jpacket_subtype(p) == JPACKET__RESULT) { 1224 if (xmlnode_has_children(p->x)) { 1225 xmlnode query = xmlnode_get_tag(p->x, "query"); 1226 set_login_progress(GJ_GC(gjc), 4, _("Authenticating")); 1227 if (!xmlnode_get_tag(query, "digest")) { 1228 g_free(gjc->sid); 1229 gjc->sid = NULL; 1230 } 1231 gjab_auth(gjc); 1232 } else { 1233 gjab_reqroster(gjc); 1234 1235 ((struct jabber_data *)GJ_GC(gjc)->proto_data)->did_import = TRUE; 1236 } 1237 } else { 1238 xmlnode xerr; 1239 char *errmsg = NULL; 1240 int errcode = 0; 1241 struct jabber_data *jd = GJ_GC(gjc)->proto_data; 1242 1243 xerr = xmlnode_get_tag(p->x, "error"); 1244 if (xerr) { 1245 char msg[BUF_LONG]; 1246 errmsg = xmlnode_get_data(xerr); 1247 if (xmlnode_get_attrib(xerr, "code")) { 1248 errcode = atoi(xmlnode_get_attrib(xerr, "code")); 1249 g_snprintf(msg, sizeof(msg), "Error %d: %s", errcode, errmsg ? errmsg : "Unknown error"); 1250 } else 1251 g_snprintf(msg, sizeof(msg), "%s", errmsg); 1252 hide_login_progress(GJ_GC(gjc), msg); 1253 } else { 1254 hide_login_progress(GJ_GC(gjc), _("Unknown login error")); 1255 } 1256 1257 jd->die = TRUE; 1258 } 1259 } 1260 1261 static void jabber_handleversion(gjconn gjc, xmlnode iqnode) { 1262 xmlnode querynode, x; 1263 char *id, *from; 1264 char os[1024]; 1265 #ifndef _WIN32 1266 struct utsname osinfo; 1267 1268 uname(&osinfo); 1269 g_snprintf(os, sizeof os, "%s %s %s", osinfo.sysname, osinfo.release, osinfo.machine); 1270 #else 1271 g_snprintf(os, sizeof os, "Windows %d %d", _winmajor, _winminor); 1272 #endif 1273 1274 1275 id = xmlnode_get_attrib(iqnode, "id"); 1276 from = xmlnode_get_attrib(iqnode, "from"); 1277 1278 x = jutil_iqnew(JPACKET__RESULT, NS_VERSION); 1279 1280 xmlnode_put_attrib(x, "to", from); 1281 xmlnode_put_attrib(x, "id", id); 1282 querynode = xmlnode_get_tag(x, "query"); 1283 xmlnode_insert_cdata(xmlnode_insert_tag(querynode, "name"), PACKAGE, -1); 1284 xmlnode_insert_cdata(xmlnode_insert_tag(querynode, "version"), BITLBEE_VERSION, -1); 1285 xmlnode_insert_cdata(xmlnode_insert_tag(querynode, "os"), os, -1); 1286 1287 gjab_send(gjc, x); 1288 1289 xmlnode_free(x); 1290 } 1291 1292 static void jabber_handletime(gjconn gjc, xmlnode iqnode) { 1293 xmlnode querynode, x; 1294 char *id, *from; 1295 time_t now_t; 1296 struct tm *now; 1297 char buf[1024]; 1298 1299 time(&now_t); 1300 now = localtime(&now_t); 1301 1302 id = xmlnode_get_attrib(iqnode, "id"); 1303 from = xmlnode_get_attrib(iqnode, "from"); 1304 1305 x = jutil_iqnew(JPACKET__RESULT, NS_TIME); 1306 1307 xmlnode_put_attrib(x, "to", from); 1308 xmlnode_put_attrib(x, "id", id); 1309 querynode = xmlnode_get_tag(x, "query"); 1310 1311 strftime(buf, 1024, "%Y%m%dT%T", now); 1312 xmlnode_insert_cdata(xmlnode_insert_tag(querynode, "utc"), buf, -1); 1313 strftime(buf, 1024, "%Z", now); 1314 xmlnode_insert_cdata(xmlnode_insert_tag(querynode, "tz"), buf, -1); 1315 strftime(buf, 1024, "%d %b %Y %T", now); 1316 xmlnode_insert_cdata(xmlnode_insert_tag(querynode, "display"), buf, -1); 1317 1318 gjab_send(gjc, x); 1319 1320 xmlnode_free(x); 1321 } 1322 1323 static void jabber_handlelast(gjconn gjc, xmlnode iqnode) { 1324 xmlnode x, querytag; 1325 char *id, *from; 1326 struct jabber_data *jd = GJ_GC(gjc)->proto_data; 1327 char idle_time[32]; 1328 1329 id = xmlnode_get_attrib(iqnode, "id"); 1330 from = xmlnode_get_attrib(iqnode, "from"); 1331 1332 x = jutil_iqnew(JPACKET__RESULT, "jabber:iq:last"); 1333 1334 xmlnode_put_attrib(x, "to", from); 1335 xmlnode_put_attrib(x, "id", id); 1336 querytag = xmlnode_get_tag(x, "query"); 1337 g_snprintf(idle_time, sizeof idle_time, "%ld", jd->idle ? time(NULL) - jd->idle : 0); 1338 xmlnode_put_attrib(querytag, "seconds", idle_time); 1339 1340 gjab_send(gjc, x); 1341 xmlnode_free(x); 1342 } 1343 1344 /* 1345 * delete == TRUE: delete found entry 1346 * 1347 * returns pointer to (local) copy of value if found, NULL otherwise 1348 * 1349 * Note: non-reentrant! Local static storage re-used on subsequent calls. 1350 * If you're going to need to keep the returned value, make a copy! 1351 */ 1352 static gchar *jabber_track_queries(GHashTable *queries, gchar *key, gboolean delete) 1353 { 1354 gpointer my_key, my_val; 1355 static gchar *ret_val = NULL; 1356 1357 if(ret_val != NULL) { 1358 g_free(ret_val); 1359 ret_val = NULL; 1360 } 1361 1362 /* self-protection */ 1363 if(queries != NULL && key != NULL) { 1364 if(g_hash_table_lookup_extended(queries, key, &my_key, &my_val)) { 1365 ret_val = g_strdup((gchar *) my_val); 1366 if(delete) { 1367 g_hash_table_remove(queries, key); 1368 g_free(my_key); 1369 g_free(my_val); 1370 } 1371 } 1372 } 1373 1374 return(ret_val); 1375 } 1376 1377 static void jabber_handlepacket(gjconn gjc, jpacket p) 1378 { 1379 char *id; 1380 switch (p->type) { 1381 case JPACKET_MESSAGE: 1382 jabber_handlemessage(gjc, p); 1383 break; 1384 case JPACKET_PRESENCE: 1385 jabber_handlepresence(gjc, p); 1386 break; 1387 case JPACKET_IQ: 1388 id = xmlnode_get_attrib(p->x, "id"); 1389 if (id != NULL && !strcmp(id, IQID_AUTH)) { 1390 jabber_handleauthresp(gjc, p); 1391 break; 1392 } 1393 1394 if (jpacket_subtype(p) == JPACKET__SET) { 1395 xmlnode querynode; 1396 querynode = xmlnode_get_tag(p->x, "query"); 1397 if (NSCHECK(querynode, "jabber:iq:roster")) { 1398 jabber_handlebuddy(gjc, xmlnode_get_firstchild(querynode)); 1399 } 1400 } else if (jpacket_subtype(p) == JPACKET__GET) { 1401 xmlnode querynode; 1402 querynode = xmlnode_get_tag(p->x, "query"); 1403 if (NSCHECK(querynode, NS_VERSION)) { 1404 jabber_handleversion(gjc, p->x); 1405 } else if (NSCHECK(querynode, NS_TIME)) { 1406 jabber_handletime(gjc, p->x); 1407 } else if (NSCHECK(querynode, "jabber:iq:last")) { 1408 jabber_handlelast(gjc, p->x); 1409 } 1410 } else if (jpacket_subtype(p) == JPACKET__RESULT) { 1411 xmlnode querynode, vcard; 1412 /* char *xmlns; */ 1413 char *from; 1414 1415 /* 1416 * TBD: ISTM maybe this part could use a serious re-work? 1417 */ 1418 from = xmlnode_get_attrib(p->x, "from"); 1419 querynode = xmlnode_get_tag(p->x, "query"); 1420 vcard = xmlnode_get_tag(p->x, "vCard"); 1421 if (!vcard) 1422 vcard = xmlnode_get_tag(p->x, "VCARD"); 1423 1424 if (NSCHECK(querynode, NS_ROSTER)) { 1425 jabber_handleroster(gjc, querynode); 1426 } else if (NSCHECK(querynode, NS_VCARD)) { 1427 jabber_track_queries(gjc->queries, id, TRUE); /* delete query track */ 1428 jabber_handlevcard(gjc, querynode, from); 1429 } else if (vcard) { 1430 jabber_track_queries(gjc->queries, id, TRUE); /* delete query track */ 1431 jabber_handlevcard(gjc, vcard, from); 1432 } else { 1433 char *val; 1434 1435 /* handle "null" query results */ 1436 if((val = jabber_track_queries(gjc->queries, id, TRUE)) != NULL) { 1437 if (!g_strncasecmp(val, "vcard", 5)) { 1438 jabber_handlevcard(gjc, NULL, from); 1439 } 1440 1441 /* No-op */ 1442 } 1443 } 1444 1445 } else if (jpacket_subtype(p) == JPACKET__ERROR) { 1446 xmlnode xerr; 1447 char *from, *errmsg = NULL; 1448 int errcode = 0; 1449 1450 from = xmlnode_get_attrib(p->x, "from"); 1451 xerr = xmlnode_get_tag(p->x, "error"); 1452 if (xerr) { 1453 errmsg = xmlnode_get_data(xerr); 1454 if (xmlnode_get_attrib(xerr, "code")) 1455 errcode = atoi(xmlnode_get_attrib(xerr, "code")); 1456 } 1457 1458 from = g_strdup_printf("Error %d (%s)", errcode, from); 1459 do_error_dialog(GJ_GC(gjc), errmsg, from); 1460 g_free(from); 1461 1462 } 1463 1464 break; 1465 case JPACKET_S10N: 1466 jabber_handles10n(gjc, p); 1467 break; 1468 } 1469 1470 xmlnode_free(p->x); 1471 1472 return; 1473 } 1474 1475 static void jabber_handlestate(gjconn gjc, int state) 1476 { 1477 switch (state) { 1478 case JCONN_STATE_OFF: 1479 if(gjc->was_connected) { 1480 hide_login_progress_error(GJ_GC(gjc), _("Connection lost")); 1481 } else { 1482 hide_login_progress(GJ_GC(gjc), _("Unable to connect")); 1483 } 1484 signoff(GJ_GC(gjc)); 1485 break; 1486 case JCONN_STATE_CONNECTED: 1487 gjc->was_connected = 1; 1488 set_login_progress(GJ_GC(gjc), 2, _("Connected")); 1489 break; 1490 case JCONN_STATE_ON: 1491 set_login_progress(GJ_GC(gjc), 3, _("Requesting Authentication Method")); 1492 gjab_reqauth(gjc); 1493 break; 1494 } 1495 return; 1496 } 1497 1498 static void jabber_acc_init(account_t *acc) 34 35 static void jabber_acc_init( account_t *acc ) 1499 36 { 1500 37 set_t *s; … … 1503 40 s->flags |= ACC_SET_OFFLINE_ONLY; 1504 41 42 s = set_add( &acc->set, "priority", "0", set_eval_priority, acc ); 43 1505 44 s = set_add( &acc->set, "resource", "BitlBee", NULL, acc ); 1506 45 s->flags |= ACC_SET_OFFLINE_ONLY; 1507 46 47 s = set_add( &acc->set, "resource_select", "priority", NULL, acc ); 48 1508 49 s = set_add( &acc->set, "server", NULL, set_eval_account, acc ); 1509 50 s->flags |= ACC_SET_NOSAVE | ACC_SET_OFFLINE_ONLY; … … 1511 52 s = set_add( &acc->set, "ssl", "false", set_eval_bool, acc ); 1512 53 s->flags |= ACC_SET_OFFLINE_ONLY; 1513 } 1514 1515 static void jabber_login(account_t *acc) 1516 { 1517 struct gaim_connection *gc; 1518 struct jabber_data *jd; 1519 char *resource, *loginname; 1520 1521 /* Time to move some data/things from the old syntax to the new one: */ 1522 if (acc->server) { 1523 char *s, *tmp_server; 1524 int port; 1525 1526 if (g_strcasecmp(acc->server, "ssl") == 0) { 1527 set_setstr(&acc->set, "server", ""); 1528 set_setint(&acc->set, "port", DEFAULT_PORT_SSL); 1529 set_setstr(&acc->set, "ssl", "true"); 1530 1531 g_free(acc->server); 1532 acc->server = NULL; 1533 } else if ((s = strchr(acc->server, ':'))) { 1534 if (strstr(acc->server, ":ssl")) { 1535 set_setint(&acc->set, "port", DEFAULT_PORT_SSL); 1536 set_setstr(&acc->set, "ssl", "true"); 1537 } 1538 if (isdigit(s[1])) { 1539 if (sscanf(s + 1, "%d", &port) == 1) 1540 set_setint(&acc->set, "port", port); 1541 } 1542 tmp_server = g_strndup(acc->server, s - acc->server); 1543 set_setstr(&acc->set, "server", tmp_server); 1544 g_free(tmp_server); 1545 } 1546 } 1547 1548 gc = new_gaim_conn(acc); 1549 jd = gc->proto_data = g_new0(struct jabber_data, 1); 1550 1551 if( strchr( acc->user, '@' ) == NULL ) 1552 { 1553 hide_login_progress( gc, "Invalid account name" ); 54 55 s = set_add( &acc->set, "tls", "try", set_eval_tls, acc ); 56 s->flags |= ACC_SET_OFFLINE_ONLY; 57 } 58 59 static void jabber_login( account_t *acc ) 60 { 61 struct gaim_connection *gc = new_gaim_conn( acc ); 62 struct jabber_data *jd = g_new0( struct jabber_data, 1 ); 63 struct ns_srv_reply *srv = NULL; 64 char *connect_to; 65 66 jd->gc = gc; 67 gc->proto_data = jd; 68 69 jd->username = g_strdup( acc->user ); 70 jd->server = strchr( jd->username, '@' ); 71 72 if( jd->server == NULL ) 73 { 74 hide_login_progress( gc, "Incomplete account name (format it like <username@jabberserver.name>)" ); 1554 75 signoff( gc ); 1555 76 return; 1556 77 } 1557 78 1558 resource = set_getstr(&acc->set, "resource"); 1559 loginname = create_valid_jid(acc->user, DEFAULT_SERVER, resource); 1560 1561 jd->hash = g_hash_table_new(g_str_hash, g_str_equal); 1562 jd->chats = NULL; /* we have no chats yet */ 1563 1564 set_login_progress(gc, 1, _("Connecting")); 1565 1566 if (!(jd->gjc = gjab_new(loginname, acc->pass, gc))) { 1567 g_free(loginname); 1568 hide_login_progress(gc, _("Unable to connect")); 1569 signoff(gc); 1570 return; 1571 } 1572 1573 g_free(loginname); 1574 gjab_state_handler(jd->gjc, jabber_handlestate); 1575 gjab_packet_handler(jd->gjc, jabber_handlepacket); 1576 jd->gjc->queries = g_hash_table_new(g_str_hash, g_str_equal); 1577 gjab_start(jd->gjc); 1578 } 1579 1580 static gboolean jabber_destroy_hash(gpointer key, gpointer val, gpointer data) { 1581 g_free(key); 1582 g_free(val); 1583 return TRUE; 1584 } 1585 1586 static gboolean jabber_free(gpointer data, gint fd, b_input_condition cond) 1587 { 1588 struct jabber_data *jd = data; 1589 1590 if(jd->gjc != NULL) { 1591 gjab_delete(jd->gjc); 1592 /* YAY for modules with their own memory pool managers!... 1593 g_free(jd->gjc->sid); 1594 And a less sarcastic yay for valgrind. :-) */ 1595 jd->gjc = NULL; 1596 } 1597 g_free(jd); 1598 1599 return FALSE; 1600 } 1601 1602 static void jabber_close(struct gaim_connection *gc) 1603 { 1604 struct jabber_data *jd = gc->proto_data; 1605 1606 if(jd) { 1607 GSList *jcs = jd->chats; 1608 1609 /* Free-up the jabber_chat struct allocs and the list */ 1610 while (jcs) { 1611 g_free(jcs->data); 1612 jcs = jcs->next; 1613 } 1614 g_slist_free(jd->chats); 1615 1616 /* Free-up the away status memories and the list */ 1617 if(jd->hash != NULL) { 1618 g_hash_table_foreach_remove(jd->hash, jabber_destroy_hash, NULL); 1619 g_hash_table_destroy(jd->hash); 1620 jd->hash = NULL; 1621 } 1622 1623 /* Free-up the pending queries memories and the list */ 1624 if(jd->gjc != NULL && jd->gjc->queries != NULL) { 1625 g_hash_table_foreach_remove(jd->gjc->queries, jabber_destroy_hash, NULL); 1626 g_hash_table_destroy(jd->gjc->queries); 1627 jd->gjc->queries = NULL; 1628 } 1629 } 1630 if (gc->inpa) 1631 b_event_remove(gc->inpa); 1632 1633 if(jd) { 1634 b_timeout_add(50, jabber_free, jd); 1635 if(jd->gjc != NULL) 1636 xmlnode_free(jd->gjc->current); 1637 } 1638 gc->proto_data = NULL; 1639 } 1640 1641 static int jabber_send_im(struct gaim_connection *gc, char *who, char *message, int len, int flags) 1642 { 1643 xmlnode x, y; 1644 char *realwho; 1645 gjconn gjc = ((struct jabber_data *)gc->proto_data)->gjc; 1646 1647 if (!who || !message) 1648 return 0; 1649 1650 x = xmlnode_new_tag("message"); 1651 /* Bare username and "username" not the server itself? */ 1652 if (!strchr(who, '@') && strcmp(who, gjc->user->server) != 0) 1653 realwho = g_strdup_printf("%s@%s", who, gjc->user->server); 79 /* So don't think of free()ing jd->server.. :-) */ 80 *jd->server = 0; 81 jd->server ++; 82 83 jd->node_cache = g_hash_table_new_full( g_str_hash, g_str_equal, NULL, jabber_cache_entry_free ); 84 jd->buddies = g_hash_table_new( g_str_hash, g_str_equal ); 85 86 /* Figure out the hostname to connect to. */ 87 if( acc->server ) 88 connect_to = acc->server; 89 else if( ( srv = srv_lookup( "xmpp-client", "tcp", jd->server ) ) || 90 ( srv = srv_lookup( "jabber-client", "tcp", jd->server ) ) ) 91 connect_to = srv->name; 1654 92 else 1655 realwho = g_strdup(who); 1656 xmlnode_put_attrib(x, "to", realwho); 1657 g_free(realwho); 1658 1659 xmlnode_insert_tag(x, "bitlbee"); 1660 xmlnode_put_attrib(x, "type", "chat"); 1661 1662 if (message && strlen(message)) { 1663 y = xmlnode_insert_tag(x, "body"); 1664 xmlnode_insert_cdata(y, message, -1); 1665 } 1666 1667 gjab_send(((struct jabber_data *)gc->proto_data)->gjc, x); 1668 xmlnode_free(x); 93 connect_to = jd->server; 94 95 /* For non-SSL connections we can try to use the port # from the SRV 96 reply, but let's not do that when using SSL, SSL usually runs on 97 non-standard ports... */ 98 if( set_getbool( &acc->set, "ssl" ) ) 99 { 100 jd->ssl = ssl_connect( connect_to, set_getint( &acc->set, "port" ), jabber_connected_ssl, gc ); 101 jd->fd = ssl_getfd( jd->ssl ); 102 } 103 else 104 { 105 jd->fd = proxy_connect( connect_to, srv ? srv->port : set_getint( &acc->set, "port" ), jabber_connected_plain, gc ); 106 } 107 108 g_free( srv ); 109 } 110 111 static void jabber_close( struct gaim_connection *gc ) 112 { 113 struct jabber_data *jd = gc->proto_data; 114 115 jabber_end_stream( gc ); 116 117 if( jd->r_inpa >= 0 ) 118 b_event_remove( jd->r_inpa ); 119 if( jd->w_inpa >= 0 ) 120 b_event_remove( jd->w_inpa ); 121 122 if( jd->ssl ) 123 ssl_disconnect( jd->ssl ); 124 if( jd->fd >= 0 ) 125 closesocket( jd->fd ); 126 127 if( jd->tx_len ) 128 g_free( jd->txq ); 129 130 g_hash_table_destroy( jd->node_cache ); 131 132 xt_free( jd->xt ); 133 134 g_free( jd->away_message ); 135 g_free( jd->username ); 136 g_free( jd ); 137 } 138 139 static int jabber_send_im( struct gaim_connection *gc, char *who, char *message, int len, int away ) 140 { 141 struct jabber_data *jd = gc->proto_data; 142 struct jabber_buddy *bud; 143 struct xt_node *node; 144 int st; 145 146 bud = jabber_buddy_by_jid( gc, who ); 147 148 node = xt_new_node( "body", message, NULL ); 149 node = jabber_make_packet( "message", "chat", bud->full_jid, node ); 150 151 if( ( jd->flags & JFLAG_WANT_TYPING ) && 152 ( ( bud->flags & JBFLAG_DOES_JEP85 ) || 153 !( bud->flags & JBFLAG_PROBED_JEP85 ) ) ) 154 { 155 struct xt_node *act; 156 157 /* If the user likes typing notification and if we don't know 158 (and didn't probe before) if this resource supports JEP85, 159 include a probe in this packet now. */ 160 act = xt_new_node( "active", NULL, NULL ); 161 xt_add_attr( act, "xmlns", "http://jabber.org/protocol/chatstates" ); 162 xt_add_child( node, act ); 163 164 /* Just make sure we do this only once. */ 165 bud->flags |= JBFLAG_PROBED_JEP85; 166 } 167 168 st = jabber_write_packet( gc, node ); 169 xt_free_node( node ); 170 171 return st; 172 } 173 174 static GList *jabber_away_states( struct gaim_connection *gc ) 175 { 176 static GList *l = NULL; 177 int i; 178 179 if( l == NULL ) 180 for( i = 0; jabber_away_state_list[i].full_name; i ++ ) 181 l = g_list_append( l, (void*) jabber_away_state_list[i].full_name ); 182 183 return l; 184 } 185 186 static void jabber_get_info( struct gaim_connection *gc, char *who ) 187 { 188 struct jabber_data *jd = gc->proto_data; 189 struct jabber_buddy *bud; 190 191 if( strchr( who, '/' ) ) 192 bud = jabber_buddy_by_jid( gc, who ); 193 else 194 bud = g_hash_table_lookup( jd->buddies, who ); 195 196 while( bud ) 197 { 198 serv_got_crap( gc, "Buddy %s/%s (%d) information:\nAway state: %s\nAway message: %s", 199 bud->handle, bud->resource, bud->priority, 200 bud->away_state ? bud->away_state->full_name : "(none)", 201 bud->away_message ? : "(none)" ); 202 bud = bud->next; 203 } 204 } 205 206 static void jabber_set_away( struct gaim_connection *gc, char *state_txt, char *message ) 207 { 208 struct jabber_data *jd = gc->proto_data; 209 struct jabber_away_state *state; 210 211 /* Save all this info. We need it, for example, when changing the priority setting. */ 212 state = (void *) jabber_away_state_by_name( state_txt ); 213 jd->away_state = state ? state : (void *) jabber_away_state_list; /* Fall back to "Away" if necessary. */ 214 g_free( jd->away_message ); 215 jd->away_message = ( message && *message ) ? g_strdup( message ) : NULL; 216 217 presence_send_update( gc ); 218 } 219 220 static void jabber_add_buddy( struct gaim_connection *gc, char *who ) 221 { 222 if( jabber_add_to_roster( gc, who, NULL ) ) 223 presence_send_request( gc, who, "subscribe" ); 224 } 225 226 static void jabber_remove_buddy( struct gaim_connection *gc, char *who, char *group ) 227 { 228 if( jabber_remove_from_roster( gc, who ) ) 229 presence_send_request( gc, who, "unsubscribe" ); 230 } 231 232 static void jabber_keepalive( struct gaim_connection *gc ) 233 { 234 /* Just any whitespace character is enough as a keepalive for XMPP sessions. */ 235 jabber_write( gc, "\n", 1 ); 236 237 /* This runs the garbage collection every minute, which means every packet 238 is in the cache for about a minute (which should be enough AFAIK). */ 239 jabber_cache_clean( gc ); 240 } 241 242 static int jabber_send_typing( struct gaim_connection *gc, char *who, int typing ) 243 { 244 struct jabber_data *jd = gc->proto_data; 245 struct jabber_buddy *bud; 246 247 /* Enable typing notification related code from now. */ 248 jd->flags |= JFLAG_WANT_TYPING; 249 250 bud = jabber_buddy_by_jid( gc, who ); 251 if( bud->flags & JBFLAG_DOES_JEP85 ) 252 { 253 /* We're only allowed to send this stuff if we know the other 254 side supports it. */ 255 256 struct xt_node *node; 257 char *type; 258 int st; 259 260 if( typing == 0 ) 261 type = "active"; 262 else if( typing == 2 ) 263 type = "paused"; 264 else /* if( typing == 1 ) */ 265 type = "composing"; 266 267 node = xt_new_node( type, NULL, NULL ); 268 xt_add_attr( node, "xmlns", "http://jabber.org/protocol/chatstates" ); 269 node = jabber_make_packet( "message", "chat", bud->full_jid, node ); 270 271 st = jabber_write_packet( gc, node ); 272 xt_free_node( node ); 273 274 return st; 275 } 276 1669 277 return 1; 1670 278 } 1671 279 1672 /*1673 * Add/update buddy's roster entry on server1674 */1675 static void jabber_roster_update(struct gaim_connection *gc, char *name)1676 {1677 xmlnode x, y;1678 char *realwho;1679 gjconn gjc;1680 struct buddy *buddy = NULL;1681 /* struct group *buddy_group = NULL; */1682 1683 if(gc && gc->proto_data && ((struct jabber_data *)gc->proto_data)->gjc && name) {1684 gjc = ((struct jabber_data *)gc->proto_data)->gjc;1685 1686 if (!strchr(name, '@'))1687 realwho = g_strdup_printf("%s@%s", name, gjc->user->server);1688 else {1689 jid who = jid_new(gjc->p, name);1690 if (who->user == NULL) {1691 /* FIXME: transport */1692 return;1693 }1694 realwho = g_strdup_printf("%s@%s", who->user, who->server);1695 }1696 1697 1698 x = jutil_iqnew(JPACKET__SET, NS_ROSTER);1699 y = xmlnode_insert_tag(xmlnode_get_tag(x, "query"), "item");1700 xmlnode_put_attrib(y, "jid", realwho);1701 1702 1703 /* If we can find the buddy, there's an alias for him, it's not 0-length1704 * and it doesn't match his JID, add the "name" attribute.1705 */1706 if((buddy = find_buddy(gc, realwho)) != NULL &&1707 buddy->show != NULL && buddy->show[0] != '\0' && strcmp(realwho, buddy->show)) {1708 1709 xmlnode_put_attrib(y, "name", buddy->show);1710 }1711 1712 /*1713 * Find out what group the buddy's in and send that along1714 * with the roster item.1715 */1716 /* ** Bitlbee disabled **1717 if((buddy_group = NULL) != NULL) {1718 xmlnode z;1719 z = xmlnode_insert_tag(y, "group");1720 xmlnode_insert_cdata(z, buddy_group->name, -1);1721 }1722 ** End - Bitlbee ** */1723 1724 gjab_send(((struct jabber_data *)gc->proto_data)->gjc, x);1725 1726 xmlnode_free(x);1727 g_free(realwho);1728 }1729 }1730 1731 /*1732 * Change buddy's group on server roster1733 */1734 static void jabber_group_change(struct gaim_connection *gc, char *name, char *old_group, char *new_group)1735 {1736 if(strcmp(old_group, new_group)) {1737 jabber_roster_update(gc, name);1738 }1739 }1740 1741 static void jabber_add_buddy(struct gaim_connection *gc, char *name)1742 {1743 xmlnode x;1744 char *realwho;1745 gjconn gjc = ((struct jabber_data *)gc->proto_data)->gjc;1746 1747 if (!((struct jabber_data *)gc->proto_data)->did_import)1748 return;1749 1750 if (!name)1751 return;1752 1753 if (!strcmp(gc->username, name))1754 return;1755 1756 if (!strchr(name, '@'))1757 realwho = g_strdup_printf("%s@%s", name, gjc->user->server);1758 else {1759 jid who;1760 1761 if((who = jid_new(gjc->p, name)) == NULL) {1762 char *msg = g_strdup_printf("%s: \"%s\"", _("Invalid Jabber I.D."), name);1763 do_error_dialog(GJ_GC(gjc), msg, _("Jabber Error"));1764 g_free(msg);1765 jabber_remove_gaim_buddy(gc, name);1766 return;1767 }1768 if (who->user == NULL) {1769 /* FIXME: transport */1770 return;1771 }1772 realwho = g_strdup_printf("%s@%s", who->user, who->server);1773 }1774 1775 x = xmlnode_new_tag("presence");1776 xmlnode_put_attrib(x, "to", realwho);1777 xmlnode_put_attrib(x, "type", "subscribe");1778 gjab_send(((struct jabber_data *)gc->proto_data)->gjc, x);1779 xmlnode_free(x);1780 1781 jabber_roster_update(gc, realwho);1782 1783 g_free(realwho);1784 }1785 1786 static void jabber_remove_buddy(struct gaim_connection *gc, char *name, char *group)1787 {1788 xmlnode x;1789 char *realwho;1790 gjconn gjc = ((struct jabber_data *)gc->proto_data)->gjc;1791 1792 if (!name)1793 return;1794 1795 if (!strchr(name, '@'))1796 realwho = g_strdup_printf("%s@%s", name, gjc->user->server);1797 else1798 realwho = g_strdup(name);1799 1800 x = xmlnode_new_tag("presence");1801 xmlnode_put_attrib(x, "to", realwho);1802 xmlnode_put_attrib(x, "type", "unsubscribe");1803 gjab_send(((struct jabber_data *)gc->proto_data)->gjc, x);1804 g_free(realwho);1805 xmlnode_free(x);1806 }1807 1808 static void jabber_get_info(struct gaim_connection *gc, char *who) {1809 xmlnode x;1810 char *id;1811 char *realwho;1812 struct jabber_data *jd = gc->proto_data;1813 gjconn gjc = jd->gjc;1814 1815 x = jutil_iqnew(JPACKET__GET, NS_VCARD);1816 /* Bare username? */1817 if (!strchr(who, '@')) {1818 realwho = g_strdup_printf("%s@%s", who, gjc->user->server);1819 } else {1820 realwho = g_strdup(who);1821 }1822 xmlnode_put_attrib(x, "to", realwho);1823 g_free(realwho);1824 1825 id = gjab_getid(gjc);1826 xmlnode_put_attrib(x, "id", id);1827 1828 g_hash_table_insert(jd->gjc->queries, g_strdup(id), g_strdup("vCard"));1829 1830 gjab_send(gjc, x);1831 1832 xmlnode_free(x);1833 1834 }1835 1836 static void jabber_get_away_msg(struct gaim_connection *gc, char *who) {1837 struct jabber_data *jd = gc->proto_data;1838 gjconn gjc = jd->gjc;1839 char *status;1840 1841 /* space for all elements: Jabber I.D. + "status" + NULL (list terminator) */1842 gchar **str_arr = (gchar **) g_new(gpointer, 3);1843 gchar **ap = str_arr;1844 gchar *realwho, *final;1845 1846 /* Bare username? */1847 if (!strchr(who, '@')) {1848 realwho = g_strdup_printf("%s@%s", who, gjc->user->server);1849 } else {1850 realwho = g_strdup(who);1851 }1852 *ap++ = g_strdup_printf("<B>Jabber ID:</B> %s<BR>\n", realwho);1853 1854 if((status = g_hash_table_lookup(jd->hash, realwho)) == NULL) {1855 status = _("Unknown");1856 }1857 *ap++ = g_strdup_printf("<B>Status:</B> %s<BR>\n", status);1858 1859 *ap = NULL;1860 1861 final= g_strjoinv(NULL, str_arr);1862 g_strfreev(str_arr);1863 1864 g_free(realwho);1865 g_free(final);1866 1867 }1868 1869 static GList *jabber_away_states(struct gaim_connection *gc) {1870 GList *m = NULL;1871 1872 m = g_list_append(m, "Online");1873 m = g_list_append(m, "Chatty");1874 m = g_list_append(m, "Away");1875 m = g_list_append(m, "Extended Away");1876 m = g_list_append(m, "Do Not Disturb");1877 1878 return m;1879 }1880 1881 static void jabber_set_away(struct gaim_connection *gc, char *state, char *message)1882 {1883 xmlnode x, y;1884 struct jabber_data *jd = gc->proto_data;1885 gjconn gjc = jd->gjc;1886 1887 gc->away = NULL; /* never send an auto-response */1888 1889 x = xmlnode_new_tag("presence");1890 1891 if (!strcmp(state, GAIM_AWAY_CUSTOM)) {1892 /* oh goody. Gaim is telling us what to do. */1893 if (message) {1894 /* Gaim wants us to be away */1895 y = xmlnode_insert_tag(x, "show");1896 xmlnode_insert_cdata(y, "away", -1);1897 y = xmlnode_insert_tag(x, "status");1898 xmlnode_insert_cdata(y, message, -1);1899 gc->away = "";1900 } else {1901 /* Gaim wants us to not be away */1902 /* but for Jabber, we can just send presence with no other information. */1903 }1904 } else {1905 /* state is one of our own strings. it won't be NULL. */1906 if (!g_strcasecmp(state, "Online")) {1907 /* once again, we don't have to put anything here */1908 } else if (!g_strcasecmp(state, "Chatty")) {1909 y = xmlnode_insert_tag(x, "show");1910 xmlnode_insert_cdata(y, "chat", -1);1911 } else if (!g_strcasecmp(state, "Away")) {1912 y = xmlnode_insert_tag(x, "show");1913 xmlnode_insert_cdata(y, "away", -1);1914 gc->away = "";1915 } else if (!g_strcasecmp(state, "Extended Away")) {1916 y = xmlnode_insert_tag(x, "show");1917 xmlnode_insert_cdata(y, "xa", -1);1918 gc->away = "";1919 } else if (!g_strcasecmp(state, "Do Not Disturb")) {1920 y = xmlnode_insert_tag(x, "show");1921 xmlnode_insert_cdata(y, "dnd", -1);1922 gc->away = "";1923 }1924 }1925 1926 gjab_send(gjc, x);1927 xmlnode_free(x);1928 }1929 1930 static void jabber_keepalive(struct gaim_connection *gc) {1931 struct jabber_data *jd = (struct jabber_data *)gc->proto_data;1932 gjab_send_raw(jd->gjc, " \t ");1933 }1934 1935 /*---------------------------------------*/1936 /* Jabber "set info" (vCard) support */1937 /*---------------------------------------*/1938 1939 /*1940 * V-Card format:1941 *1942 * <vCard prodid='' version='' xmlns=''>1943 * <FN></FN>1944 * <N>1945 * <FAMILY/>1946 * <GIVEN/>1947 * </N>1948 * <NICKNAME/>1949 * <URL/>1950 * <ADR>1951 * <STREET/>1952 * <EXTADD/>1953 * <LOCALITY/>1954 * <REGION/>1955 * <PCODE/>1956 * <COUNTRY/>1957 * </ADR>1958 * <TEL/>1959 * <EMAIL/>1960 * <ORG>1961 * <ORGNAME/>1962 * <ORGUNIT/>1963 * </ORG>1964 * <TITLE/>1965 * <ROLE/>1966 * <DESC/>1967 * <BDAY/>1968 * </vCard>1969 *1970 * See also:1971 *1972 * http://docs.jabber.org/proto/html/vcard-temp.html1973 * http://www.vcard-xml.org/dtd/vCard-XML-v2-20010520.dtd1974 */1975 1976 /*1977 * Cross-reference user-friendly V-Card entry labels to vCard XML tags1978 * and attributes.1979 *1980 * Order is (or should be) unimportant. For example: we have no way of1981 * knowing in what order real data will arrive.1982 *1983 * Format: Label, Pre-set text, "visible" flag, "editable" flag, XML tag1984 * name, XML tag's parent tag "path" (relative to vCard node).1985 *1986 * List is terminated by a NULL label pointer.1987 *1988 * Entries with no label text, but with XML tag and parent tag1989 * entries, are used by V-Card XML construction routines to1990 * "automagically" construct the appropriate XML node tree.1991 *1992 * Thoughts on future direction/expansion1993 *1994 * This is a "simple" vCard.1995 *1996 * It is possible for nodes other than the "vCard" node to have1997 * attributes. Should that prove necessary/desirable, add an1998 * "attributes" pointer to the vcard_template struct, create the1999 * necessary tag_attr structs, and add 'em to the vcard_dflt_data2000 * array.2001 *2002 * The above changes will (obviously) require changes to the vCard2003 * construction routines.2004 */2005 2006 static struct vcard_template {2007 char *label; /* label text pointer */2008 char *text; /* entry text pointer */2009 int visible; /* should entry field be "visible?" */2010 int editable; /* should entry field be editable? */2011 char *tag; /* tag text */2012 char *ptag; /* parent tag "path" text */2013 char *url; /* vCard display format if URL */2014 } vcard_template_data[] = {2015 {N_("Full Name"), NULL, TRUE, TRUE, "FN", NULL, NULL},2016 {N_("Family Name"), NULL, TRUE, TRUE, "FAMILY", "N", NULL},2017 {N_("Given Name"), NULL, TRUE, TRUE, "GIVEN", "N", NULL},2018 {N_("Nickname"), NULL, TRUE, TRUE, "NICKNAME", NULL, NULL},2019 {N_("URL"), NULL, TRUE, TRUE, "URL", NULL, "<A HREF=\"%s\">%s</A>"},2020 {N_("Street Address"), NULL, TRUE, TRUE, "STREET", "ADR", NULL},2021 {N_("Extended Address"), NULL, TRUE, TRUE, "EXTADD", "ADR", NULL},2022 {N_("Locality"), NULL, TRUE, TRUE, "LOCALITY", "ADR", NULL},2023 {N_("Region"), NULL, TRUE, TRUE, "REGION", "ADR", NULL},2024 {N_("Postal Code"), NULL, TRUE, TRUE, "PCODE", "ADR", NULL},2025 {N_("Country"), NULL, TRUE, TRUE, "COUNTRY", "ADR", NULL},2026 {N_("Telephone"), NULL, TRUE, TRUE, "TELEPHONE", NULL, NULL},2027 {N_("Email"), NULL, TRUE, TRUE, "EMAIL", NULL, "<A HREF=\"mailto:%s\">%s</A>"},2028 {N_("Organization Name"), NULL, TRUE, TRUE, "ORGNAME", "ORG", NULL},2029 {N_("Organization Unit"), NULL, TRUE, TRUE, "ORGUNIT", "ORG", NULL},2030 {N_("Title"), NULL, TRUE, TRUE, "TITLE", NULL, NULL},2031 {N_("Role"), NULL, TRUE, TRUE, "ROLE", NULL, NULL},2032 {N_("Birthday"), NULL, TRUE, TRUE, "BDAY", NULL, NULL},2033 {N_("Description"), NULL, TRUE, TRUE, "DESC", NULL, NULL},2034 {"", NULL, TRUE, TRUE, "N", NULL, NULL},2035 {"", NULL, TRUE, TRUE, "ADR", NULL, NULL},2036 {"", NULL, TRUE, TRUE, "ORG", NULL, NULL},2037 {NULL, NULL, 0, 0, NULL, NULL, NULL}2038 };2039 2040 /*2041 * Used by routines to parse an XML-encoded string into an xmlnode tree2042 */2043 typedef struct {2044 XML_Parser parser;2045 xmlnode current;2046 } *xmlstr2xmlnode_parser, xmlstr2xmlnode_parser_struct;2047 2048 2049 /*2050 * Used by XML_Parse on parsing CDATA2051 */2052 static void xmlstr2xmlnode_charData(void *userdata, const char *s, int slen)2053 {2054 xmlstr2xmlnode_parser xmlp = (xmlstr2xmlnode_parser) userdata;2055 2056 if (xmlp->current)2057 xmlnode_insert_cdata(xmlp->current, s, slen);2058 }2059 2060 /*2061 * Used by XML_Parse to start or append to an xmlnode2062 */2063 static void xmlstr2xmlnode_startElement(void *userdata, const char *name, const char **attribs)2064 {2065 xmlnode x;2066 xmlstr2xmlnode_parser xmlp = (xmlstr2xmlnode_parser) userdata;2067 2068 if (xmlp->current) {2069 /* Append the node to the current one */2070 x = xmlnode_insert_tag(xmlp->current, name);2071 xmlnode_put_expat_attribs(x, attribs);2072 2073 xmlp->current = x;2074 } else {2075 x = xmlnode_new_tag(name);2076 xmlnode_put_expat_attribs(x, attribs);2077 xmlp->current = x;2078 }2079 }2080 2081 /*2082 * Used by XML_Parse to end an xmlnode2083 */2084 static void xmlstr2xmlnode_endElement(void *userdata, const char *name)2085 {2086 xmlstr2xmlnode_parser xmlp = (xmlstr2xmlnode_parser) userdata;2087 xmlnode x;2088 2089 if (xmlp->current != NULL && (x = xmlnode_get_parent(xmlp->current)) != NULL) {2090 xmlp->current = x;2091 }2092 }2093 2094 /*2095 * Parse an XML-encoded string into an xmlnode tree2096 *2097 * Caller is responsible for freeing the returned xmlnode2098 */2099 static xmlnode xmlstr2xmlnode(char *xmlstring)2100 {2101 xmlstr2xmlnode_parser my_parser = g_new(xmlstr2xmlnode_parser_struct, 1);2102 xmlnode x = NULL;2103 2104 my_parser->parser = XML_ParserCreate(NULL);2105 my_parser->current = NULL;2106 2107 XML_SetUserData(my_parser->parser, (void *)my_parser);2108 XML_SetElementHandler(my_parser->parser, xmlstr2xmlnode_startElement, xmlstr2xmlnode_endElement);2109 XML_SetCharacterDataHandler(my_parser->parser, xmlstr2xmlnode_charData);2110 XML_Parse(my_parser->parser, xmlstring, strlen(xmlstring), 0);2111 2112 x = my_parser->current;2113 2114 XML_ParserFree(my_parser->parser);2115 g_free(my_parser);2116 2117 return(x);2118 }2119 2120 /*2121 * Insert a tag node into an xmlnode tree, recursively inserting parent tag2122 * nodes as necessary2123 *2124 * Returns pointer to inserted node2125 *2126 * Note to hackers: this code is designed to be re-entrant (it's recursive--it2127 * calls itself), so don't put any "static"s in here!2128 */2129 static xmlnode insert_tag_to_parent_tag(xmlnode start, const char *parent_tag, const char *new_tag)2130 {2131 xmlnode x = NULL;2132 2133 /*2134 * If the parent tag wasn't specified, see if we can get it2135 * from the vCard template struct.2136 */2137 if(parent_tag == NULL) {2138 struct vcard_template *vc_tp = vcard_template_data;2139 2140 while(vc_tp->label != NULL) {2141 if(strcmp(vc_tp->tag, new_tag) == 0) {2142 parent_tag = vc_tp->ptag;2143 break;2144 }2145 ++vc_tp;2146 }2147 }2148 2149 /*2150 * If we have a parent tag...2151 */2152 if(parent_tag != NULL ) {2153 /*2154 * Try to get the parent node for a tag2155 */2156 if((x = xmlnode_get_tag(start, parent_tag)) == NULL) {2157 /*2158 * Descend?2159 */2160 char *grand_parent = strcpy(g_malloc(strlen(parent_tag) + 1), parent_tag);2161 char *parent;2162 2163 if((parent = strrchr(grand_parent, '/')) != NULL) {2164 *(parent++) = '\0';2165 x = insert_tag_to_parent_tag(start, grand_parent, parent);2166 } else {2167 x = xmlnode_insert_tag(start, grand_parent);2168 }2169 g_free(grand_parent);2170 } else {2171 /*2172 * We found *something* to be the parent node.2173 * Note: may be the "root" node!2174 */2175 xmlnode y;2176 if((y = xmlnode_get_tag(x, new_tag)) != NULL) {2177 return(y);2178 }2179 }2180 }2181 2182 /*2183 * insert the new tag into its parent node2184 */2185 return(xmlnode_insert_tag((x == NULL? start : x), new_tag));2186 }2187 2188 /*2189 * Send vCard info to Jabber server2190 */2191 static void jabber_set_info(struct gaim_connection *gc, char *info)2192 {2193 xmlnode x, vc_node;2194 char *id;2195 struct jabber_data *jd = gc->proto_data;2196 gjconn gjc = jd->gjc;2197 2198 x = xmlnode_new_tag("iq");2199 xmlnode_put_attrib(x,"type","set");2200 2201 id = gjab_getid(gjc);2202 2203 xmlnode_put_attrib(x, "id", id);2204 2205 /*2206 * Send only if there's actually any *information* to send2207 */2208 if((vc_node = xmlstr2xmlnode(info)) != NULL && xmlnode_get_name(vc_node) != NULL &&2209 g_strncasecmp(xmlnode_get_name(vc_node), "vcard", 5) == 0) {2210 xmlnode_insert_tag_node(x, vc_node);2211 gjab_send(gjc, x);2212 }2213 2214 xmlnode_free(x);2215 }2216 2217 /*2218 * displays a Jabber vCard2219 */2220 static void jabber_handlevcard(gjconn gjc, xmlnode querynode, char *from)2221 {2222 struct jabber_data *jd = GJ_GC(gjc)->proto_data;2223 jid who = jid_new(gjc->p, from);2224 char *status = NULL, *text = NULL;2225 GString *str = g_string_sized_new(100);2226 xmlnode child;2227 2228 gchar *buddy = NULL;2229 2230 if(querynode == NULL) {2231 serv_got_crap(GJ_GC(gjc), "%s - Received empty info reply from %s", _("User Info"), from);2232 return;2233 }2234 2235 if(who->resource != NULL && (who->resource)[0] != '\0') {2236 buddy = g_strdup_printf("%s@%s/%s", who->user, who->server, who->resource);2237 } else {2238 buddy = g_strdup_printf("%s@%s", who->user, who->server);2239 }2240 2241 if((status = g_hash_table_lookup(jd->hash, buddy)) == NULL) {2242 status = _("Unknown");2243 }2244 2245 g_string_sprintfa(str, "%s: %s - %s: %s", _("Jabber ID"), buddy, _("Status"),2246 status);2247 2248 for(child = querynode->firstchild; child; child = child->next)2249 {2250 xmlnode child2;2251 2252 if(child->type != NTYPE_TAG)2253 continue;2254 2255 text = xmlnode_get_data(child);2256 if(text && !strcmp(child->name, "FN")) {2257 info_string_append(str, "\n", _("Full Name"), text);2258 } else if (!strcmp(child->name, "N")) {2259 for (child2 = child->firstchild; child2; child2 = child2->next) {2260 char *text2 = NULL;2261 2262 if (child2->type != NTYPE_TAG)2263 continue;2264 2265 text2 = xmlnode_get_data(child2);2266 if (text2 && !strcmp(child2->name, "FAMILY")) {2267 info_string_append(str, "\n", _("Family Name"), text2);2268 } else if (text2 && !strcmp(child2->name, "GIVEN")) {2269 info_string_append(str, "\n", _("Given Name"), text2);2270 } else if (text2 && !strcmp(child2->name, "MIDDLE")) {2271 info_string_append(str, "\n", _("Middle Name"), text2);2272 }2273 }2274 } else if (text && !strcmp(child->name, "NICKNAME")) {2275 info_string_append(str, "\n", _("Nickname"), text);2276 } else if (text && !strcmp(child->name, "BDAY")) {2277 info_string_append(str, "\n", _("Birthday"), text);2278 } else if (!strcmp(child->name, "ADR")) {2279 /* show wich address it is */2280 /* Just for the beauty of bitlbee2281 if (child->firstchild)2282 g_string_sprintfa(str, "%s:\n", _("Address"));2283 */2284 for(child2 = child->firstchild; child2; child2 = child2->next) {2285 char *text2 = NULL;2286 2287 if(child2->type != NTYPE_TAG)2288 continue;2289 2290 text2 = xmlnode_get_data(child2);2291 if(text2 && !strcmp(child2->name, "POBOX")) {2292 info_string_append(str, "\n",2293 _("P.O. Box"), text2);2294 } else if(text2 && !strcmp(child2->name, "EXTADR")) {2295 info_string_append(str, "\n",2296 _("Extended Address"), text2);2297 } else if(text2 && !strcmp(child2->name, "STREET")) {2298 info_string_append(str, "\n",2299 _("Street Address"), text2);2300 } else if(text2 && !strcmp(child2->name, "LOCALITY")) {2301 info_string_append(str, "\n",2302 _("Locality"), text2);2303 } else if(text2 && !strcmp(child2->name, "REGION")) {2304 info_string_append(str, "\n",2305 _("Region"), text2);2306 } else if(text2 && !strcmp(child2->name, "PCODE")) {2307 info_string_append(str, "\n",2308 _("Postal Code"), text2);2309 } else if(text2 && (!strcmp(child2->name, "CTRY")2310 || !strcmp(child2->name, "COUNTRY"))) {2311 info_string_append(str, "\n", _("Country"), text2);2312 }2313 }2314 } else if(!strcmp(child->name, "TEL")) {2315 char *number = NULL;2316 if ((child2 = xmlnode_get_tag(child, "NUMBER"))) {2317 /* show what kind of number it is */2318 number = xmlnode_get_data(child2);2319 if(number) {2320 info_string_append(str, "\n", _("Telephone"), number);2321 }2322 } else if((number = xmlnode_get_data(child))) {2323 /* lots of clients (including gaim) do this,2324 * but it's out of spec */2325 info_string_append(str, "\n", _("Telephone"), number);2326 }2327 } else if(!strcmp(child->name, "EMAIL")) {2328 char *userid = NULL;2329 if((child2 = xmlnode_get_tag(child, "USERID"))) {2330 /* show what kind of email it is */2331 userid = xmlnode_get_data(child2);2332 if(userid) {2333 info_string_append(str, "\n", _("Email"), userid);2334 }2335 } else if((userid = xmlnode_get_data(child))) {2336 /* lots of clients (including gaim) do this,2337 * but it's out of spec */2338 info_string_append(str, "\n", _("Email"), userid);2339 }2340 } else if(!strcmp(child->name, "ORG")) {2341 for(child2 = child->firstchild; child2; child2 = child2->next) {2342 char *text2 = NULL;2343 2344 if(child2->type != NTYPE_TAG)2345 continue;2346 2347 text2 = xmlnode_get_data(child2);2348 if(text2 && !strcmp(child2->name, "ORGNAME")) {2349 info_string_append(str, "\n", _("Organization Name"), text2);2350 } else if(text2 && !strcmp(child2->name, "ORGUNIT")) {2351 info_string_append(str, "\n", _("Organization Unit"), text2);2352 }2353 }2354 } else if(text && !strcmp(child->name, "TITLE")) {2355 info_string_append(str, "\n", _("Title"), text);2356 } else if(text && !strcmp(child->name, "ROLE")) {2357 info_string_append(str, "\n", _("Role"), text);2358 } else if(text && !strcmp(child->name, "DESC")) {2359 g_string_sprintfa(str, "\n%s:\n%s\n%s", _("Description"),2360 text, _("End of Description"));2361 }2362 }2363 2364 serv_got_crap(GJ_GC(gjc), "%s\n%s", _("User Info"), str->str);2365 2366 g_free(buddy);2367 g_string_free(str, TRUE);2368 }2369 2370 280 void jabber_init() 2371 281 { 2372 struct prpl *ret = g_new0( struct prpl, 1);2373 282 struct prpl *ret = g_new0( struct prpl, 1 ); 283 2374 284 ret->name = "jabber"; 2375 ret-> away_states = jabber_away_states;285 ret->login = jabber_login; 2376 286 ret->acc_init = jabber_acc_init; 2377 ret->login = jabber_login;2378 287 ret->close = jabber_close; 2379 288 ret->send_im = jabber_send_im; 2380 ret->set_info = jabber_set_info; 289 ret->away_states = jabber_away_states; 290 // ret->get_status_string = jabber_get_status_string; 291 ret->set_away = jabber_set_away; 292 // ret->set_info = jabber_set_info; 2381 293 ret->get_info = jabber_get_info; 2382 ret->set_away = jabber_set_away;2383 ret->get_away = jabber_get_away_msg;2384 294 ret->add_buddy = jabber_add_buddy; 2385 295 ret->remove_buddy = jabber_remove_buddy; 296 // ret->chat_send = jabber_chat_send; 297 // ret->chat_invite = jabber_chat_invite; 298 // ret->chat_leave = jabber_chat_leave; 299 // ret->chat_open = jabber_chat_open; 2386 300 ret->keepalive = jabber_keepalive; 2387 ret->alias_buddy = jabber_roster_update; 2388 ret->group_buddy = jabber_group_change; 301 ret->send_typing = jabber_send_typing; 2389 302 ret->handle_cmp = g_strcasecmp; 2390 303 2391 register_protocol (ret);2392 } 304 register_protocol( ret ); 305 } -
protocols/jabber/jabber.h
r3dc9d46 re617b35 1 /* 2 * This program is free software; you can redistribute it and/or modify 3 * it under the terms of the GNU General Public License as published by 4 * the Free Software Foundation; either version 2 of the License, or 5 * (at your option) any later version. 6 * 7 * This program is distributed in the hope that it will be useful, 8 * but WITHOUT ANY WARRANTY; without even the implied warranty of 9 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the 10 * GNU General Public License for more details. 11 * 12 * You should have received a copy of the GNU General Public License 13 * along with this program; if not, write to the Free Software 14 * Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. 15 * 16 * Jabber 17 * Copyright (C) 1998-1999 The Jabber Team http://jabber.org/ 18 */ 1 /***************************************************************************\ 2 * * 3 * BitlBee - An IRC to IM gateway * 4 * Jabber module - Main file * 5 * * 6 * Copyright 2006 Wilmer van der Gaast <wilmer@gaast.net> * 7 * * 8 * This program is free software; you can redistribute it and/or modify * 9 * it under the terms of the GNU General Public License as published by * 10 * the Free Software Foundation; either version 2 of the License, or * 11 * (at your option) any later version. * 12 * * 13 * This program is distributed in the hope that it will be useful, * 14 * but WITHOUT ANY WARRANTY; without even the implied warranty of * 15 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * 16 * GNU General Public License for more details. * 17 * * 18 * You should have received a copy of the GNU General Public License along * 19 * with this program; if not, write to the Free Software Foundation, Inc., * 20 * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA. * 21 * * 22 \***************************************************************************/ 19 23 20 #include <string.h> 21 #include <stdlib.h> 22 #include <sys/types.h> 23 #include <stdio.h> 24 #include <setjmp.h> 25 #include <sys/stat.h> 26 #include <fcntl.h> 27 #include <errno.h> 28 #include <signal.h> 29 #include <stdarg.h> 30 #include <time.h> 31 #include <ctype.h> 32 #ifdef _WIN32 33 #undef DATADIR 34 #include "sock.h" 24 #ifndef _JABBER_H 25 #define _JABBER_H 26 27 #include <glib.h> 28 29 #include "xmltree.h" 30 #include "bitlbee.h" 31 32 typedef enum 33 { 34 JFLAG_STREAM_STARTED = 1, /* Set when we detected the beginning of the stream 35 and want to do auth. */ 36 JFLAG_AUTHENTICATED = 2, /* Set when we're successfully authenticatd. */ 37 JFLAG_STREAM_RESTART = 4, /* Set when we want to restart the stream (after 38 SASL or TLS). */ 39 JFLAG_WAIT_SESSION = 8, /* Set if we sent a <session> tag and need a reply 40 before we continue. */ 41 JFLAG_WAIT_BIND = 16, /* ... for <bind> tag. */ 42 JFLAG_WANT_TYPING = 32, /* Set if we ever sent a typing notification, this 43 activates all JEP-85 related code. */ 44 } jabber_flags_t; 45 46 typedef enum 47 { 48 JBFLAG_PROBED_JEP85 = 1, /* Set this when we sent our probe packet to make 49 sure it gets sent only once. */ 50 JBFLAG_DOES_JEP85 = 2, /* Set this when the resource seems to support 51 JEP85 (typing notification shite). */ 52 } jabber_buddy_flag_t; 53 54 struct jabber_data 55 { 56 struct gaim_connection *gc; 57 58 int fd; 59 void *ssl; 60 char *txq; 61 int tx_len; 62 int r_inpa, w_inpa; 63 64 struct xt_parser *xt; 65 jabber_flags_t flags; 66 67 char *username; /* USERNAME@server */ 68 char *server; /* username@SERVER -=> server/domain, not hostname */ 69 70 /* After changing one of these two (or the priority setting), call 71 presence_send_update() to inform the server about the changes. */ 72 struct jabber_away_state *away_state; 73 char *away_message; 74 75 GHashTable *node_cache; 76 GHashTable *buddies; 77 }; 78 79 struct jabber_away_state 80 { 81 char code[5]; 82 char *full_name; 83 }; 84 85 typedef xt_status (*jabber_cache_event) ( struct gaim_connection *gc, struct xt_node *node, struct xt_node *orig ); 86 87 struct jabber_cache_entry 88 { 89 struct xt_node *node; 90 jabber_cache_event func; 91 }; 92 93 struct jabber_buddy 94 { 95 char *handle; 96 char *full_jid; 97 char *resource; 98 99 int priority; 100 struct jabber_away_state *away_state; 101 char *away_message; 102 103 time_t last_act; 104 int flags; 105 106 struct jabber_buddy *next; 107 }; 108 109 /* iq.c */ 110 xt_status jabber_pkt_iq( struct xt_node *node, gpointer data ); 111 int jabber_init_iq_auth( struct gaim_connection *gc ); 112 xt_status jabber_pkt_bind_sess( struct gaim_connection *gc, struct xt_node *node, struct xt_node *orig ); 113 int jabber_get_roster( struct gaim_connection *gc ); 114 int jabber_add_to_roster( struct gaim_connection *gc, char *handle, char *name ); 115 int jabber_remove_from_roster( struct gaim_connection *gc, char *handle ); 116 117 /* message.c */ 118 xt_status jabber_pkt_message( struct xt_node *node, gpointer data ); 119 120 /* presence.c */ 121 xt_status jabber_pkt_presence( struct xt_node *node, gpointer data ); 122 int presence_send_update( struct gaim_connection *gc ); 123 int presence_send_request( struct gaim_connection *gc, char *handle, char *request ); 124 125 /* jabber_util.c */ 126 char *set_eval_priority( set_t *set, char *value ); 127 char *set_eval_tls( set_t *set, char *value ); 128 struct xt_node *jabber_make_packet( char *name, char *type, char *to, struct xt_node *children ); 129 struct xt_node *jabber_make_error_packet( struct xt_node *orig, char *err_cond, char *err_type ); 130 void jabber_cache_add( struct gaim_connection *gc, struct xt_node *node, jabber_cache_event func ); 131 struct xt_node *jabber_cache_get( struct gaim_connection *gc, char *id ); 132 void jabber_cache_entry_free( gpointer entry ); 133 void jabber_cache_clean( struct gaim_connection *gc ); 134 const struct jabber_away_state *jabber_away_state_by_code( char *code ); 135 const struct jabber_away_state *jabber_away_state_by_name( char *name ); 136 void jabber_buddy_ask( struct gaim_connection *gc, char *handle ); 137 struct jabber_buddy *jabber_buddy_add( struct gaim_connection *gc, char *full_jid ); 138 struct jabber_buddy *jabber_buddy_by_jid( struct gaim_connection *gc, char *jid ); 139 int jabber_buddy_remove( struct gaim_connection *gc, char *full_jid ); 140 141 extern const struct jabber_away_state jabber_away_state_list[]; 142 143 /* io.c */ 144 int jabber_write_packet( struct gaim_connection *gc, struct xt_node *node ); 145 int jabber_write( struct gaim_connection *gc, char *buf, int len ); 146 gboolean jabber_connected_plain( gpointer data, gint source, b_input_condition cond ); 147 gboolean jabber_connected_ssl( gpointer data, void *source, b_input_condition cond ); 148 gboolean jabber_start_stream( struct gaim_connection *gc ); 149 void jabber_end_stream( struct gaim_connection *gc ); 150 151 /* sasl.c */ 152 xt_status sasl_pkt_mechanisms( struct xt_node *node, gpointer data ); 153 xt_status sasl_pkt_challenge( struct xt_node *node, gpointer data ); 154 xt_status sasl_pkt_result( struct xt_node *node, gpointer data ); 155 gboolean sasl_supported( struct gaim_connection *gc ); 156 35 157 #endif 36 37 #include "lib.h"38 39 40 #ifndef INCL_JABBER_H41 #define INCL_JABBER_H42 43 #ifdef __cplusplus44 extern "C" {45 #endif46 47 /* --------------------------------------------------------- */48 /* */49 /* JID structures & constants */50 /* */51 /* --------------------------------------------------------- */52 #define JID_RESOURCE 153 #define JID_USER 254 #define JID_SERVER 455 56 typedef struct jid_struct57 {58 pool p;59 char* resource;60 char* user;61 char* server;62 char* full;63 struct jid_struct *next; /* for lists of jids */64 } *jid;65 66 jid jid_new(pool p, char *idstr); /* Creates a jabber id from the idstr */67 void jid_set(jid id, char *str, int item); /* Individually sets jid components */68 char* jid_full(jid id); /* Builds a string type=user/resource@server from the jid data */69 int jid_cmp(jid a, jid b); /* Compares two jid's, returns 0 for perfect match */70 int jid_cmpx(jid a, jid b, int parts); /* Compares just the parts specified as JID_|JID_ */71 jid jid_append(jid a, jid b); /* Appending b to a (list), no dups */72 xmlnode jid_xres(jid id); /* Returns xmlnode representation of the resource?query=string */73 xmlnode jid_nodescan(jid id, xmlnode x); /* Scans the children of the node for a matching jid attribute */74 jid jid_user(jid a); /* returns the same jid but just of the user@host part */75 76 77 /* --------------------------------------------------------- */78 /* */79 /* JPacket structures & constants */80 /* */81 /* --------------------------------------------------------- */82 #define JPACKET_UNKNOWN 0x0083 #define JPACKET_MESSAGE 0x0184 #define JPACKET_PRESENCE 0x0285 #define JPACKET_IQ 0x0486 #define JPACKET_S10N 0x0887 88 #define JPACKET__UNKNOWN 089 #define JPACKET__NONE 190 #define JPACKET__ERROR 291 #define JPACKET__CHAT 392 #define JPACKET__GROUPCHAT 493 #define JPACKET__GET 594 #define JPACKET__SET 695 #define JPACKET__RESULT 796 #define JPACKET__SUBSCRIBE 897 #define JPACKET__SUBSCRIBED 998 #define JPACKET__UNSUBSCRIBE 1099 #define JPACKET__UNSUBSCRIBED 11100 #define JPACKET__AVAILABLE 12101 #define JPACKET__UNAVAILABLE 13102 #define JPACKET__PROBE 14103 #define JPACKET__HEADLINE 15104 #define JPACKET__INVISIBLE 16105 106 typedef struct jpacket_struct107 {108 unsigned char type;109 int subtype;110 int flag;111 void* aux1;112 xmlnode x;113 jid to;114 jid from;115 char* iqns;116 xmlnode iq;117 pool p;118 } *jpacket, _jpacket;119 120 jpacket jpacket_new(xmlnode x); /* Creates a jabber packet from the xmlnode */121 int jpacket_subtype(jpacket p); /* Returns the subtype value (looks at xmlnode for it) */122 123 124 /* --------------------------------------------------------- */125 /* */126 /* Presence Proxy DB structures & constants */127 /* */128 /* --------------------------------------------------------- */129 typedef struct ppdb_struct130 {131 jid id; /* entry data */132 int pri;133 xmlnode x;134 struct ppdb_struct* user; /* linked list for user@server */135 pool p; /* db-level data */136 struct ppdb_struct* next;137 } _ppdb, *ppdb;138 139 ppdb ppdb_insert(ppdb db, jid id, xmlnode x); /* Inserts presence into the proxy */140 xmlnode ppdb_primary(ppdb db, jid id); /* Fetches the matching primary presence for the id */141 void ppdb_free(ppdb db); /* Frees the db and all entries */142 xmlnode ppdb_get(ppdb db, jid id); /* Called successively to return each presence xmlnode */143 /* for the id and children, returns NULL at the end */144 145 146 /* --------------------------------------------------------- */147 /* */148 /* Simple Jabber Rate limit functions */149 /* */150 /* --------------------------------------------------------- */151 typedef struct jlimit_struct152 {153 char *key;154 int start;155 int points;156 int maxt, maxp;157 pool p;158 } *jlimit, _jlimit;159 160 jlimit jlimit_new(int maxt, int maxp);161 void jlimit_free(jlimit r);162 int jlimit_check(jlimit r, char *key, int points);163 164 165 /* --------------------------------------------------------- */166 /* */167 /* Error structures & constants */168 /* */169 /* --------------------------------------------------------- */170 typedef struct terror_struct171 {172 int code;173 char msg[64];174 } terror;175 176 #define TERROR_BAD (terror){400,"Bad Request"}177 #define TERROR_AUTH (terror){401,"Unauthorized"}178 #define TERROR_PAY (terror){402,"Payment Required"}179 #define TERROR_FORBIDDEN (terror){403,"Forbidden"}180 #define TERROR_NOTFOUND (terror){404,"Not Found"}181 #define TERROR_NOTALLOWED (terror){405,"Not Allowed"}182 #define TERROR_NOTACCEPTABLE (terror){406,"Not Acceptable"}183 #define TERROR_REGISTER (terror){407,"Registration Required"}184 #define TERROR_REQTIMEOUT (terror){408,"Request Timeout"}185 #define TERROR_CONFLICT (terror){409,"Conflict"}186 187 #define TERROR_INTERNAL (terror){500,"Internal Server Error"}188 #define TERROR_NOTIMPL (terror){501,"Not Implemented"}189 #define TERROR_EXTERNAL (terror){502,"Remote Server Error"}190 #define TERROR_UNAVAIL (terror){503,"Service Unavailable"}191 #define TERROR_EXTTIMEOUT (terror){504,"Remote Server Timeout"}192 #define TERROR_DISCONNECTED (terror){510,"Disconnected"}193 194 /* --------------------------------------------------------- */195 /* */196 /* Namespace constants */197 /* */198 /* --------------------------------------------------------- */199 #define NSCHECK(x,n) (j_strcmp(xmlnode_get_attrib(x,"xmlns"),n) == 0)200 201 #define NS_CLIENT "jabber:client"202 #define NS_SERVER "jabber:server"203 #define NS_AUTH "jabber:iq:auth"204 #define NS_REGISTER "jabber:iq:register"205 #define NS_ROSTER "jabber:iq:roster"206 #define NS_OFFLINE "jabber:x:offline"207 #define NS_AGENT "jabber:iq:agent"208 #define NS_AGENTS "jabber:iq:agents"209 #define NS_DELAY "jabber:x:delay"210 #define NS_VERSION "jabber:iq:version"211 #define NS_TIME "jabber:iq:time"212 #define NS_VCARD "vcard-temp"213 #define NS_PRIVATE "jabber:iq:private"214 #define NS_SEARCH "jabber:iq:search"215 #define NS_OOB "jabber:iq:oob"216 #define NS_XOOB "jabber:x:oob"217 #define NS_ADMIN "jabber:iq:admin"218 #define NS_FILTER "jabber:iq:filter"219 #define NS_AUTH_0K "jabber:iq:auth:0k"220 221 222 /* --------------------------------------------------------- */223 /* */224 /* Message Types */225 /* */226 /* --------------------------------------------------------- */227 #define TMSG_NORMAL "normal"228 #define TMSG_ERROR "error"229 #define TMSG_CHAT "chat"230 #define TMSG_GROUPCHAT "groupchat"231 #define TMSG_HEADLINE "headline"232 233 234 /* --------------------------------------------------------- */235 /* */236 /* JUtil functions */237 /* */238 /* --------------------------------------------------------- */239 xmlnode jutil_presnew(int type, char *to, char *status); /* Create a skeleton presence packet */240 xmlnode jutil_iqnew(int type, char *ns); /* Create a skeleton iq packet */241 xmlnode jutil_msgnew(char *type, char *to, char *subj, char *body);242 /* Create a skeleton message packet */243 xmlnode jutil_header(char* xmlns, char* server); /* Create a skeleton stream packet */244 int jutil_priority(xmlnode x); /* Determine priority of this packet */245 void jutil_tofrom(xmlnode x); /* Swaps to/from fields on a packet */246 xmlnode jutil_iqresult(xmlnode x); /* Generate a skeleton iq/result, given a iq/query */247 char* jutil_timestamp(void); /* Get stringified timestamp */248 void jutil_error(xmlnode x, terror E); /* Append an <error> node to x */249 void jutil_delay(xmlnode msg, char *reason); /* Append a delay packet to msg */250 char* jutil_regkey(char *key, char *seed); /* pass a seed to generate a key, pass the key again to validate (returns it) */251 252 253 /* --------------------------------------------------------- */254 /* */255 /* JConn structures & functions */256 /* */257 /* --------------------------------------------------------- */258 #define JCONN_STATE_OFF 0259 #define JCONN_STATE_CONNECTED 1260 #define JCONN_STATE_ON 2261 #define JCONN_STATE_AUTH 3262 263 typedef struct jconn_struct264 {265 /* Core structure */266 pool p; /* Memory allocation pool */267 int state; /* Connection state flag */268 int fd; /* Connection file descriptor */269 jid user; /* User info */270 char *pass; /* User passwd */271 272 /* Stream stuff */273 int id; /* id counter for jab_getid() function */274 char idbuf[9]; /* temporary storage for jab_getid() */275 char *sid; /* stream id from server, for digest auth */276 XML_Parser parser; /* Parser instance */277 xmlnode current; /* Current node in parsing instance.. */278 279 /* Event callback ptrs */280 void (*on_state)(struct jconn_struct *j, int state);281 void (*on_packet)(struct jconn_struct *j, jpacket p);282 283 } *jconn, jconn_struct;284 285 typedef void (*jconn_state_h)(jconn j, int state);286 typedef void (*jconn_packet_h)(jconn j, jpacket p);287 288 289 jconn jab_new(char *user, char *pass);290 void jab_delete(jconn j);291 void jab_state_handler(jconn j, jconn_state_h h);292 void jab_packet_handler(jconn j, jconn_packet_h h);293 void jab_start(jconn j);294 void jab_stop(jconn j);295 296 int jab_getfd(jconn j);297 jid jab_getjid(jconn j);298 char *jab_getsid(jconn j);299 char *jab_getid(jconn j);300 301 void jab_send(jconn j, xmlnode x);302 void jab_send_raw(jconn j, const char *str);303 void jab_recv(jconn j);304 void jab_poll(jconn j, int timeout);305 306 char *jab_auth(jconn j);307 char *jab_reg(jconn j);308 309 310 311 #ifdef __cplusplus312 }313 #endif314 315 #endif /* INCL_JABBER_H */ -
protocols/msn/msn.c
r3dc9d46 re617b35 183 183 static GList *msn_away_states( struct gaim_connection *gc ) 184 184 { 185 GList *l = NULL;185 static GList *l = NULL; 186 186 int i; 187 187 188 for( i = 0; msn_away_state_list[i].number > -1; i ++ ) 189 l = g_list_append( l, (void*) msn_away_state_list[i].name ); 190 191 return( l ); 188 if( l == NULL ) 189 for( i = 0; msn_away_state_list[i].number > -1; i ++ ) 190 l = g_list_append( l, (void*) msn_away_state_list[i].name ); 191 192 return l; 192 193 } 193 194 -
protocols/nogaim.c
r3dc9d46 re617b35 279 279 user_t *t, *u = irc->users; 280 280 account_t *a; 281 282 /* Nested calls might happen sometimes, this is probably the best 283 place to catch them. */ 284 if( gc->flags & OPT_LOGGING_OUT ) 285 return; 281 286 282 287 serv_got_crap( gc, "Signing off.." ); … … 1014 1019 } 1015 1020 1016 g_list_free( ms );1017 1018 1021 return( 1 ); 1019 1022 }
Note: See TracChangeset
for help on using the changeset viewer.