| 1 /* |
|
| 2 * gaim - Jabber Protocol Plugin |
|
| 3 * |
|
| 4 * Copyright (C) 2003, Nathan Walp <faceprint@faceprint.com> |
|
| 5 * |
|
| 6 * This program is free software; you can redistribute it and/or modify |
|
| 7 * it under the terms of the GNU General Public License as published by |
|
| 8 * the Free Software Foundation; either version 2 of the License, or |
|
| 9 * (at your option) any later version. |
|
| 10 * |
|
| 11 * This program is distributed in the hope that it will be useful, |
|
| 12 * but WITHOUT ANY WARRANTY; without even the implied warranty of |
|
| 13 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the |
|
| 14 * GNU General Public License for more details. |
|
| 15 * |
|
| 16 * You should have received a copy of the GNU General Public License |
|
| 17 * along with this program; if not, write to the Free Software |
|
| 18 * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA |
|
| 19 * |
|
| 20 */ |
|
| 21 #include "internal.h" |
|
| 22 |
|
| 23 #include "account.h" |
|
| 24 #include "accountopt.h" |
|
| 25 #include "blist.h" |
|
| 26 #include "cmds.h" |
|
| 27 #include "connection.h" |
|
| 28 #include "debug.h" |
|
| 29 #include "dnssrv.h" |
|
| 30 #include "message.h" |
|
| 31 #include "notify.h" |
|
| 32 #include "pluginpref.h" |
|
| 33 #include "prpl.h" |
|
| 34 #include "request.h" |
|
| 35 #include "server.h" |
|
| 36 #include "util.h" |
|
| 37 #include "version.h" |
|
| 38 |
|
| 39 #include "auth.h" |
|
| 40 #include "buddy.h" |
|
| 41 #include "chat.h" |
|
| 42 #include "disco.h" |
|
| 43 #include "iq.h" |
|
| 44 #include "jutil.h" |
|
| 45 #include "message.h" |
|
| 46 #include "parser.h" |
|
| 47 #include "presence.h" |
|
| 48 #include "jabber.h" |
|
| 49 #include "roster.h" |
|
| 50 #include "si.h" |
|
| 51 #include "xdata.h" |
|
| 52 |
|
| 53 #define JABBER_CONNECT_STEPS (js->gsc ? 8 : 5) |
|
| 54 |
|
| 55 static GaimPlugin *my_protocol = NULL; |
|
| 56 |
|
| 57 static void jabber_stream_init(JabberStream *js) |
|
| 58 { |
|
| 59 char *open_stream; |
|
| 60 |
|
| 61 open_stream = g_strdup_printf("<stream:stream to='%s' " |
|
| 62 "xmlns='jabber:client' " |
|
| 63 "xmlns:stream='http://etherx.jabber.org/streams' " |
|
| 64 "version='1.0'>", |
|
| 65 js->user->domain); |
|
| 66 |
|
| 67 jabber_send_raw(js, open_stream, -1); |
|
| 68 |
|
| 69 g_free(open_stream); |
|
| 70 } |
|
| 71 |
|
| 72 static void |
|
| 73 jabber_session_initialized_cb(JabberStream *js, xmlnode *packet, gpointer data) |
|
| 74 { |
|
| 75 const char *type = xmlnode_get_attrib(packet, "type"); |
|
| 76 if(type && !strcmp(type, "result")) { |
|
| 77 jabber_stream_set_state(js, JABBER_STREAM_CONNECTED); |
|
| 78 } else { |
|
| 79 gaim_connection_error(js->gc, _("Error initializing session")); |
|
| 80 } |
|
| 81 } |
|
| 82 |
|
| 83 static void jabber_session_init(JabberStream *js) |
|
| 84 { |
|
| 85 JabberIq *iq = jabber_iq_new(js, JABBER_IQ_SET); |
|
| 86 xmlnode *session; |
|
| 87 |
|
| 88 jabber_iq_set_callback(iq, jabber_session_initialized_cb, NULL); |
|
| 89 |
|
| 90 session = xmlnode_new_child(iq->node, "session"); |
|
| 91 xmlnode_set_attrib(session, "xmlns", "urn:ietf:params:xml:ns:xmpp-session"); |
|
| 92 |
|
| 93 jabber_iq_send(iq); |
|
| 94 } |
|
| 95 |
|
| 96 static void jabber_bind_result_cb(JabberStream *js, xmlnode *packet, |
|
| 97 gpointer data) |
|
| 98 { |
|
| 99 const char *type = xmlnode_get_attrib(packet, "type"); |
|
| 100 xmlnode *bind; |
|
| 101 |
|
| 102 if(type && !strcmp(type, "result") && |
|
| 103 (bind = xmlnode_get_child_with_namespace(packet, "bind", "urn:ietf:params:xml:ns:xmpp-bind"))) { |
|
| 104 xmlnode *jid; |
|
| 105 char *full_jid; |
|
| 106 if((jid = xmlnode_get_child(bind, "jid")) && (full_jid = xmlnode_get_data(jid))) { |
|
| 107 JabberBuddy *my_jb = NULL; |
|
| 108 jabber_id_free(js->user); |
|
| 109 if(!(js->user = jabber_id_new(full_jid))) { |
|
| 110 gaim_connection_error(js->gc, _("Invalid response from server.")); |
|
| 111 } |
|
| 112 if((my_jb = jabber_buddy_find(js, full_jid, TRUE))) |
|
| 113 my_jb->subscription |= JABBER_SUB_BOTH; |
|
| 114 g_free(full_jid); |
|
| 115 } |
|
| 116 } else { |
|
| 117 char *msg = jabber_parse_error(js, packet); |
|
| 118 gaim_connection_error(js->gc, msg); |
|
| 119 g_free(msg); |
|
| 120 } |
|
| 121 |
|
| 122 jabber_session_init(js); |
|
| 123 } |
|
| 124 |
|
| 125 static void jabber_stream_features_parse(JabberStream *js, xmlnode *packet) |
|
| 126 { |
|
| 127 if(xmlnode_get_child(packet, "starttls")) { |
|
| 128 if(jabber_process_starttls(js, packet)) |
|
| 129 return; |
|
| 130 } |
|
| 131 |
|
| 132 if(js->registration) { |
|
| 133 jabber_register_start(js); |
|
| 134 } else if(xmlnode_get_child(packet, "mechanisms")) { |
|
| 135 jabber_auth_start(js, packet); |
|
| 136 } else if(xmlnode_get_child(packet, "bind")) { |
|
| 137 xmlnode *bind, *resource; |
|
| 138 JabberIq *iq = jabber_iq_new(js, JABBER_IQ_SET); |
|
| 139 bind = xmlnode_new_child(iq->node, "bind"); |
|
| 140 xmlnode_set_attrib(bind, "xmlns", "urn:ietf:params:xml:ns:xmpp-bind"); |
|
| 141 resource = xmlnode_new_child(bind, "resource"); |
|
| 142 xmlnode_insert_data(resource, js->user->resource, -1); |
|
| 143 |
|
| 144 jabber_iq_set_callback(iq, jabber_bind_result_cb, NULL); |
|
| 145 |
|
| 146 jabber_iq_send(iq); |
|
| 147 } else /* if(xmlnode_get_child_with_namespace(packet, "auth")) */ { |
|
| 148 /* If we get an empty stream:features packet, or we explicitly get |
|
| 149 * an auth feature with namespace http://jabber.org/features/iq-auth |
|
| 150 * we should revert back to iq:auth authentication, even though we're |
|
| 151 * connecting to an XMPP server. */ |
|
| 152 js->auth_type = JABBER_AUTH_IQ_AUTH; |
|
| 153 jabber_stream_set_state(js, JABBER_STREAM_AUTHENTICATING); |
|
| 154 } |
|
| 155 } |
|
| 156 |
|
| 157 static void jabber_stream_handle_error(JabberStream *js, xmlnode *packet) |
|
| 158 { |
|
| 159 char *msg = jabber_parse_error(js, packet); |
|
| 160 |
|
| 161 gaim_connection_error(js->gc, msg); |
|
| 162 g_free(msg); |
|
| 163 } |
|
| 164 |
|
| 165 static void tls_init(JabberStream *js); |
|
| 166 |
|
| 167 void jabber_process_packet(JabberStream *js, xmlnode *packet) |
|
| 168 { |
|
| 169 if(!strcmp(packet->name, "iq")) { |
|
| 170 jabber_iq_parse(js, packet); |
|
| 171 } else if(!strcmp(packet->name, "presence")) { |
|
| 172 jabber_presence_parse(js, packet); |
|
| 173 } else if(!strcmp(packet->name, "message")) { |
|
| 174 jabber_message_parse(js, packet); |
|
| 175 } else if(!strcmp(packet->name, "stream:features")) { |
|
| 176 jabber_stream_features_parse(js, packet); |
|
| 177 } else if(!strcmp(packet->name, "stream:error")) { |
|
| 178 jabber_stream_handle_error(js, packet); |
|
| 179 } else if(!strcmp(packet->name, "challenge")) { |
|
| 180 if(js->state == JABBER_STREAM_AUTHENTICATING) |
|
| 181 jabber_auth_handle_challenge(js, packet); |
|
| 182 } else if(!strcmp(packet->name, "success")) { |
|
| 183 if(js->state == JABBER_STREAM_AUTHENTICATING) |
|
| 184 jabber_auth_handle_success(js, packet); |
|
| 185 } else if(!strcmp(packet->name, "failure")) { |
|
| 186 if(js->state == JABBER_STREAM_AUTHENTICATING) |
|
| 187 jabber_auth_handle_failure(js, packet); |
|
| 188 } else if(!strcmp(packet->name, "proceed")) { |
|
| 189 if(js->state == JABBER_STREAM_AUTHENTICATING && !js->gsc) |
|
| 190 tls_init(js); |
|
| 191 } else { |
|
| 192 gaim_debug(GAIM_DEBUG_WARNING, "jabber", "Unknown packet: %s\n", |
|
| 193 packet->name); |
|
| 194 } |
|
| 195 } |
|
| 196 |
|
| 197 void jabber_send_raw(JabberStream *js, const char *data, int len) |
|
| 198 { |
|
| 199 int ret; |
|
| 200 |
|
| 201 /* because printing a tab to debug every minute gets old */ |
|
| 202 if(strcmp(data, "\t")) |
|
| 203 gaim_debug(GAIM_DEBUG_MISC, "jabber", "Sending%s: %s\n", |
|
| 204 js->gsc ? " (ssl)" : "", data); |
|
| 205 |
|
| 206 /* If we've got a security layer, we need to encode the data, |
|
| 207 * splitting it on the maximum buffer length negotiated */ |
|
| 208 |
|
| 209 #ifdef HAVE_CYRUS_SASL |
|
| 210 if (js->sasl_maxbuf>0) { |
|
| 211 int pos; |
|
| 212 |
|
| 213 if (!js->gsc && js->fd<0) |
|
| 214 return; |
|
| 215 pos = 0; |
|
| 216 if (len == -1) |
|
| 217 len = strlen(data); |
|
| 218 while (pos < len) { |
|
| 219 int towrite; |
|
| 220 const char *out; |
|
| 221 unsigned olen; |
|
| 222 |
|
| 223 if ((len - pos) < js->sasl_maxbuf) |
|
| 224 towrite = len - pos; |
|
| 225 else |
|
| 226 towrite = js->sasl_maxbuf; |
|
| 227 |
|
| 228 sasl_encode(js->sasl, &data[pos], towrite, &out, &olen); |
|
| 229 pos += towrite; |
|
| 230 |
|
| 231 if (js->gsc) |
|
| 232 ret = gaim_ssl_write(js->gsc, out, olen); |
|
| 233 else |
|
| 234 ret = write(js->fd, out, olen); |
|
| 235 if (ret < 0) |
|
| 236 gaim_connection_error(js->gc, _("Write error")); |
|
| 237 } |
|
| 238 return; |
|
| 239 } |
|
| 240 #endif |
|
| 241 |
|
| 242 if(js->gsc) { |
|
| 243 ret = gaim_ssl_write(js->gsc, data, len == -1 ? strlen(data) : len); |
|
| 244 } else { |
|
| 245 if(js->fd < 0) |
|
| 246 return; |
|
| 247 ret = write(js->fd, data, len == -1 ? strlen(data) : len); |
|
| 248 } |
|
| 249 |
|
| 250 if(ret < 0) |
|
| 251 gaim_connection_error(js->gc, _("Write error")); |
|
| 252 |
|
| 253 } |
|
| 254 |
|
| 255 void jabber_send(JabberStream *js, xmlnode *packet) |
|
| 256 { |
|
| 257 char *txt; |
|
| 258 int len; |
|
| 259 |
|
| 260 txt = xmlnode_to_str(packet, &len); |
|
| 261 jabber_send_raw(js, txt, len); |
|
| 262 g_free(txt); |
|
| 263 } |
|
| 264 |
|
| 265 static void jabber_keepalive(GaimConnection *gc) |
|
| 266 { |
|
| 267 jabber_send_raw(gc->proto_data, "\t", -1); |
|
| 268 } |
|
| 269 |
|
| 270 static void |
|
| 271 jabber_recv_cb_ssl(gpointer data, GaimSslConnection *gsc, |
|
| 272 GaimInputCondition cond) |
|
| 273 { |
|
| 274 GaimConnection *gc = data; |
|
| 275 JabberStream *js = gc->proto_data; |
|
| 276 int len; |
|
| 277 static char buf[4096]; |
|
| 278 |
|
| 279 if(!g_list_find(gaim_connections_get_all(), gc)) { |
|
| 280 gaim_ssl_close(gsc); |
|
| 281 return; |
|
| 282 } |
|
| 283 |
|
| 284 if((len = gaim_ssl_read(gsc, buf, sizeof(buf) - 1)) > 0) { |
|
| 285 buf[len] = '\0'; |
|
| 286 gaim_debug(GAIM_DEBUG_INFO, "jabber", "Recv (ssl)(%d): %s\n", len, buf); |
|
| 287 jabber_parser_process(js, buf, len); |
|
| 288 } else { |
|
| 289 gaim_connection_error(gc, _("Read Error")); |
|
| 290 } |
|
| 291 } |
|
| 292 |
|
| 293 static void |
|
| 294 jabber_recv_cb(gpointer data, gint source, GaimInputCondition condition) |
|
| 295 { |
|
| 296 GaimConnection *gc = data; |
|
| 297 JabberStream *js = gc->proto_data; |
|
| 298 int len; |
|
| 299 static char buf[4096]; |
|
| 300 |
|
| 301 if(!g_list_find(gaim_connections_get_all(), gc)) |
|
| 302 return; |
|
| 303 |
|
| 304 if((len = read(js->fd, buf, sizeof(buf) - 1)) > 0) { |
|
| 305 #ifdef HAVE_CYRUS_SASL |
|
| 306 if (js->sasl_maxbuf>0) { |
|
| 307 const char *out; |
|
| 308 int olen; |
|
| 309 sasl_decode(js->sasl, buf, len, &out, &olen); |
|
| 310 if (olen>0) { |
|
| 311 gaim_debug(GAIM_DEBUG_INFO, "jabber", "RecvSASL (%d): %s\n", olen, out); |
|
| 312 jabber_parser_process(js,out,olen); |
|
| 313 } |
|
| 314 return; |
|
| 315 } |
|
| 316 #endif |
|
| 317 buf[len] = '\0'; |
|
| 318 gaim_debug(GAIM_DEBUG_INFO, "jabber", "Recv (%d): %s\n", len, buf); |
|
| 319 jabber_parser_process(js, buf, len); |
|
| 320 } else { |
|
| 321 gaim_connection_error(gc, _("Read Error")); |
|
| 322 } |
|
| 323 } |
|
| 324 |
|
| 325 static void |
|
| 326 jabber_login_callback_ssl(gpointer data, GaimSslConnection *gsc, |
|
| 327 GaimInputCondition cond) |
|
| 328 { |
|
| 329 GaimConnection *gc = data; |
|
| 330 JabberStream *js = gc->proto_data; |
|
| 331 |
|
| 332 if(!g_list_find(gaim_connections_get_all(), gc)) { |
|
| 333 gaim_ssl_close(gsc); |
|
| 334 return; |
|
| 335 } |
|
| 336 |
|
| 337 js->gsc = gsc; |
|
| 338 |
|
| 339 if(js->state == JABBER_STREAM_CONNECTING) |
|
| 340 jabber_send_raw(js, "<?xml version='1.0' ?>", -1); |
|
| 341 |
|
| 342 jabber_stream_set_state(js, JABBER_STREAM_INITIALIZING); |
|
| 343 gaim_ssl_input_add(gsc, jabber_recv_cb_ssl, gc); |
|
| 344 } |
|
| 345 |
|
| 346 |
|
| 347 static void |
|
| 348 jabber_login_callback(gpointer data, gint source, GaimInputCondition cond) |
|
| 349 { |
|
| 350 GaimConnection *gc = data; |
|
| 351 JabberStream *js = gc->proto_data; |
|
| 352 |
|
| 353 if (source < 0) { |
|
| 354 gaim_connection_error(gc, _("Couldn't connect to host")); |
|
| 355 return; |
|
| 356 } |
|
| 357 |
|
| 358 if(!g_list_find(gaim_connections_get_all(), gc)) { |
|
| 359 close(source); |
|
| 360 return; |
|
| 361 } |
|
| 362 |
|
| 363 js->fd = source; |
|
| 364 |
|
| 365 if(js->state == JABBER_STREAM_CONNECTING) |
|
| 366 jabber_send_raw(js, "<?xml version='1.0' ?>", -1); |
|
| 367 |
|
| 368 jabber_stream_set_state(js, JABBER_STREAM_INITIALIZING); |
|
| 369 gc->inpa = gaim_input_add(js->fd, GAIM_INPUT_READ, jabber_recv_cb, gc); |
|
| 370 } |
|
| 371 |
|
| 372 static void |
|
| 373 jabber_ssl_connect_failure(GaimSslConnection *gsc, GaimSslErrorType error, |
|
| 374 gpointer data) |
|
| 375 { |
|
| 376 GaimConnection *gc = data; |
|
| 377 JabberStream *js = gc->proto_data; |
|
| 378 |
|
| 379 switch(error) { |
|
| 380 case GAIM_SSL_CONNECT_FAILED: |
|
| 381 gaim_connection_error(gc, _("Connection Failed")); |
|
| 382 break; |
|
| 383 case GAIM_SSL_HANDSHAKE_FAILED: |
|
| 384 gaim_connection_error(gc, _("SSL Handshake Failed")); |
|
| 385 break; |
|
| 386 } |
|
| 387 |
|
| 388 js->gsc = NULL; |
|
| 389 } |
|
| 390 |
|
| 391 static void tls_init(JabberStream *js) |
|
| 392 { |
|
| 393 gaim_input_remove(js->gc->inpa); |
|
| 394 js->gc->inpa = 0; |
|
| 395 js->gsc = gaim_ssl_connect_fd(js->gc->account, js->fd, |
|
| 396 jabber_login_callback_ssl, jabber_ssl_connect_failure, js->gc); |
|
| 397 } |
|
| 398 |
|
| 399 static void jabber_login_connect(JabberStream *js, const char *server, int port) |
|
| 400 { |
|
| 401 int rc; |
|
| 402 |
|
| 403 rc = gaim_proxy_connect(js->gc->account, server, |
|
| 404 port, jabber_login_callback, js->gc); |
|
| 405 |
|
| 406 if (rc != 0) |
|
| 407 gaim_connection_error(js->gc, _("Unable to create socket")); |
|
| 408 } |
|
| 409 |
|
| 410 static void srv_resolved_cb(GaimSrvResponse *resp, int results, gpointer data) |
|
| 411 { |
|
| 412 JabberStream *js = (JabberStream*)data; |
|
| 413 |
|
| 414 if(results) { |
|
| 415 jabber_login_connect(js, resp->hostname, resp->port); |
|
| 416 g_free(resp); |
|
| 417 } else { |
|
| 418 jabber_login_connect(js, js->user->domain, |
|
| 419 gaim_account_get_int(js->gc->account, "port", 0)); |
|
| 420 } |
|
| 421 } |
|
| 422 |
|
| 423 |
|
| 424 |
|
| 425 static void |
|
| 426 jabber_login(GaimAccount *account) |
|
| 427 { |
|
| 428 GaimConnection *gc = gaim_account_get_connection(account); |
|
| 429 const char *connect_server = gaim_account_get_string(account, |
|
| 430 "connect_server", ""); |
|
| 431 JabberStream *js; |
|
| 432 JabberBuddy *my_jb = NULL; |
|
| 433 |
|
| 434 gc->flags |= GAIM_CONNECTION_HTML; |
|
| 435 js = gc->proto_data = g_new0(JabberStream, 1); |
|
| 436 js->gc = gc; |
|
| 437 js->fd = -1; |
|
| 438 js->iq_callbacks = g_hash_table_new_full(g_str_hash, g_str_equal, |
|
| 439 g_free, g_free); |
|
| 440 js->disco_callbacks = g_hash_table_new_full(g_str_hash, g_str_equal, |
|
| 441 g_free, g_free); |
|
| 442 js->buddies = g_hash_table_new_full(g_str_hash, g_str_equal, |
|
| 443 g_free, (GDestroyNotify)jabber_buddy_free); |
|
| 444 js->chats = g_hash_table_new_full(g_str_hash, g_str_equal, |
|
| 445 g_free, (GDestroyNotify)jabber_chat_free); |
|
| 446 js->chat_servers = g_list_append(NULL, g_strdup("conference.jabber.org")); |
|
| 447 js->user = jabber_id_new(gaim_account_get_username(account)); |
|
| 448 js->next_id = g_random_int(); |
|
| 449 |
|
| 450 if(!js->user) { |
|
| 451 gaim_connection_error(gc, _("Invalid Jabber ID")); |
|
| 452 return; |
|
| 453 } |
|
| 454 |
|
| 455 if(!js->user->resource) { |
|
| 456 char *me; |
|
| 457 js->user->resource = g_strdup("Home"); |
|
| 458 if(!js->user->node) { |
|
| 459 js->user->node = js->user->domain; |
|
| 460 js->user->domain = g_strdup("jabber.org"); |
|
| 461 } |
|
| 462 me = g_strdup_printf("%s@%s/%s", js->user->node, js->user->domain, |
|
| 463 js->user->resource); |
|
| 464 gaim_account_set_username(account, me); |
|
| 465 g_free(me); |
|
| 466 } |
|
| 467 |
|
| 468 if((my_jb = jabber_buddy_find(js, gaim_account_get_username(account), TRUE))) |
|
| 469 my_jb->subscription |= JABBER_SUB_BOTH; |
|
| 470 |
|
| 471 jabber_stream_set_state(js, JABBER_STREAM_CONNECTING); |
|
| 472 |
|
| 473 /* if they've got old-ssl mode going, we probably want to ignore SRV lookups */ |
|
| 474 if(gaim_account_get_bool(js->gc->account, "old_ssl", FALSE)) { |
|
| 475 if(gaim_ssl_is_supported()) { |
|
| 476 js->gsc = gaim_ssl_connect(js->gc->account, |
|
| 477 connect_server[0] ? connect_server : js->user->domain, |
|
| 478 gaim_account_get_int(account, "port", 5223), jabber_login_callback_ssl, |
|
| 479 jabber_ssl_connect_failure, js->gc); |
|
| 480 } else { |
|
| 481 gaim_connection_error(js->gc, _("SSL support unavailable")); |
|
| 482 } |
|
| 483 } |
|
| 484 |
|
| 485 /* no old-ssl, so if they've specified a connect server, we'll use that, otherwise we'll |
|
| 486 * invoke the magic of SRV lookups, to figure out host and port */ |
|
| 487 if(!js->gsc) { |
|
| 488 if(connect_server[0]) { |
|
| 489 jabber_login_connect(js, connect_server, gaim_account_get_int(account, "port", 5222)); |
|
| 490 } else { |
|
| 491 gaim_srv_resolve("xmpp-client", "tcp", js->user->domain, srv_resolved_cb, js); |
|
| 492 } |
|
| 493 } |
|
| 494 } |
|
| 495 |
|
| 496 |
|
| 497 static gboolean |
|
| 498 conn_close_cb(gpointer data) |
|
| 499 { |
|
| 500 JabberStream *js = data; |
|
| 501 GaimAccount *account = gaim_connection_get_account(js->gc); |
|
| 502 |
|
| 503 gaim_account_disconnect(account); |
|
| 504 |
|
| 505 return FALSE; |
|
| 506 } |
|
| 507 |
|
| 508 static void |
|
| 509 jabber_connection_schedule_close(JabberStream *js) |
|
| 510 { |
|
| 511 gaim_timeout_add(0, conn_close_cb, js); |
|
| 512 } |
|
| 513 |
|
| 514 static void |
|
| 515 jabber_registration_result_cb(JabberStream *js, xmlnode *packet, gpointer data) |
|
| 516 { |
|
| 517 const char *type = xmlnode_get_attrib(packet, "type"); |
|
| 518 char *buf; |
|
| 519 |
|
| 520 if(!strcmp(type, "result")) { |
|
| 521 buf = g_strdup_printf(_("Registration of %s@%s successful"), |
|
| 522 js->user->node, js->user->domain); |
|
| 523 gaim_notify_info(NULL, _("Registration Successful"), |
|
| 524 _("Registration Successful"), buf); |
|
| 525 g_free(buf); |
|
| 526 } else { |
|
| 527 char *msg = jabber_parse_error(js, packet); |
|
| 528 |
|
| 529 if(!msg) |
|
| 530 msg = g_strdup(_("Unknown Error")); |
|
| 531 |
|
| 532 gaim_notify_error(NULL, _("Registration Failed"), |
|
| 533 _("Registration Failed"), msg); |
|
| 534 g_free(msg); |
|
| 535 } |
|
| 536 jabber_connection_schedule_close(js); |
|
| 537 } |
|
| 538 |
|
| 539 static void |
|
| 540 jabber_register_cb(JabberStream *js, GaimRequestFields *fields) |
|
| 541 { |
|
| 542 GList *groups, *flds; |
|
| 543 xmlnode *query, *y; |
|
| 544 JabberIq *iq; |
|
| 545 char *username; |
|
| 546 |
|
| 547 iq = jabber_iq_new_query(js, JABBER_IQ_SET, "jabber:iq:register"); |
|
| 548 query = xmlnode_get_child(iq->node, "query"); |
|
| 549 |
|
| 550 for(groups = gaim_request_fields_get_groups(fields); groups; |
|
| 551 groups = groups->next) { |
|
| 552 for(flds = gaim_request_field_group_get_fields(groups->data); |
|
| 553 flds; flds = flds->next) { |
|
| 554 GaimRequestField *field = flds->data; |
|
| 555 const char *id = gaim_request_field_get_id(field); |
|
| 556 const char *value = gaim_request_field_string_get_value(field); |
|
| 557 |
|
| 558 if(!strcmp(id, "username")) { |
|
| 559 y = xmlnode_new_child(query, "username"); |
|
| 560 } else if(!strcmp(id, "password")) { |
|
| 561 y = xmlnode_new_child(query, "password"); |
|
| 562 } else if(!strcmp(id, "name")) { |
|
| 563 y = xmlnode_new_child(query, "name"); |
|
| 564 } else if(!strcmp(id, "email")) { |
|
| 565 y = xmlnode_new_child(query, "email"); |
|
| 566 } else if(!strcmp(id, "nick")) { |
|
| 567 y = xmlnode_new_child(query, "nick"); |
|
| 568 } else if(!strcmp(id, "first")) { |
|
| 569 y = xmlnode_new_child(query, "first"); |
|
| 570 } else if(!strcmp(id, "last")) { |
|
| 571 y = xmlnode_new_child(query, "last"); |
|
| 572 } else if(!strcmp(id, "address")) { |
|
| 573 y = xmlnode_new_child(query, "address"); |
|
| 574 } else if(!strcmp(id, "city")) { |
|
| 575 y = xmlnode_new_child(query, "city"); |
|
| 576 } else if(!strcmp(id, "state")) { |
|
| 577 y = xmlnode_new_child(query, "state"); |
|
| 578 } else if(!strcmp(id, "zip")) { |
|
| 579 y = xmlnode_new_child(query, "zip"); |
|
| 580 } else if(!strcmp(id, "phone")) { |
|
| 581 y = xmlnode_new_child(query, "phone"); |
|
| 582 } else if(!strcmp(id, "url")) { |
|
| 583 y = xmlnode_new_child(query, "url"); |
|
| 584 } else if(!strcmp(id, "date")) { |
|
| 585 y = xmlnode_new_child(query, "date"); |
|
| 586 } else { |
|
| 587 continue; |
|
| 588 } |
|
| 589 xmlnode_insert_data(y, value, -1); |
|
| 590 if(!strcmp(id, "username")) { |
|
| 591 if(js->user->node) |
|
| 592 g_free(js->user->node); |
|
| 593 js->user->node = g_strdup(value); |
|
| 594 } |
|
| 595 } |
|
| 596 } |
|
| 597 |
|
| 598 username = g_strdup_printf("%s@%s/%s", js->user->node, js->user->domain, |
|
| 599 js->user->resource); |
|
| 600 gaim_account_set_username(js->gc->account, username); |
|
| 601 g_free(username); |
|
| 602 |
|
| 603 jabber_iq_set_callback(iq, jabber_registration_result_cb, NULL); |
|
| 604 |
|
| 605 jabber_iq_send(iq); |
|
| 606 |
|
| 607 } |
|
| 608 |
|
| 609 static void |
|
| 610 jabber_register_cancel_cb(JabberStream *js, GaimRequestFields *fields) |
|
| 611 { |
|
| 612 jabber_connection_schedule_close(js); |
|
| 613 } |
|
| 614 |
|
| 615 static void jabber_register_x_data_cb(JabberStream *js, xmlnode *result, gpointer data) |
|
| 616 { |
|
| 617 xmlnode *query; |
|
| 618 JabberIq *iq; |
|
| 619 |
|
| 620 iq = jabber_iq_new_query(js, JABBER_IQ_SET, "jabber:iq:register"); |
|
| 621 query = xmlnode_get_child(iq->node, "query"); |
|
| 622 |
|
| 623 xmlnode_insert_child(query, result); |
|
| 624 |
|
| 625 jabber_iq_set_callback(iq, jabber_registration_result_cb, NULL); |
|
| 626 jabber_iq_send(iq); |
|
| 627 } |
|
| 628 |
|
| 629 void jabber_register_parse(JabberStream *js, xmlnode *packet) |
|
| 630 { |
|
| 631 if(js->registration) { |
|
| 632 GaimRequestFields *fields; |
|
| 633 GaimRequestFieldGroup *group; |
|
| 634 GaimRequestField *field; |
|
| 635 xmlnode *query, *x, *y; |
|
| 636 char *instructions; |
|
| 637 |
|
| 638 /* get rid of the login thingy */ |
|
| 639 gaim_connection_set_state(js->gc, GAIM_CONNECTED); |
|
| 640 |
|
| 641 query = xmlnode_get_child(packet, "query"); |
|
| 642 |
|
| 643 if(xmlnode_get_child(query, "registered")) { |
|
| 644 gaim_notify_error(NULL, _("Already Registered"), |
|
| 645 _("Already Registered"), NULL); |
|
| 646 jabber_connection_schedule_close(js); |
|
| 647 return; |
|
| 648 } |
|
| 649 |
|
| 650 if((x = xmlnode_get_child_with_namespace(packet, "x", |
|
| 651 "jabber:x:data"))) { |
|
| 652 jabber_x_data_request(js, x, jabber_register_x_data_cb, NULL); |
|
| 653 return; |
|
| 654 } else if((x = xmlnode_get_child_with_namespace(packet, "x", |
|
| 655 "jabber:x:oob"))) { |
|
| 656 xmlnode *url; |
|
| 657 |
|
| 658 if((url = xmlnode_get_child(x, "url"))) { |
|
| 659 char *href; |
|
| 660 if((href = xmlnode_get_data(url))) { |
|
| 661 gaim_notify_uri(NULL, href); |
|
| 662 g_free(href); |
|
| 663 js->gc->wants_to_die = TRUE; |
|
| 664 jabber_connection_schedule_close(js); |
|
| 665 return; |
|
| 666 } |
|
| 667 } |
|
| 668 } |
|
| 669 |
|
| 670 /* as a last resort, use the old jabber:iq:register syntax */ |
|
| 671 |
|
| 672 fields = gaim_request_fields_new(); |
|
| 673 group = gaim_request_field_group_new(NULL); |
|
| 674 gaim_request_fields_add_group(fields, group); |
|
| 675 |
|
| 676 field = gaim_request_field_string_new("username", _("Username"), |
|
| 677 js->user->node, FALSE); |
|
| 678 gaim_request_field_group_add_field(group, field); |
|
| 679 |
|
| 680 field = gaim_request_field_string_new("password", _("Password"), |
|
| 681 gaim_connection_get_password(js->gc), FALSE); |
|
| 682 gaim_request_field_string_set_masked(field, TRUE); |
|
| 683 gaim_request_field_group_add_field(group, field); |
|
| 684 |
|
| 685 if(xmlnode_get_child(query, "name")) { |
|
| 686 field = gaim_request_field_string_new("name", _("Name"), |
|
| 687 gaim_account_get_alias(js->gc->account), FALSE); |
|
| 688 gaim_request_field_group_add_field(group, field); |
|
| 689 } |
|
| 690 if(xmlnode_get_child(query, "email")) { |
|
| 691 field = gaim_request_field_string_new("email", _("E-Mail"), |
|
| 692 NULL, FALSE); |
|
| 693 gaim_request_field_group_add_field(group, field); |
|
| 694 } |
|
| 695 if(xmlnode_get_child(query, "nick")) { |
|
| 696 field = gaim_request_field_string_new("nick", _("Nickname"), |
|
| 697 NULL, FALSE); |
|
| 698 gaim_request_field_group_add_field(group, field); |
|
| 699 } |
|
| 700 if(xmlnode_get_child(query, "first")) { |
|
| 701 field = gaim_request_field_string_new("first", _("First Name"), |
|
| 702 NULL, FALSE); |
|
| 703 gaim_request_field_group_add_field(group, field); |
|
| 704 } |
|
| 705 if(xmlnode_get_child(query, "last")) { |
|
| 706 field = gaim_request_field_string_new("last", _("Last Name"), |
|
| 707 NULL, FALSE); |
|
| 708 gaim_request_field_group_add_field(group, field); |
|
| 709 } |
|
| 710 if(xmlnode_get_child(query, "address")) { |
|
| 711 field = gaim_request_field_string_new("address", _("Address"), |
|
| 712 NULL, FALSE); |
|
| 713 gaim_request_field_group_add_field(group, field); |
|
| 714 } |
|
| 715 if(xmlnode_get_child(query, "city")) { |
|
| 716 field = gaim_request_field_string_new("city", _("City"), |
|
| 717 NULL, FALSE); |
|
| 718 gaim_request_field_group_add_field(group, field); |
|
| 719 } |
|
| 720 if(xmlnode_get_child(query, "state")) { |
|
| 721 field = gaim_request_field_string_new("state", _("State"), |
|
| 722 NULL, FALSE); |
|
| 723 gaim_request_field_group_add_field(group, field); |
|
| 724 } |
|
| 725 if(xmlnode_get_child(query, "zip")) { |
|
| 726 field = gaim_request_field_string_new("zip", _("Postal Code"), |
|
| 727 NULL, FALSE); |
|
| 728 gaim_request_field_group_add_field(group, field); |
|
| 729 } |
|
| 730 if(xmlnode_get_child(query, "phone")) { |
|
| 731 field = gaim_request_field_string_new("phone", _("Phone"), |
|
| 732 NULL, FALSE); |
|
| 733 gaim_request_field_group_add_field(group, field); |
|
| 734 } |
|
| 735 if(xmlnode_get_child(query, "url")) { |
|
| 736 field = gaim_request_field_string_new("url", _("URL"), |
|
| 737 NULL, FALSE); |
|
| 738 gaim_request_field_group_add_field(group, field); |
|
| 739 } |
|
| 740 if(xmlnode_get_child(query, "date")) { |
|
| 741 field = gaim_request_field_string_new("date", _("Date"), |
|
| 742 NULL, FALSE); |
|
| 743 gaim_request_field_group_add_field(group, field); |
|
| 744 } |
|
| 745 |
|
| 746 if((y = xmlnode_get_child(query, "instructions"))) |
|
| 747 instructions = xmlnode_get_data(y); |
|
| 748 else |
|
| 749 instructions = g_strdup(_("Please fill out the information below " |
|
| 750 "to register your new account.")); |
|
| 751 |
|
| 752 gaim_request_fields(js->gc, _("Register New Jabber Account"), |
|
| 753 _("Register New Jabber Account"), instructions, fields, |
|
| 754 _("Register"), G_CALLBACK(jabber_register_cb), |
|
| 755 _("Cancel"), G_CALLBACK(jabber_register_cancel_cb), js); |
|
| 756 } |
|
| 757 } |
|
| 758 |
|
| 759 void jabber_register_start(JabberStream *js) |
|
| 760 { |
|
| 761 JabberIq *iq; |
|
| 762 |
|
| 763 iq = jabber_iq_new_query(js, JABBER_IQ_GET, "jabber:iq:register"); |
|
| 764 jabber_iq_send(iq); |
|
| 765 } |
|
| 766 |
|
| 767 static void jabber_register_account(GaimAccount *account) |
|
| 768 { |
|
| 769 GaimConnection *gc = gaim_account_get_connection(account); |
|
| 770 JabberStream *js; |
|
| 771 JabberBuddy *my_jb = NULL; |
|
| 772 const char *connect_server = gaim_account_get_string(account, |
|
| 773 "connect_server", ""); |
|
| 774 const char *server; |
|
| 775 int rc; |
|
| 776 |
|
| 777 js = gc->proto_data = g_new0(JabberStream, 1); |
|
| 778 js->gc = gc; |
|
| 779 js->registration = TRUE; |
|
| 780 js->iq_callbacks = g_hash_table_new_full(g_str_hash, g_str_equal, |
|
| 781 g_free, g_free); |
|
| 782 js->disco_callbacks = g_hash_table_new_full(g_str_hash, g_str_equal, |
|
| 783 g_free, g_free); |
|
| 784 js->user = jabber_id_new(gaim_account_get_username(account)); |
|
| 785 js->next_id = g_random_int(); |
|
| 786 |
|
| 787 if(!js->user) { |
|
| 788 gaim_connection_error(gc, _("Invalid Jabber ID")); |
|
| 789 return; |
|
| 790 } |
|
| 791 |
|
| 792 if(!js->user->resource) { |
|
| 793 char *me; |
|
| 794 js->user->resource = g_strdup("Home"); |
|
| 795 if(!js->user->node) { |
|
| 796 js->user->node = js->user->domain; |
|
| 797 js->user->domain = g_strdup("jabber.org"); |
|
| 798 } |
|
| 799 me = g_strdup_printf("%s@%s/%s", js->user->node, js->user->domain, |
|
| 800 js->user->resource); |
|
| 801 gaim_account_set_username(account, me); |
|
| 802 g_free(me); |
|
| 803 } |
|
| 804 |
|
| 805 if((my_jb = jabber_buddy_find(js, gaim_account_get_username(account), TRUE))) |
|
| 806 my_jb->subscription |= JABBER_SUB_BOTH; |
|
| 807 |
|
| 808 server = connect_server[0] ? connect_server : js->user->domain; |
|
| 809 |
|
| 810 jabber_stream_set_state(js, JABBER_STREAM_CONNECTING); |
|
| 811 |
|
| 812 if(gaim_account_get_bool(account, "old_ssl", FALSE)) { |
|
| 813 if(gaim_ssl_is_supported()) { |
|
| 814 js->gsc = gaim_ssl_connect(account, server, |
|
| 815 gaim_account_get_int(account, "port", 5222), |
|
| 816 jabber_login_callback_ssl, jabber_ssl_connect_failure, gc); |
|
| 817 } else { |
|
| 818 gaim_connection_error(gc, _("SSL support unavailable")); |
|
| 819 } |
|
| 820 } |
|
| 821 |
|
| 822 if(!js->gsc) { |
|
| 823 rc = gaim_proxy_connect(account, server, |
|
| 824 gaim_account_get_int(account, "port", 5222), |
|
| 825 jabber_login_callback, gc); |
|
| 826 |
|
| 827 if (rc != 0) |
|
| 828 gaim_connection_error(gc, _("Unable to create socket")); |
|
| 829 } |
|
| 830 } |
|
| 831 |
|
| 832 static void jabber_close(GaimConnection *gc) |
|
| 833 { |
|
| 834 JabberStream *js = gc->proto_data; |
|
| 835 |
|
| 836 jabber_send_raw(js, "</stream:stream>", -1); |
|
| 837 |
|
| 838 if(js->gsc) { |
|
| 839 gaim_ssl_close(js->gsc); |
|
| 840 } else if (js->fd > 0) { |
|
| 841 if(js->gc->inpa) |
|
| 842 gaim_input_remove(js->gc->inpa); |
|
| 843 close(js->fd); |
|
| 844 } |
|
| 845 |
|
| 846 if(js->context) |
|
| 847 g_markup_parse_context_free(js->context); |
|
| 848 if(js->iq_callbacks) |
|
| 849 g_hash_table_destroy(js->iq_callbacks); |
|
| 850 if(js->disco_callbacks) |
|
| 851 g_hash_table_destroy(js->disco_callbacks); |
|
| 852 if(js->buddies) |
|
| 853 g_hash_table_destroy(js->buddies); |
|
| 854 if(js->chats) |
|
| 855 g_hash_table_destroy(js->chats); |
|
| 856 while(js->chat_servers) { |
|
| 857 g_free(js->chat_servers->data); |
|
| 858 js->chat_servers = g_list_delete_link(js->chat_servers, js->chat_servers); |
|
| 859 } |
|
| 860 while(js->user_directories) { |
|
| 861 g_free(js->user_directories->data); |
|
| 862 js->user_directories = g_list_delete_link(js->user_directories, js->user_directories); |
|
| 863 } |
|
| 864 if(js->stream_id) |
|
| 865 g_free(js->stream_id); |
|
| 866 if(js->user) |
|
| 867 jabber_id_free(js->user); |
|
| 868 if(js->avatar_hash) |
|
| 869 g_free(js->avatar_hash); |
|
| 870 #ifdef HAVE_CYRUS_SASL |
|
| 871 if(js->sasl) |
|
| 872 sasl_dispose(&js->sasl); |
|
| 873 if(js->sasl_mechs) |
|
| 874 g_string_free(js->sasl_mechs, TRUE); |
|
| 875 if(js->sasl_cb) |
|
| 876 g_free(js->sasl_cb); |
|
| 877 #endif |
|
| 878 g_free(js); |
|
| 879 |
|
| 880 gc->proto_data = NULL; |
|
| 881 } |
|
| 882 |
|
| 883 void jabber_stream_set_state(JabberStream *js, JabberStreamState state) |
|
| 884 { |
|
| 885 GaimPresence *gpresence; |
|
| 886 GaimStatus *status; |
|
| 887 |
|
| 888 js->state = state; |
|
| 889 switch(state) { |
|
| 890 case JABBER_STREAM_OFFLINE: |
|
| 891 break; |
|
| 892 case JABBER_STREAM_CONNECTING: |
|
| 893 gaim_connection_update_progress(js->gc, _("Connecting"), 1, |
|
| 894 JABBER_CONNECT_STEPS); |
|
| 895 break; |
|
| 896 case JABBER_STREAM_INITIALIZING: |
|
| 897 gaim_connection_update_progress(js->gc, _("Initializing Stream"), |
|
| 898 js->gsc ? 5 : 2, JABBER_CONNECT_STEPS); |
|
| 899 jabber_stream_init(js); |
|
| 900 jabber_parser_setup(js); |
|
| 901 break; |
|
| 902 case JABBER_STREAM_AUTHENTICATING: |
|
| 903 gaim_connection_update_progress(js->gc, _("Authenticating"), |
|
| 904 js->gsc ? 6 : 3, JABBER_CONNECT_STEPS); |
|
| 905 if(js->protocol_version == JABBER_PROTO_0_9 && js->registration) { |
|
| 906 jabber_register_start(js); |
|
| 907 } else if(js->auth_type == JABBER_AUTH_IQ_AUTH) { |
|
| 908 jabber_auth_start_old(js); |
|
| 909 } |
|
| 910 break; |
|
| 911 case JABBER_STREAM_REINITIALIZING: |
|
| 912 gaim_connection_update_progress(js->gc, _("Re-initializing Stream"), |
|
| 913 6, JABBER_CONNECT_STEPS); |
|
| 914 jabber_stream_init(js); |
|
| 915 break; |
|
| 916 case JABBER_STREAM_CONNECTED: |
|
| 917 jabber_roster_request(js); |
|
| 918 gpresence = gaim_account_get_presence(js->gc->account); |
|
| 919 status = gaim_presence_get_active_status(gpresence); |
|
| 920 jabber_presence_send(js->gc->account, status); |
|
| 921 gaim_connection_set_state(js->gc, GAIM_CONNECTED); |
|
| 922 jabber_disco_items_server(js); |
|
| 923 break; |
|
| 924 } |
|
| 925 } |
|
| 926 |
|
| 927 char *jabber_get_next_id(JabberStream *js) |
|
| 928 { |
|
| 929 return g_strdup_printf("gaim%x", js->next_id++); |
|
| 930 } |
|
| 931 |
|
| 932 |
|
| 933 static void jabber_idle_set(GaimConnection *gc, int idle) |
|
| 934 { |
|
| 935 JabberStream *js = gc->proto_data; |
|
| 936 |
|
| 937 js->idle = idle ? time(NULL) - idle : idle; |
|
| 938 } |
|
| 939 |
|
| 940 static const char *jabber_list_icon(GaimAccount *a, GaimBuddy *b) |
|
| 941 { |
|
| 942 return "jabber"; |
|
| 943 } |
|
| 944 |
|
| 945 static void jabber_list_emblems(GaimBuddy *b, const char **se, const char **sw, |
|
| 946 const char **nw, const char **ne) |
|
| 947 { |
|
| 948 JabberStream *js; |
|
| 949 JabberBuddy *jb; |
|
| 950 |
|
| 951 if(!b->account->gc) |
|
| 952 return; |
|
| 953 js = b->account->gc->proto_data; |
|
| 954 jb = jabber_buddy_find(js, b->name, FALSE); |
|
| 955 |
|
| 956 if(!GAIM_BUDDY_IS_ONLINE(b)) { |
|
| 957 if(jb && jb->error_msg) |
|
| 958 *nw = "error"; |
|
| 959 |
|
| 960 if(jb && (jb->subscription & JABBER_SUB_PENDING || |
|
| 961 !(jb->subscription & JABBER_SUB_TO))) |
|
| 962 *se = "notauthorized"; |
|
| 963 else |
|
| 964 *se = "offline"; |
|
| 965 } else { |
|
| 966 GaimStatusType *status_type = gaim_status_get_type(gaim_presence_get_active_status(gaim_buddy_get_presence(b))); |
|
| 967 GaimStatusPrimitive primitive = gaim_status_type_get_primitive(status_type); |
|
| 968 |
|
| 969 if(primitive > GAIM_STATUS_AVAILABLE) { |
|
| 970 *se = gaim_status_type_get_id(status_type); |
|
| 971 } |
|
| 972 } |
|
| 973 } |
|
| 974 |
|
| 975 static char *jabber_status_text(GaimBuddy *b) |
|
| 976 { |
|
| 977 JabberBuddy *jb = jabber_buddy_find(b->account->gc->proto_data, b->name, |
|
| 978 FALSE); |
|
| 979 char *ret = NULL; |
|
| 980 |
|
| 981 if(jb && !GAIM_BUDDY_IS_ONLINE(b) && (jb->subscription & JABBER_SUB_PENDING || !(jb->subscription & JABBER_SUB_TO))) { |
|
| 982 ret = g_strdup(_("Not Authorized")); |
|
| 983 } else if(jb && !GAIM_BUDDY_IS_ONLINE(b) && jb->error_msg) { |
|
| 984 ret = g_strdup(jb->error_msg); |
|
| 985 } else { |
|
| 986 char *stripped; |
|
| 987 |
|
| 988 if(!(stripped = gaim_markup_strip_html(jabber_buddy_get_status_msg(jb)))) { |
|
| 989 GaimStatus *status = gaim_presence_get_active_status(gaim_buddy_get_presence(b)); |
|
| 990 |
|
| 991 if(!gaim_status_is_available(status)) |
|
| 992 stripped = g_strdup(gaim_status_get_name(status)); |
|
| 993 } |
|
| 994 |
|
| 995 if(stripped) { |
|
| 996 ret = g_markup_escape_text(stripped, -1); |
|
| 997 g_free(stripped); |
|
| 998 } |
|
| 999 } |
|
| 1000 |
|
| 1001 return ret; |
|
| 1002 } |
|
| 1003 |
|
| 1004 static void jabber_tooltip_text(GaimBuddy *b, GString *str, gboolean full) |
|
| 1005 { |
|
| 1006 JabberBuddy *jb; |
|
| 1007 |
|
| 1008 g_return_if_fail(b != NULL); |
|
| 1009 g_return_if_fail(b->account != NULL); |
|
| 1010 g_return_if_fail(b->account->gc != NULL); |
|
| 1011 g_return_if_fail(b->account->gc->proto_data != NULL); |
|
| 1012 |
|
| 1013 jb = jabber_buddy_find(b->account->gc->proto_data, b->name, |
|
| 1014 FALSE); |
|
| 1015 |
|
| 1016 if(jb) { |
|
| 1017 JabberBuddyResource *jbr = NULL; |
|
| 1018 const char *sub; |
|
| 1019 GList *l; |
|
| 1020 |
|
| 1021 if (full) { |
|
| 1022 if(jb->subscription & JABBER_SUB_FROM) { |
|
| 1023 if(jb->subscription & JABBER_SUB_TO) |
|
| 1024 sub = _("Both"); |
|
| 1025 else if(jb->subscription & JABBER_SUB_PENDING) |
|
| 1026 sub = _("From (To pending)"); |
|
| 1027 else |
|
| 1028 sub = _("From"); |
|
| 1029 } else { |
|
| 1030 if(jb->subscription & JABBER_SUB_TO) |
|
| 1031 sub = _("To"); |
|
| 1032 else if(jb->subscription & JABBER_SUB_PENDING) |
|
| 1033 sub = _("None (To pending)"); |
|
| 1034 else |
|
| 1035 sub = _("None"); |
|
| 1036 } |
|
| 1037 g_string_append_printf(str, "\n<b>%s:</b> %s", _("Subscription"), sub); |
|
| 1038 } |
|
| 1039 |
|
| 1040 for(l=jb->resources; l; l = l->next) { |
|
| 1041 char *text = NULL; |
|
| 1042 char *res = NULL; |
|
| 1043 const char *state; |
|
| 1044 |
|
| 1045 jbr = l->data; |
|
| 1046 |
|
| 1047 if(jbr->status) { |
|
| 1048 char *tmp; |
|
| 1049 text = gaim_strreplace(jbr->status, "\n", "<br />\n"); |
|
| 1050 tmp = gaim_markup_strip_html(text); |
|
| 1051 g_free(text); |
|
| 1052 text = g_markup_escape_text(tmp, -1); |
|
| 1053 g_free(tmp); |
|
| 1054 } |
|
| 1055 |
|
| 1056 if(jbr->name) |
|
| 1057 res = g_strdup_printf(" (%s)", jbr->name); |
|
| 1058 |
|
| 1059 state = jabber_buddy_state_get_name(jbr->state); |
|
| 1060 if (text != NULL && !gaim_utf8_strcasecmp(state, text)) { |
|
| 1061 g_free(text); |
|
| 1062 text = NULL; |
|
| 1063 } |
|
| 1064 |
|
| 1065 g_string_append_printf(str, "\n<b>%s%s:</b> %s%s%s", |
|
| 1066 _("Status"), |
|
| 1067 res ? res : "", |
|
| 1068 state, |
|
| 1069 text ? ": " : "", |
|
| 1070 text ? text : ""); |
|
| 1071 |
|
| 1072 g_free(text); |
|
| 1073 g_free(res); |
|
| 1074 } |
|
| 1075 |
|
| 1076 if(!GAIM_BUDDY_IS_ONLINE(b) && jb->error_msg) { |
|
| 1077 g_string_append_printf(str, "\n<b>%s:</b> %s", |
|
| 1078 _("Error"), jb->error_msg); |
|
| 1079 } |
|
| 1080 } |
|
| 1081 } |
|
| 1082 |
|
| 1083 static GList *jabber_status_types(GaimAccount *account) |
|
| 1084 { |
|
| 1085 GaimStatusType *type; |
|
| 1086 GList *types = NULL; |
|
| 1087 GaimValue *priority_value; |
|
| 1088 |
|
| 1089 priority_value = gaim_value_new(GAIM_TYPE_INT); |
|
| 1090 gaim_value_set_int(priority_value, 1); |
|
| 1091 type = gaim_status_type_new_with_attrs(GAIM_STATUS_AVAILABLE, |
|
| 1092 jabber_buddy_state_get_status_id(JABBER_BUDDY_STATE_ONLINE), |
|
| 1093 NULL, TRUE, TRUE, FALSE, |
|
| 1094 "priority", _("Priority"), priority_value, |
|
| 1095 "message", _("Message"), gaim_value_new(GAIM_TYPE_STRING), |
|
| 1096 NULL); |
|
| 1097 types = g_list_append(types, type); |
|
| 1098 |
|
| 1099 priority_value = gaim_value_new(GAIM_TYPE_INT); |
|
| 1100 gaim_value_set_int(priority_value, 1); |
|
| 1101 type = gaim_status_type_new_with_attrs(GAIM_STATUS_AVAILABLE, |
|
| 1102 jabber_buddy_state_get_status_id(JABBER_BUDDY_STATE_CHAT), |
|
| 1103 _("Chatty"), TRUE, TRUE, FALSE, |
|
| 1104 "priority", _("Priority"), priority_value, |
|
| 1105 "message", _("Message"), gaim_value_new(GAIM_TYPE_STRING), |
|
| 1106 NULL); |
|
| 1107 types = g_list_append(types, type); |
|
| 1108 |
|
| 1109 priority_value = gaim_value_new(GAIM_TYPE_INT); |
|
| 1110 gaim_value_set_int(priority_value, 0); |
|
| 1111 type = gaim_status_type_new_with_attrs(GAIM_STATUS_AWAY, |
|
| 1112 jabber_buddy_state_get_status_id(JABBER_BUDDY_STATE_AWAY), |
|
| 1113 NULL, TRUE, TRUE, FALSE, |
|
| 1114 "priority", _("Priority"), priority_value, |
|
| 1115 "message", _("Message"), gaim_value_new(GAIM_TYPE_STRING), |
|
| 1116 NULL); |
|
| 1117 types = g_list_append(types, type); |
|
| 1118 |
|
| 1119 priority_value = gaim_value_new(GAIM_TYPE_INT); |
|
| 1120 gaim_value_set_int(priority_value, 0); |
|
| 1121 type = gaim_status_type_new_with_attrs(GAIM_STATUS_EXTENDED_AWAY, |
|
| 1122 jabber_buddy_state_get_status_id(JABBER_BUDDY_STATE_XA), |
|
| 1123 NULL, TRUE, TRUE, FALSE, |
|
| 1124 "priority", _("Priority"), priority_value, |
|
| 1125 "message", _("Message"), gaim_value_new(GAIM_TYPE_STRING), |
|
| 1126 NULL); |
|
| 1127 types = g_list_append(types, type); |
|
| 1128 |
|
| 1129 priority_value = gaim_value_new(GAIM_TYPE_INT); |
|
| 1130 gaim_value_set_int(priority_value, 0); |
|
| 1131 type = gaim_status_type_new_with_attrs(GAIM_STATUS_UNAVAILABLE, |
|
| 1132 jabber_buddy_state_get_status_id(JABBER_BUDDY_STATE_DND), |
|
| 1133 _("Do Not Disturb"), TRUE, TRUE, FALSE, |
|
| 1134 "priority", _("Priority"), priority_value, |
|
| 1135 "message", _("Message"), gaim_value_new(GAIM_TYPE_STRING), |
|
| 1136 NULL); |
|
| 1137 types = g_list_append(types, type); |
|
| 1138 |
|
| 1139 /* |
|
| 1140 if(js->protocol_version == JABBER_PROTO_0_9) |
|
| 1141 m = g_list_append(m, _("Invisible")); |
|
| 1142 */ |
|
| 1143 |
|
| 1144 type = gaim_status_type_new_with_attrs(GAIM_STATUS_OFFLINE, |
|
| 1145 jabber_buddy_state_get_status_id(JABBER_BUDDY_STATE_UNAVAILABLE), |
|
| 1146 NULL, FALSE, TRUE, FALSE, |
|
| 1147 "message", _("Message"), gaim_value_new(GAIM_TYPE_STRING), |
|
| 1148 NULL); |
|
| 1149 types = g_list_append(types, type); |
|
| 1150 |
|
| 1151 return types; |
|
| 1152 } |
|
| 1153 |
|
| 1154 static void |
|
| 1155 jabber_password_change_result_cb(JabberStream *js, xmlnode *packet, |
|
| 1156 gpointer data) |
|
| 1157 { |
|
| 1158 const char *type; |
|
| 1159 |
|
| 1160 type = xmlnode_get_attrib(packet, "type"); |
|
| 1161 |
|
| 1162 if(type && !strcmp(type, "result")) { |
|
| 1163 gaim_notify_info(js->gc, _("Password Changed"), _("Password Changed"), |
|
| 1164 _("Your password has been changed.")); |
|
| 1165 } else { |
|
| 1166 char *msg = jabber_parse_error(js, packet); |
|
| 1167 |
|
| 1168 gaim_notify_error(js->gc, _("Error changing password"), |
|
| 1169 _("Error changing password"), msg); |
|
| 1170 g_free(msg); |
|
| 1171 } |
|
| 1172 } |
|
| 1173 |
|
| 1174 static void jabber_password_change_cb(JabberStream *js, |
|
| 1175 GaimRequestFields *fields) |
|
| 1176 { |
|
| 1177 const char *p1, *p2; |
|
| 1178 JabberIq *iq; |
|
| 1179 xmlnode *query, *y; |
|
| 1180 |
|
| 1181 p1 = gaim_request_fields_get_string(fields, "password1"); |
|
| 1182 p2 = gaim_request_fields_get_string(fields, "password2"); |
|
| 1183 |
|
| 1184 if(strcmp(p1, p2)) { |
|
| 1185 gaim_notify_error(js->gc, NULL, _("New passwords do not match."), NULL); |
|
| 1186 return; |
|
| 1187 } |
|
| 1188 |
|
| 1189 iq = jabber_iq_new_query(js, JABBER_IQ_SET, "jabber:iq:register"); |
|
| 1190 |
|
| 1191 xmlnode_set_attrib(iq->node, "to", js->user->domain); |
|
| 1192 |
|
| 1193 query = xmlnode_get_child(iq->node, "query"); |
|
| 1194 |
|
| 1195 y = xmlnode_new_child(query, "username"); |
|
| 1196 xmlnode_insert_data(y, js->user->node, -1); |
|
| 1197 y = xmlnode_new_child(query, "password"); |
|
| 1198 xmlnode_insert_data(y, p1, -1); |
|
| 1199 |
|
| 1200 jabber_iq_set_callback(iq, jabber_password_change_result_cb, NULL); |
|
| 1201 |
|
| 1202 jabber_iq_send(iq); |
|
| 1203 |
|
| 1204 gaim_account_set_password(js->gc->account, p1); |
|
| 1205 } |
|
| 1206 |
|
| 1207 static void jabber_password_change(GaimPluginAction *action) |
|
| 1208 { |
|
| 1209 |
|
| 1210 GaimConnection *gc = (GaimConnection *) action->context; |
|
| 1211 JabberStream *js = gc->proto_data; |
|
| 1212 GaimRequestFields *fields; |
|
| 1213 GaimRequestFieldGroup *group; |
|
| 1214 GaimRequestField *field; |
|
| 1215 |
|
| 1216 fields = gaim_request_fields_new(); |
|
| 1217 group = gaim_request_field_group_new(NULL); |
|
| 1218 gaim_request_fields_add_group(fields, group); |
|
| 1219 |
|
| 1220 field = gaim_request_field_string_new("password1", _("Password"), |
|
| 1221 "", FALSE); |
|
| 1222 gaim_request_field_string_set_masked(field, TRUE); |
|
| 1223 gaim_request_field_group_add_field(group, field); |
|
| 1224 |
|
| 1225 field = gaim_request_field_string_new("password2", _("Password (again)"), |
|
| 1226 "", FALSE); |
|
| 1227 gaim_request_field_string_set_masked(field, TRUE); |
|
| 1228 gaim_request_field_group_add_field(group, field); |
|
| 1229 |
|
| 1230 gaim_request_fields(js->gc, _("Change Jabber Password"), |
|
| 1231 _("Change Jabber Password"), _("Please enter your new password"), |
|
| 1232 fields, _("OK"), G_CALLBACK(jabber_password_change_cb), |
|
| 1233 _("Cancel"), NULL, js); |
|
| 1234 } |
|
| 1235 |
|
| 1236 static GList *jabber_actions(GaimPlugin *plugin, gpointer context) |
|
| 1237 { |
|
| 1238 GList *m = NULL; |
|
| 1239 GaimPluginAction *act; |
|
| 1240 |
|
| 1241 act = gaim_plugin_action_new(_("Set User Info..."), |
|
| 1242 jabber_setup_set_info); |
|
| 1243 m = g_list_append(m, act); |
|
| 1244 |
|
| 1245 /* if (js->protocol_options & CHANGE_PASSWORD) { */ |
|
| 1246 act = gaim_plugin_action_new(_("Change Password..."), |
|
| 1247 jabber_password_change); |
|
| 1248 m = g_list_append(m, act); |
|
| 1249 /* } */ |
|
| 1250 |
|
| 1251 act = gaim_plugin_action_new(_("Search for Users..."), |
|
| 1252 jabber_user_search_begin); |
|
| 1253 m = g_list_append(m, act); |
|
| 1254 |
|
| 1255 return m; |
|
| 1256 } |
|
| 1257 |
|
| 1258 static GaimChat *jabber_find_blist_chat(GaimAccount *account, const char *name) |
|
| 1259 { |
|
| 1260 GaimBlistNode *gnode, *cnode; |
|
| 1261 JabberID *jid; |
|
| 1262 |
|
| 1263 if(!(jid = jabber_id_new(name))) |
|
| 1264 return NULL; |
|
| 1265 |
|
| 1266 for(gnode = gaim_get_blist()->root; gnode; gnode = gnode->next) { |
|
| 1267 for(cnode = gnode->child; cnode; cnode = cnode->next) { |
|
| 1268 GaimChat *chat = (GaimChat*)cnode; |
|
| 1269 const char *room, *server; |
|
| 1270 if(!GAIM_BLIST_NODE_IS_CHAT(cnode)) |
|
| 1271 continue; |
|
| 1272 |
|
| 1273 if(chat->account != account) |
|
| 1274 continue; |
|
| 1275 |
|
| 1276 if(!(room = g_hash_table_lookup(chat->components, "room"))) |
|
| 1277 continue; |
|
| 1278 if(!(server = g_hash_table_lookup(chat->components, "server"))) |
|
| 1279 continue; |
|
| 1280 |
|
| 1281 if(jid->node && jid->domain && |
|
| 1282 !g_utf8_collate(room, jid->node) && !g_utf8_collate(server, jid->domain)) { |
|
| 1283 jabber_id_free(jid); |
|
| 1284 return chat; |
|
| 1285 } |
|
| 1286 } |
|
| 1287 } |
|
| 1288 jabber_id_free(jid); |
|
| 1289 return NULL; |
|
| 1290 } |
|
| 1291 |
|
| 1292 static void jabber_convo_closed(GaimConnection *gc, const char *who) |
|
| 1293 { |
|
| 1294 JabberStream *js = gc->proto_data; |
|
| 1295 JabberID *jid; |
|
| 1296 JabberBuddy *jb; |
|
| 1297 JabberBuddyResource *jbr; |
|
| 1298 |
|
| 1299 if(!(jid = jabber_id_new(who))) |
|
| 1300 return; |
|
| 1301 |
|
| 1302 if((jb = jabber_buddy_find(js, who, TRUE)) && |
|
| 1303 (jbr = jabber_buddy_find_resource(jb, jid->resource))) { |
|
| 1304 if(jbr->thread_id) { |
|
| 1305 g_free(jbr->thread_id); |
|
| 1306 jbr->thread_id = NULL; |
|
| 1307 } |
|
| 1308 } |
|
| 1309 |
|
| 1310 jabber_id_free(jid); |
|
| 1311 } |
|
| 1312 |
|
| 1313 |
|
| 1314 char *jabber_parse_error(JabberStream *js, xmlnode *packet) |
|
| 1315 { |
|
| 1316 xmlnode *error; |
|
| 1317 const char *code = NULL, *text = NULL; |
|
| 1318 const char *xmlns = xmlnode_get_attrib(packet, "xmlns"); |
|
| 1319 char *cdata = NULL; |
|
| 1320 |
|
| 1321 if((error = xmlnode_get_child(packet, "error"))) { |
|
| 1322 cdata = xmlnode_get_data(error); |
|
| 1323 code = xmlnode_get_attrib(error, "code"); |
|
| 1324 |
|
| 1325 /* Stanza errors */ |
|
| 1326 if(xmlnode_get_child(error, "bad-request")) { |
|
| 1327 text = _("Bad Request"); |
|
| 1328 } else if(xmlnode_get_child(error, "conflict")) { |
|
| 1329 text = _("Conflict"); |
|
| 1330 } else if(xmlnode_get_child(error, "feature-not-implemented")) { |
|
| 1331 text = _("Feature Not Implemented"); |
|
| 1332 } else if(xmlnode_get_child(error, "forbidden")) { |
|
| 1333 text = _("Forbidden"); |
|
| 1334 } else if(xmlnode_get_child(error, "gone")) { |
|
| 1335 text = _("Gone"); |
|
| 1336 } else if(xmlnode_get_child(error, "internal-server-error")) { |
|
| 1337 text = _("Internal Server Error"); |
|
| 1338 } else if(xmlnode_get_child(error, "item-not-found")) { |
|
| 1339 text = _("Item Not Found"); |
|
| 1340 } else if(xmlnode_get_child(error, "jid-malformed")) { |
|
| 1341 text = _("Malformed Jabber ID"); |
|
| 1342 } else if(xmlnode_get_child(error, "not-acceptable")) { |
|
| 1343 text = _("Not Acceptable"); |
|
| 1344 } else if(xmlnode_get_child(error, "not-allowed")) { |
|
| 1345 text = _("Not Allowed"); |
|
| 1346 } else if(xmlnode_get_child(error, "not-authorized")) { |
|
| 1347 text = _("Not Authorized"); |
|
| 1348 } else if(xmlnode_get_child(error, "payment-required")) { |
|
| 1349 text = _("Payment Required"); |
|
| 1350 } else if(xmlnode_get_child(error, "recipient-unavailable")) { |
|
| 1351 text = _("Recipient Unavailable"); |
|
| 1352 } else if(xmlnode_get_child(error, "redirect")) { |
|
| 1353 /* XXX */ |
|
| 1354 } else if(xmlnode_get_child(error, "registration-required")) { |
|
| 1355 text = _("Registration Required"); |
|
| 1356 } else if(xmlnode_get_child(error, "remote-server-not-found")) { |
|
| 1357 text = _("Remote Server Not Found"); |
|
| 1358 } else if(xmlnode_get_child(error, "remote-server-timeout")) { |
|
| 1359 text = _("Remote Server Timeout"); |
|
| 1360 } else if(xmlnode_get_child(error, "resource-constraint")) { |
|
| 1361 text = _("Server Overloaded"); |
|
| 1362 } else if(xmlnode_get_child(error, "service-unavailable")) { |
|
| 1363 text = _("Service Unavailable"); |
|
| 1364 } else if(xmlnode_get_child(error, "subscription-required")) { |
|
| 1365 text = _("Subscription Required"); |
|
| 1366 } else if(xmlnode_get_child(error, "unexpected-request")) { |
|
| 1367 text = _("Unexpected Request"); |
|
| 1368 } else if(xmlnode_get_child(error, "undefined-condition")) { |
|
| 1369 text = _("Unknown Error"); |
|
| 1370 } |
|
| 1371 } else if(xmlns && !strcmp(xmlns, "urn:ietf:params:xml:ns:xmpp-sasl")) { |
|
| 1372 if(xmlnode_get_child(packet, "aborted")) { |
|
| 1373 js->gc->wants_to_die = TRUE; |
|
| 1374 text = _("Authorization Aborted"); |
|
| 1375 } else if(xmlnode_get_child(error, "incorrect-encoding")) { |
|
| 1376 text = _("Incorrect encoding in authorization"); |
|
| 1377 } else if(xmlnode_get_child(error, "invalid-authzid")) { |
|
| 1378 js->gc->wants_to_die = TRUE; |
|
| 1379 text = _("Invalid authzid"); |
|
| 1380 } else if(xmlnode_get_child(error, "invalid-mechanism")) { |
|
| 1381 js->gc->wants_to_die = TRUE; |
|
| 1382 text = _("Invalid Authorization Mechanism"); |
|
| 1383 } else if(xmlnode_get_child(error, "mechanism-too-weak")) { |
|
| 1384 js->gc->wants_to_die = TRUE; |
|
| 1385 text = _("Authorization mechanism too weak"); |
|
| 1386 } else if(xmlnode_get_child(error, "not-authorized")) { |
|
| 1387 js->gc->wants_to_die = TRUE; |
|
| 1388 text = _("Not Authorized"); |
|
| 1389 } else if(xmlnode_get_child(error, "temporary-auth-failure")) { |
|
| 1390 text = _("Temporary Authentication Failure"); |
|
| 1391 } else { |
|
| 1392 js->gc->wants_to_die = TRUE; |
|
| 1393 text = _("Authentication Failure"); |
|
| 1394 } |
|
| 1395 } else if(!strcmp(packet->name, "stream:error")) { |
|
| 1396 if(xmlnode_get_child(packet, "bad-format")) { |
|
| 1397 text = _("Bad Format"); |
|
| 1398 } else if(xmlnode_get_child(packet, "bad-namespace-prefix")) { |
|
| 1399 text = _("Bad Namespace Prefix"); |
|
| 1400 } else if(xmlnode_get_child(packet, "conflict")) { |
|
| 1401 js->gc->wants_to_die = TRUE; |
|
| 1402 text = _("Resource Conflict"); |
|
| 1403 } else if(xmlnode_get_child(packet, "connection-timeout")) { |
|
| 1404 text = _("Connection Timeout"); |
|
| 1405 } else if(xmlnode_get_child(packet, "host-gone")) { |
|
| 1406 text = _("Host Gone"); |
|
| 1407 } else if(xmlnode_get_child(packet, "host-unknown")) { |
|
| 1408 text = _("Host Unknown"); |
|
| 1409 } else if(xmlnode_get_child(packet, "improper-addressing")) { |
|
| 1410 text = _("Improper Addressing"); |
|
| 1411 } else if(xmlnode_get_child(packet, "internal-server-error")) { |
|
| 1412 text = _("Internal Server Error"); |
|
| 1413 } else if(xmlnode_get_child(packet, "invalid-id")) { |
|
| 1414 text = _("Invalid ID"); |
|
| 1415 } else if(xmlnode_get_child(packet, "invalid-namespace")) { |
|
| 1416 text = _("Invalid Namespace"); |
|
| 1417 } else if(xmlnode_get_child(packet, "invalid-xml")) { |
|
| 1418 text = _("Invalid XML"); |
|
| 1419 } else if(xmlnode_get_child(packet, "nonmatching-hosts")) { |
|
| 1420 text = _("Non-matching Hosts"); |
|
| 1421 } else if(xmlnode_get_child(packet, "not-authorized")) { |
|
| 1422 text = _("Not Authorized"); |
|
| 1423 } else if(xmlnode_get_child(packet, "policy-violation")) { |
|
| 1424 text = _("Policy Violation"); |
|
| 1425 } else if(xmlnode_get_child(packet, "remote-connection-failed")) { |
|
| 1426 text = _("Remote Connection Failed"); |
|
| 1427 } else if(xmlnode_get_child(packet, "resource-constraint")) { |
|
| 1428 text = _("Resource Constraint"); |
|
| 1429 } else if(xmlnode_get_child(packet, "restricted-xml")) { |
|
| 1430 text = _("Restricted XML"); |
|
| 1431 } else if(xmlnode_get_child(packet, "see-other-host")) { |
|
| 1432 text = _("See Other Host"); |
|
| 1433 } else if(xmlnode_get_child(packet, "system-shutdown")) { |
|
| 1434 text = _("System Shutdown"); |
|
| 1435 } else if(xmlnode_get_child(packet, "undefined-condition")) { |
|
| 1436 text = _("Undefined Condition"); |
|
| 1437 } else if(xmlnode_get_child(packet, "unsupported-encoding")) { |
|
| 1438 text = _("Unsupported Encoding"); |
|
| 1439 } else if(xmlnode_get_child(packet, "unsupported-stanza-type")) { |
|
| 1440 text = _("Unsupported Stanza Type"); |
|
| 1441 } else if(xmlnode_get_child(packet, "unsupported-version")) { |
|
| 1442 text = _("Unsupported Version"); |
|
| 1443 } else if(xmlnode_get_child(packet, "xml-not-well-formed")) { |
|
| 1444 text = _("XML Not Well Formed"); |
|
| 1445 } else { |
|
| 1446 text = _("Stream Error"); |
|
| 1447 } |
|
| 1448 } |
|
| 1449 |
|
| 1450 if(text || cdata) { |
|
| 1451 char *ret = g_strdup_printf("%s%s%s", code ? code : "", |
|
| 1452 code ? ": " : "", text ? text : cdata); |
|
| 1453 g_free(cdata); |
|
| 1454 return ret; |
|
| 1455 } else { |
|
| 1456 return NULL; |
|
| 1457 } |
|
| 1458 } |
|
| 1459 |
|
| 1460 static GaimCmdRet jabber_cmd_chat_config(GaimConversation *conv, |
|
| 1461 const char *cmd, char **args, char **error, void *data) |
|
| 1462 { |
|
| 1463 JabberChat *chat = jabber_chat_find_by_conv(conv); |
|
| 1464 jabber_chat_request_room_configure(chat); |
|
| 1465 return GAIM_CMD_RET_OK; |
|
| 1466 } |
|
| 1467 |
|
| 1468 static GaimCmdRet jabber_cmd_chat_register(GaimConversation *conv, |
|
| 1469 const char *cmd, char **args, char **error, void *data) |
|
| 1470 { |
|
| 1471 JabberChat *chat = jabber_chat_find_by_conv(conv); |
|
| 1472 jabber_chat_register(chat); |
|
| 1473 return GAIM_CMD_RET_OK; |
|
| 1474 } |
|
| 1475 |
|
| 1476 static GaimCmdRet jabber_cmd_chat_topic(GaimConversation *conv, |
|
| 1477 const char *cmd, char **args, char **error, void *data) |
|
| 1478 { |
|
| 1479 JabberChat *chat = jabber_chat_find_by_conv(conv); |
|
| 1480 jabber_chat_change_topic(chat, args ? args[0] : NULL); |
|
| 1481 return GAIM_CMD_RET_OK; |
|
| 1482 } |
|
| 1483 |
|
| 1484 static GaimCmdRet jabber_cmd_chat_nick(GaimConversation *conv, |
|
| 1485 const char *cmd, char **args, char **error, void *data) |
|
| 1486 { |
|
| 1487 JabberChat *chat = jabber_chat_find_by_conv(conv); |
|
| 1488 |
|
| 1489 if(!args || !args[0]) |
|
| 1490 return GAIM_CMD_RET_FAILED; |
|
| 1491 |
|
| 1492 jabber_chat_change_nick(chat, args[0]); |
|
| 1493 return GAIM_CMD_RET_OK; |
|
| 1494 } |
|
| 1495 |
|
| 1496 static GaimCmdRet jabber_cmd_chat_part(GaimConversation *conv, |
|
| 1497 const char *cmd, char **args, char **error, void *data) |
|
| 1498 { |
|
| 1499 JabberChat *chat = jabber_chat_find_by_conv(conv); |
|
| 1500 jabber_chat_part(chat, args ? args[0] : NULL); |
|
| 1501 return GAIM_CMD_RET_OK; |
|
| 1502 } |
|
| 1503 |
|
| 1504 static GaimCmdRet jabber_cmd_chat_ban(GaimConversation *conv, |
|
| 1505 const char *cmd, char **args, char **error, void *data) |
|
| 1506 { |
|
| 1507 JabberChat *chat = jabber_chat_find_by_conv(conv); |
|
| 1508 |
|
| 1509 if(!args || !args[0]) |
|
| 1510 return GAIM_CMD_RET_FAILED; |
|
| 1511 |
|
| 1512 if(!jabber_chat_ban_user(chat, args[0], args[1])) { |
|
| 1513 *error = g_strdup_printf(_("Unable to ban user %s"), args[0]); |
|
| 1514 return GAIM_CMD_RET_FAILED; |
|
| 1515 } |
|
| 1516 |
|
| 1517 return GAIM_CMD_RET_OK; |
|
| 1518 } |
|
| 1519 |
|
| 1520 static GaimCmdRet jabber_cmd_chat_affiliate(GaimConversation *conv, |
|
| 1521 const char *cmd, char **args, char **error, void *data) |
|
| 1522 { |
|
| 1523 JabberChat *chat = jabber_chat_find_by_conv(conv); |
|
| 1524 |
|
| 1525 if (!args || !args[0] || !args[1]) |
|
| 1526 return GAIM_CMD_RET_FAILED; |
|
| 1527 |
|
| 1528 if ( |
|
| 1529 strcmp(args[1], "owner") != 0 && |
|
| 1530 strcmp(args[1], "admin") != 0 && |
|
| 1531 strcmp(args[1], "member") != 0 && |
|
| 1532 strcmp(args[1], "outcast") != 0 && |
|
| 1533 strcmp(args[1], "none") != 0 |
|
| 1534 ) { |
|
| 1535 *error = g_strdup_printf(_("Unknown affiliation: \"%s\""), args[1]); |
|
| 1536 return GAIM_CMD_RET_FAILED; |
|
| 1537 } |
|
| 1538 |
|
| 1539 if (!jabber_chat_affiliate_user(chat, args[0], args[1])) { |
|
| 1540 *error = g_strdup_printf(_("Unable to affiliate user %s as \"%s\""), args[0], args[1]); |
|
| 1541 return GAIM_CMD_RET_FAILED; |
|
| 1542 } |
|
| 1543 |
|
| 1544 return GAIM_CMD_RET_OK; |
|
| 1545 } |
|
| 1546 |
|
| 1547 static GaimCmdRet jabber_cmd_chat_invite(GaimConversation *conv, |
|
| 1548 const char *cmd, char **args, char **error, void *data) |
|
| 1549 { |
|
| 1550 if(!args || !args[0]) |
|
| 1551 return GAIM_CMD_RET_FAILED; |
|
| 1552 |
|
| 1553 jabber_chat_invite(gaim_conversation_get_gc(conv), |
|
| 1554 gaim_conv_chat_get_id(GAIM_CONV_CHAT(conv)), args[1] ? args[1] : "", |
|
| 1555 args[0]); |
|
| 1556 |
|
| 1557 return GAIM_CMD_RET_OK; |
|
| 1558 } |
|
| 1559 |
|
| 1560 static GaimCmdRet jabber_cmd_chat_join(GaimConversation *conv, |
|
| 1561 const char *cmd, char **args, char **error, void *data) |
|
| 1562 { |
|
| 1563 JabberChat *chat = jabber_chat_find_by_conv(conv); |
|
| 1564 GHashTable *components; |
|
| 1565 |
|
| 1566 if(!args || !args[0]) |
|
| 1567 return GAIM_CMD_RET_FAILED; |
|
| 1568 |
|
| 1569 components = g_hash_table_new_full(g_str_hash, g_str_equal, NULL, NULL); |
|
| 1570 |
|
| 1571 g_hash_table_replace(components, "room", args[0]); |
|
| 1572 g_hash_table_replace(components, "server", chat->server); |
|
| 1573 g_hash_table_replace(components, "handle", chat->handle); |
|
| 1574 if(args[1]) |
|
| 1575 g_hash_table_replace(components, "password", args[1]); |
|
| 1576 |
|
| 1577 jabber_chat_join(gaim_conversation_get_gc(conv), components); |
|
| 1578 |
|
| 1579 g_hash_table_destroy(components); |
|
| 1580 return GAIM_CMD_RET_OK; |
|
| 1581 } |
|
| 1582 |
|
| 1583 static GaimCmdRet jabber_cmd_chat_kick(GaimConversation *conv, |
|
| 1584 const char *cmd, char **args, char **error, void *data) |
|
| 1585 { |
|
| 1586 JabberChat *chat = jabber_chat_find_by_conv(conv); |
|
| 1587 |
|
| 1588 if(!args || !args[0]) |
|
| 1589 return GAIM_CMD_RET_FAILED; |
|
| 1590 |
|
| 1591 if(!jabber_chat_kick_user(chat, args[0], args[1])) { |
|
| 1592 *error = g_strdup_printf(_("Unable to kick user %s"), args[0]); |
|
| 1593 return GAIM_CMD_RET_FAILED; |
|
| 1594 } |
|
| 1595 |
|
| 1596 return GAIM_CMD_RET_OK; |
|
| 1597 } |
|
| 1598 |
|
| 1599 static GaimCmdRet jabber_cmd_chat_msg(GaimConversation *conv, |
|
| 1600 const char *cmd, char **args, char **error, void *data) |
|
| 1601 { |
|
| 1602 JabberChat *chat = jabber_chat_find_by_conv(conv); |
|
| 1603 char *who; |
|
| 1604 |
|
| 1605 who = g_strdup_printf("%s@%s/%s", chat->room, chat->server, args[0]); |
|
| 1606 |
|
| 1607 jabber_message_send_im(gaim_conversation_get_gc(conv), who, args[1], 0); |
|
| 1608 |
|
| 1609 g_free(who); |
|
| 1610 return GAIM_CMD_RET_OK; |
|
| 1611 } |
|
| 1612 |
|
| 1613 static gboolean jabber_offline_message(const GaimBuddy *buddy) |
|
| 1614 { |
|
| 1615 return TRUE; |
|
| 1616 } |
|
| 1617 |
|
| 1618 static void jabber_register_commands(void) |
|
| 1619 { |
|
| 1620 gaim_cmd_register("config", "", GAIM_CMD_P_PRPL, |
|
| 1621 GAIM_CMD_FLAG_CHAT | GAIM_CMD_FLAG_PRPL_ONLY, |
|
| 1622 "prpl-jabber", jabber_cmd_chat_config, |
|
| 1623 _("config: Configure a chat room."), NULL); |
|
| 1624 gaim_cmd_register("configure", "", GAIM_CMD_P_PRPL, |
|
| 1625 GAIM_CMD_FLAG_CHAT | GAIM_CMD_FLAG_PRPL_ONLY, |
|
| 1626 "prpl-jabber", jabber_cmd_chat_config, |
|
| 1627 _("configure: Configure a chat room."), NULL); |
|
| 1628 gaim_cmd_register("nick", "s", GAIM_CMD_P_PRPL, |
|
| 1629 GAIM_CMD_FLAG_CHAT | GAIM_CMD_FLAG_PRPL_ONLY, |
|
| 1630 "prpl-jabber", jabber_cmd_chat_nick, |
|
| 1631 _("nick <new nickname>: Change your nickname."), |
|
| 1632 NULL); |
|
| 1633 gaim_cmd_register("part", "s", GAIM_CMD_P_PRPL, |
|
| 1634 GAIM_CMD_FLAG_CHAT | GAIM_CMD_FLAG_PRPL_ONLY | |
|
| 1635 GAIM_CMD_FLAG_ALLOW_WRONG_ARGS, "prpl-jabber", |
|
| 1636 jabber_cmd_chat_part, _("part [room]: Leave the room."), |
|
| 1637 NULL); |
|
| 1638 gaim_cmd_register("register", "", GAIM_CMD_P_PRPL, |
|
| 1639 GAIM_CMD_FLAG_CHAT | GAIM_CMD_FLAG_PRPL_ONLY, |
|
| 1640 "prpl-jabber", jabber_cmd_chat_register, |
|
| 1641 _("register: Register with a chat room."), NULL); |
|
| 1642 /* XXX: there needs to be a core /topic cmd, methinks */ |
|
| 1643 gaim_cmd_register("topic", "s", GAIM_CMD_P_PRPL, |
|
| 1644 GAIM_CMD_FLAG_CHAT | GAIM_CMD_FLAG_PRPL_ONLY | |
|
| 1645 GAIM_CMD_FLAG_ALLOW_WRONG_ARGS, "prpl-jabber", |
|
| 1646 jabber_cmd_chat_topic, |
|
| 1647 _("topic [new topic]: View or change the topic."), |
|
| 1648 NULL); |
|
| 1649 gaim_cmd_register("ban", "ws", GAIM_CMD_P_PRPL, |
|
| 1650 GAIM_CMD_FLAG_CHAT | GAIM_CMD_FLAG_PRPL_ONLY | |
|
| 1651 GAIM_CMD_FLAG_ALLOW_WRONG_ARGS, "prpl-jabber", |
|
| 1652 jabber_cmd_chat_ban, |
|
| 1653 _("ban <user> [room]: Ban a user from the room."), |
|
| 1654 NULL); |
|
| 1655 gaim_cmd_register("affiliate", "ws", GAIM_CMD_P_PRPL, |
|
| 1656 GAIM_CMD_FLAG_CHAT | GAIM_CMD_FLAG_PRPL_ONLY | |
|
| 1657 GAIM_CMD_FLAG_ALLOW_WRONG_ARGS, "prpl-jabber", |
|
| 1658 jabber_cmd_chat_affiliate, |
|
| 1659 _("affiliate <user> <owner|admin|member|outcast|none>: Set a user's affiliation with the room."), |
|
| 1660 NULL); |
|
| 1661 gaim_cmd_register("invite", "ws", GAIM_CMD_P_PRPL, |
|
| 1662 GAIM_CMD_FLAG_CHAT | GAIM_CMD_FLAG_PRPL_ONLY | |
|
| 1663 GAIM_CMD_FLAG_ALLOW_WRONG_ARGS, "prpl-jabber", |
|
| 1664 jabber_cmd_chat_invite, |
|
| 1665 _("invite <user> [message]: Invite a user to the room."), |
|
| 1666 NULL); |
|
| 1667 gaim_cmd_register("join", "ws", GAIM_CMD_P_PRPL, |
|
| 1668 GAIM_CMD_FLAG_CHAT | GAIM_CMD_FLAG_PRPL_ONLY | |
|
| 1669 GAIM_CMD_FLAG_ALLOW_WRONG_ARGS, "prpl-jabber", |
|
| 1670 jabber_cmd_chat_join, |
|
| 1671 _("join: <room> [server]: Join a chat on this server."), |
|
| 1672 NULL); |
|
| 1673 gaim_cmd_register("kick", "ws", GAIM_CMD_P_PRPL, |
|
| 1674 GAIM_CMD_FLAG_CHAT | GAIM_CMD_FLAG_PRPL_ONLY | |
|
| 1675 GAIM_CMD_FLAG_ALLOW_WRONG_ARGS, "prpl-jabber", |
|
| 1676 jabber_cmd_chat_kick, |
|
| 1677 _("kick <user> [room]: Kick a user from the room."), |
|
| 1678 NULL); |
|
| 1679 gaim_cmd_register("msg", "ws", GAIM_CMD_P_PRPL, |
|
| 1680 GAIM_CMD_FLAG_CHAT | GAIM_CMD_FLAG_PRPL_ONLY, |
|
| 1681 "prpl-jabber", jabber_cmd_chat_msg, |
|
| 1682 _("msg <user> <message>: Send a private message to another user."), |
|
| 1683 NULL); |
|
| 1684 } |
|
| 1685 |
|
| 1686 static GaimPluginProtocolInfo prpl_info = |
|
| 1687 { |
|
| 1688 OPT_PROTO_CHAT_TOPIC | OPT_PROTO_UNIQUE_CHATNAME, |
|
| 1689 NULL, /* user_splits */ |
|
| 1690 NULL, /* protocol_options */ |
|
| 1691 {"jpeg,gif,png", 0, 0, 96, 96, GAIM_ICON_SCALE_DISPLAY}, /* icon_spec */ |
|
| 1692 jabber_list_icon, /* list_icon */ |
|
| 1693 jabber_list_emblems, /* list_emblems */ |
|
| 1694 jabber_status_text, /* status_text */ |
|
| 1695 jabber_tooltip_text, /* tooltip_text */ |
|
| 1696 jabber_status_types, /* status_types */ |
|
| 1697 jabber_blist_node_menu, /* blist_node_menu */ |
|
| 1698 jabber_chat_info, /* chat_info */ |
|
| 1699 jabber_chat_info_defaults, /* chat_info_defaults */ |
|
| 1700 jabber_login, /* login */ |
|
| 1701 jabber_close, /* close */ |
|
| 1702 jabber_message_send_im, /* send_im */ |
|
| 1703 jabber_set_info, /* set_info */ |
|
| 1704 jabber_send_typing, /* send_typing */ |
|
| 1705 jabber_buddy_get_info, /* get_info */ |
|
| 1706 jabber_presence_send, /* set_away */ |
|
| 1707 jabber_idle_set, /* set_idle */ |
|
| 1708 NULL, /* change_passwd */ |
|
| 1709 jabber_roster_add_buddy, /* add_buddy */ |
|
| 1710 NULL, /* add_buddies */ |
|
| 1711 jabber_roster_remove_buddy, /* remove_buddy */ |
|
| 1712 NULL, /* remove_buddies */ |
|
| 1713 NULL, /* add_permit */ |
|
| 1714 NULL, /* add_deny */ |
|
| 1715 NULL, /* rem_permit */ |
|
| 1716 NULL, /* rem_deny */ |
|
| 1717 NULL, /* set_permit_deny */ |
|
| 1718 jabber_chat_join, /* join_chat */ |
|
| 1719 NULL, /* reject_chat */ |
|
| 1720 jabber_get_chat_name, /* get_chat_name */ |
|
| 1721 jabber_chat_invite, /* chat_invite */ |
|
| 1722 jabber_chat_leave, /* chat_leave */ |
|
| 1723 NULL, /* chat_whisper */ |
|
| 1724 jabber_message_send_chat, /* chat_send */ |
|
| 1725 jabber_keepalive, /* keepalive */ |
|
| 1726 jabber_register_account, /* register_user */ |
|
| 1727 jabber_buddy_get_info_chat, /* get_cb_info */ |
|
| 1728 NULL, /* get_cb_away */ |
|
| 1729 jabber_roster_alias_change, /* alias_buddy */ |
|
| 1730 jabber_roster_group_change, /* group_buddy */ |
|
| 1731 jabber_roster_group_rename, /* rename_group */ |
|
| 1732 NULL, /* buddy_free */ |
|
| 1733 jabber_convo_closed, /* convo_closed */ |
|
| 1734 jabber_normalize, /* normalize */ |
|
| 1735 jabber_set_buddy_icon, /* set_buddy_icon */ |
|
| 1736 NULL, /* remove_group */ |
|
| 1737 jabber_chat_buddy_real_name, /* get_cb_real_name */ |
|
| 1738 jabber_chat_set_topic, /* set_chat_topic */ |
|
| 1739 jabber_find_blist_chat, /* find_blist_chat */ |
|
| 1740 jabber_roomlist_get_list, /* roomlist_get_list */ |
|
| 1741 jabber_roomlist_cancel, /* roomlist_cancel */ |
|
| 1742 NULL, /* roomlist_expand_category */ |
|
| 1743 NULL, /* can_receive_file */ |
|
| 1744 jabber_si_xfer_send, /* send_file */ |
|
| 1745 jabber_si_new_xfer, /* new_xfer */ |
|
| 1746 jabber_offline_message, /* offline_message */ |
|
| 1747 NULL, /* whiteboard_prpl_ops */ |
|
| 1748 NULL, /* media_prpl_ops */ |
|
| 1749 }; |
|
| 1750 |
|
| 1751 static GaimPluginInfo info = |
|
| 1752 { |
|
| 1753 GAIM_PLUGIN_MAGIC, |
|
| 1754 GAIM_MAJOR_VERSION, |
|
| 1755 GAIM_MINOR_VERSION, |
|
| 1756 GAIM_PLUGIN_PROTOCOL, /**< type */ |
|
| 1757 NULL, /**< ui_requirement */ |
|
| 1758 0, /**< flags */ |
|
| 1759 NULL, /**< dependencies */ |
|
| 1760 GAIM_PRIORITY_DEFAULT, /**< priority */ |
|
| 1761 |
|
| 1762 "prpl-jabber", /**< id */ |
|
| 1763 "Jabber", /**< name */ |
|
| 1764 VERSION, /**< version */ |
|
| 1765 /** summary */ |
|
| 1766 N_("Jabber Protocol Plugin"), |
|
| 1767 /** description */ |
|
| 1768 N_("Jabber Protocol Plugin"), |
|
| 1769 NULL, /**< author */ |
|
| 1770 GAIM_WEBSITE, /**< homepage */ |
|
| 1771 |
|
| 1772 NULL, /**< load */ |
|
| 1773 NULL, /**< unload */ |
|
| 1774 NULL, /**< destroy */ |
|
| 1775 |
|
| 1776 NULL, /**< ui_info */ |
|
| 1777 &prpl_info, /**< extra_info */ |
|
| 1778 NULL, /**< prefs_info */ |
|
| 1779 jabber_actions |
|
| 1780 }; |
|
| 1781 |
|
| 1782 static void |
|
| 1783 init_plugin(GaimPlugin *plugin) |
|
| 1784 { |
|
| 1785 GaimAccountUserSplit *split; |
|
| 1786 GaimAccountOption *option; |
|
| 1787 |
|
| 1788 split = gaim_account_user_split_new(_("Server"), "jabber.org", '@'); |
|
| 1789 prpl_info.user_splits = g_list_append(prpl_info.user_splits, split); |
|
| 1790 |
|
| 1791 split = gaim_account_user_split_new(_("Resource"), "Home", '/'); |
|
| 1792 prpl_info.user_splits = g_list_append(prpl_info.user_splits, split); |
|
| 1793 |
|
| 1794 option = gaim_account_option_bool_new(_("Use TLS if available"), "use_tls", |
|
| 1795 TRUE); |
|
| 1796 prpl_info.protocol_options = g_list_append(prpl_info.protocol_options, |
|
| 1797 option); |
|
| 1798 |
|
| 1799 option = gaim_account_option_bool_new(_("Require TLS"), "require_tls", TRUE); |
|
| 1800 prpl_info.protocol_options = g_list_append(prpl_info.protocol_options, option); |
|
| 1801 |
|
| 1802 option = gaim_account_option_bool_new(_("Force old (port 5223) SSL"), "old_ssl", FALSE); |
|
| 1803 prpl_info.protocol_options = g_list_append(prpl_info.protocol_options, |
|
| 1804 option); |
|
| 1805 |
|
| 1806 option = gaim_account_option_bool_new( |
|
| 1807 _("Allow plaintext auth over unencrypted streams"), |
|
| 1808 "auth_plain_in_clear", FALSE); |
|
| 1809 prpl_info.protocol_options = g_list_append(prpl_info.protocol_options, |
|
| 1810 option); |
|
| 1811 |
|
| 1812 option = gaim_account_option_int_new(_("Connect port"), "port", 5222); |
|
| 1813 prpl_info.protocol_options = g_list_append(prpl_info.protocol_options, |
|
| 1814 option); |
|
| 1815 |
|
| 1816 option = gaim_account_option_string_new(_("Connect server"), |
|
| 1817 "connect_server", NULL); |
|
| 1818 prpl_info.protocol_options = g_list_append(prpl_info.protocol_options, |
|
| 1819 option); |
|
| 1820 |
|
| 1821 my_protocol = plugin; |
|
| 1822 |
|
| 1823 gaim_prefs_remove("/plugins/prpl/jabber"); |
|
| 1824 |
|
| 1825 /* XXX - If any other plugin wants SASL this won't be good ... */ |
|
| 1826 #ifdef HAVE_CYRUS_SASL |
|
| 1827 sasl_client_init(NULL); |
|
| 1828 #endif |
|
| 1829 jabber_register_commands(); |
|
| 1830 } |
|
| 1831 |
|
| 1832 GAIM_INIT_PLUGIN(jabber, init_plugin, info); |
|