src/protocols/jabber/jabber.c

branch
gaim
changeset 20470
77693555855f
parent 13071
b98e72d4089a
parent 20469
b2836a24d81e
child 20471
1966704b3e42
equal deleted inserted replaced
13071:b98e72d4089a 20470:77693555855f
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 &lt;new nickname&gt;: 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 &lt;user&gt; [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 &lt;user&gt; &lt;owner|admin|member|outcast|none&gt;: 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 &lt;user&gt; [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: &lt;room&gt; [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 &lt;user&gt; [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 &lt;user&gt; &lt;message&gt;: 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);

mercurial