comparison libpurple/protocols/jabber/jabber.c @ 15373:5fe8042783c1

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