| |
1 /* -*- Mode: C; tab-width: 8; indent-tabs-mode: t; c-basic-offset: 8 -*- */ |
| |
2 /* |
| |
3 * gaim |
| |
4 * |
| |
5 * Some code copyright (C) 1998-1999, Mark Spencer <markster@marko.net> |
| |
6 * libfaim code copyright 1998, 1999 Adam Fritzler <afritz@auk.cx> |
| |
7 * |
| |
8 * This program is free software; you can redistribute it and/or modify |
| |
9 * it under the terms of the GNU General Public License as published by |
| |
10 * the Free Software Foundation; either version 2 of the License, or |
| |
11 * (at your option) any later version. |
| |
12 * |
| |
13 * This program is distributed in the hope that it will be useful, |
| |
14 * but WITHOUT ANY WARRANTY; without even the implied warranty of |
| |
15 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the |
| |
16 * GNU General Public License for more details. |
| |
17 * |
| |
18 * You should have received a copy of the GNU General Public License |
| |
19 * along with this program; if not, write to the Free Software |
| |
20 * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA |
| |
21 * |
| |
22 */ |
| |
23 |
| |
24 #ifdef HAVE_CONFIG_H |
| |
25 #include "config.h" |
| |
26 #endif |
| |
27 |
| |
28 |
| |
29 #include <gtk/gtk.h> |
| |
30 #ifdef MAX |
| |
31 #undef MAX |
| |
32 #endif |
| |
33 #ifdef MIN |
| |
34 #undef MIN |
| |
35 #endif |
| |
36 #include <netdb.h> |
| |
37 #include <unistd.h> |
| |
38 #include <errno.h> |
| |
39 #include <netinet/in.h> |
| |
40 #include <arpa/inet.h> |
| |
41 #include <string.h> |
| |
42 #include <stdlib.h> |
| |
43 #include <stdio.h> |
| |
44 #include <time.h> |
| |
45 #include <sys/socket.h> |
| |
46 #include <sys/utsname.h> |
| |
47 #include <sys/stat.h> |
| |
48 #include "multi.h" |
| |
49 #include "prpl.h" |
| |
50 #include "gaim.h" |
| |
51 #include "jabber.h" |
| |
52 #include "proxy.h" |
| |
53 |
| |
54 #include "pixmaps/available.xpm" |
| |
55 #include "pixmaps/available-away.xpm" |
| |
56 #include "pixmaps/available-chat.xpm" |
| |
57 #include "pixmaps/available-xa.xpm" |
| |
58 #include "pixmaps/available-dnd.xpm" |
| |
59 |
| |
60 /* The priv member of gjconn's is a gaim_connection for now. */ |
| |
61 #define GJ_GC(x) ((struct gaim_connection *)(x)->priv) |
| |
62 |
| |
63 #define IQID_AUTH "__AUTH__" |
| |
64 |
| |
65 #define IQ_NONE -1 |
| |
66 #define IQ_AUTH 0 |
| |
67 #define IQ_ROSTER 1 |
| |
68 |
| |
69 #define UC_AWAY 0x38 |
| |
70 #define UC_CHAT 0x48 |
| |
71 #define UC_XA 0x98 |
| |
72 #define UC_DND 0x118 |
| |
73 |
| |
74 #define DEFAULT_SERVER "jabber.org" |
| |
75 #define DEFAULT_GROUPCHAT "conference.jabber.org" |
| |
76 #define DEFAULT_PORT 5222 |
| |
77 |
| |
78 #define USEROPT_PORT 0 |
| |
79 |
| |
80 typedef struct gjconn_struct { |
| |
81 /* Core structure */ |
| |
82 pool p; /* Memory allocation pool */ |
| |
83 int state; /* Connection state flag */ |
| |
84 int fd; /* Connection file descriptor */ |
| |
85 jid user; /* User info */ |
| |
86 char *pass; /* User passwd */ |
| |
87 |
| |
88 /* Stream stuff */ |
| |
89 int id; /* id counter for jab_getid() function */ |
| |
90 char idbuf[9]; /* temporary storage for jab_getid() */ |
| |
91 char *sid; /* stream id from server, for digest auth */ |
| |
92 XML_Parser parser; /* Parser instance */ |
| |
93 xmlnode current; /* Current node in parsing instance.. */ |
| |
94 |
| |
95 /* Event callback ptrs */ |
| |
96 void (*on_state)(struct gjconn_struct *j, int state); |
| |
97 void (*on_packet)(struct gjconn_struct *j, jpacket p); |
| |
98 |
| |
99 void *priv; |
| |
100 |
| |
101 } *gjconn, gjconn_struct; |
| |
102 |
| |
103 typedef void (*gjconn_state_h)(gjconn j, int state); |
| |
104 typedef void (*gjconn_packet_h)(gjconn j, jpacket p); |
| |
105 |
| |
106 static gjconn gjab_new(char *user, char *pass, void *priv); |
| |
107 static void gjab_delete(gjconn j); |
| |
108 static void gjab_state_handler(gjconn j, gjconn_state_h h); |
| |
109 static void gjab_packet_handler(gjconn j, gjconn_packet_h h); |
| |
110 static void gjab_start(gjconn j); |
| |
111 static void gjab_stop(gjconn j); |
| |
112 /* |
| |
113 static int gjab_getfd(gjconn j); |
| |
114 static jid gjab_getjid(gjconn j); |
| |
115 static char *gjab_getsid(gjconn j); |
| |
116 */ |
| |
117 static char *gjab_getid(gjconn j); |
| |
118 static void gjab_send(gjconn j, xmlnode x); |
| |
119 static void gjab_send_raw(gjconn j, const char *str); |
| |
120 static void gjab_recv(gjconn j); |
| |
121 static void gjab_auth(gjconn j); |
| |
122 |
| |
123 struct jabber_data { |
| |
124 gjconn jc; |
| |
125 gboolean did_import; |
| |
126 GSList *pending_chats; |
| |
127 GSList *existing_chats; |
| |
128 GHashTable *hash; |
| |
129 time_t idle; |
| |
130 }; |
| |
131 |
| |
132 struct jabber_chat { |
| |
133 jid Jid; |
| |
134 struct gaim_connection *gc; |
| |
135 struct conversation *b; |
| |
136 int id; |
| |
137 }; |
| |
138 |
| |
139 static char *jabber_name() |
| |
140 { |
| |
141 return "Jabber"; |
| |
142 } |
| |
143 |
| |
144 #define STATE_EVT(arg) if(j->on_state) { (j->on_state)(j, (arg) ); } |
| |
145 |
| |
146 static char *create_valid_jid(const char *given, char *server, char *resource) |
| |
147 { |
| |
148 char *valid; |
| |
149 |
| |
150 if (!strchr(given, '@')) |
| |
151 valid = g_strdup_printf("%s@%s/%s", given, server, resource); |
| |
152 else if (!strchr(strchr(given, '@'), '/')) |
| |
153 valid = g_strdup_printf("%s/%s", given, resource); |
| |
154 else |
| |
155 valid = g_strdup(given); |
| |
156 |
| |
157 return valid; |
| |
158 } |
| |
159 |
| |
160 static gjconn gjab_new(char *user, char *pass, void *priv) |
| |
161 { |
| |
162 pool p; |
| |
163 gjconn j; |
| |
164 |
| |
165 if (!user) |
| |
166 return (NULL); |
| |
167 |
| |
168 p = pool_new(); |
| |
169 if (!p) |
| |
170 return (NULL); |
| |
171 j = pmalloc_x(p, sizeof(gjconn_struct), 0); |
| |
172 if (!j) |
| |
173 return (NULL); |
| |
174 j->p = p; |
| |
175 |
| |
176 j->user = jid_new(p, user); |
| |
177 j->pass = pstrdup(p, pass); |
| |
178 |
| |
179 j->state = JCONN_STATE_OFF; |
| |
180 j->id = 1; |
| |
181 j->fd = -1; |
| |
182 |
| |
183 j->priv = priv; |
| |
184 |
| |
185 return j; |
| |
186 } |
| |
187 |
| |
188 static void gjab_delete(gjconn j) |
| |
189 { |
| |
190 if (!j) |
| |
191 return; |
| |
192 |
| |
193 gjab_stop(j); |
| |
194 pool_free(j->p); |
| |
195 } |
| |
196 |
| |
197 static void gjab_state_handler(gjconn j, gjconn_state_h h) |
| |
198 { |
| |
199 if (!j) |
| |
200 return; |
| |
201 |
| |
202 j->on_state = h; |
| |
203 } |
| |
204 |
| |
205 static void gjab_packet_handler(gjconn j, gjconn_packet_h h) |
| |
206 { |
| |
207 if (!j) |
| |
208 return; |
| |
209 |
| |
210 j->on_packet = h; |
| |
211 } |
| |
212 |
| |
213 static void gjab_stop(gjconn j) |
| |
214 { |
| |
215 if (!j || j->state == JCONN_STATE_OFF) |
| |
216 return; |
| |
217 |
| |
218 j->state = JCONN_STATE_OFF; |
| |
219 gjab_send_raw(j, "</stream:stream>"); |
| |
220 close(j->fd); |
| |
221 j->fd = -1; |
| |
222 XML_ParserFree(j->parser); |
| |
223 j->parser = NULL; |
| |
224 } |
| |
225 |
| |
226 /* |
| |
227 static int gjab_getfd(gjconn j) |
| |
228 { |
| |
229 if (j) |
| |
230 return j->fd; |
| |
231 else |
| |
232 return -1; |
| |
233 } |
| |
234 |
| |
235 static jid gjab_getjid(gjconn j) |
| |
236 { |
| |
237 if (j) |
| |
238 return (j->user); |
| |
239 else |
| |
240 return NULL; |
| |
241 } |
| |
242 |
| |
243 static char *gjab_getsid(gjconn j) |
| |
244 { |
| |
245 if (j) |
| |
246 return (j->sid); |
| |
247 else |
| |
248 return NULL; |
| |
249 } |
| |
250 */ |
| |
251 |
| |
252 static char *gjab_getid(gjconn j) |
| |
253 { |
| |
254 snprintf(j->idbuf, 8, "%d", j->id++); |
| |
255 return &j->idbuf[0]; |
| |
256 } |
| |
257 |
| |
258 static void gjab_send(gjconn j, xmlnode x) |
| |
259 { |
| |
260 if (j && j->state != JCONN_STATE_OFF) { |
| |
261 char *buf = xmlnode2str(x); |
| |
262 if (buf) |
| |
263 write(j->fd, buf, strlen(buf)); |
| |
264 debug_printf("gjab_send: %s\n", buf); |
| |
265 } |
| |
266 } |
| |
267 |
| |
268 static void gjab_send_raw(gjconn j, const char *str) |
| |
269 { |
| |
270 if (j && j->state != JCONN_STATE_OFF) { |
| |
271 write(j->fd, str, strlen(str)); |
| |
272 debug_printf("gjab_send_raw: %s\n", str); |
| |
273 } |
| |
274 } |
| |
275 |
| |
276 static void gjab_reqroster(gjconn j) |
| |
277 { |
| |
278 xmlnode x; |
| |
279 |
| |
280 x = jutil_iqnew(JPACKET__GET, NS_ROSTER); |
| |
281 xmlnode_put_attrib(x, "id", gjab_getid(j)); |
| |
282 |
| |
283 gjab_send(j, x); |
| |
284 xmlnode_free(x); |
| |
285 } |
| |
286 |
| |
287 static void gjab_auth(gjconn j) |
| |
288 { |
| |
289 xmlnode x, y, z; |
| |
290 char *hash, *user; |
| |
291 |
| |
292 if (!j) |
| |
293 return; |
| |
294 |
| |
295 x = jutil_iqnew(JPACKET__SET, NS_AUTH); |
| |
296 xmlnode_put_attrib(x, "id", IQID_AUTH); |
| |
297 y = xmlnode_get_tag(x, "query"); |
| |
298 |
| |
299 user = j->user->user; |
| |
300 |
| |
301 if (user) { |
| |
302 z = xmlnode_insert_tag(y, "username"); |
| |
303 xmlnode_insert_cdata(z, user, -1); |
| |
304 } |
| |
305 |
| |
306 z = xmlnode_insert_tag(y, "resource"); |
| |
307 xmlnode_insert_cdata(z, j->user->resource, -1); |
| |
308 |
| |
309 if (j->sid) { |
| |
310 z = xmlnode_insert_tag(y, "digest"); |
| |
311 hash = pmalloc(x->p, strlen(j->sid) + strlen(j->pass) + 1); |
| |
312 strcpy(hash, j->sid); |
| |
313 strcat(hash, j->pass); |
| |
314 hash = shahash(hash); |
| |
315 xmlnode_insert_cdata(z, hash, 40); |
| |
316 } else { |
| |
317 z = xmlnode_insert_tag(y, "password"); |
| |
318 xmlnode_insert_cdata(z, j->pass, -1); |
| |
319 } |
| |
320 |
| |
321 gjab_send(j, x); |
| |
322 xmlnode_free(x); |
| |
323 |
| |
324 return; |
| |
325 } |
| |
326 |
| |
327 static void gjab_recv(gjconn j) |
| |
328 { |
| |
329 static char buf[4096]; |
| |
330 int len; |
| |
331 |
| |
332 if (!j || j->state == JCONN_STATE_OFF) |
| |
333 return; |
| |
334 |
| |
335 if ((len = read(j->fd, buf, sizeof(buf) - 1))) { |
| |
336 buf[len] = '\0'; |
| |
337 debug_printf("input (len %d): %s\n", len, buf); |
| |
338 XML_Parse(j->parser, buf, len, 0); |
| |
339 } else if (len <= 0) { |
| |
340 STATE_EVT(JCONN_STATE_OFF) |
| |
341 } |
| |
342 } |
| |
343 |
| |
344 static void startElement(void *userdata, const char *name, const char **attribs) |
| |
345 { |
| |
346 xmlnode x; |
| |
347 gjconn j = (gjconn) userdata; |
| |
348 |
| |
349 if (j->current) { |
| |
350 /* Append the node to the current one */ |
| |
351 x = xmlnode_insert_tag(j->current, name); |
| |
352 xmlnode_put_expat_attribs(x, attribs); |
| |
353 |
| |
354 j->current = x; |
| |
355 } else { |
| |
356 x = xmlnode_new_tag(name); |
| |
357 xmlnode_put_expat_attribs(x, attribs); |
| |
358 if (strcmp(name, "stream:stream") == 0) { |
| |
359 /* special case: name == stream:stream */ |
| |
360 /* id attrib of stream is stored for digest auth */ |
| |
361 j->sid = xmlnode_get_attrib(x, "id"); |
| |
362 /* STATE_EVT(JCONN_STATE_AUTH) */ |
| |
363 } else { |
| |
364 j->current = x; |
| |
365 } |
| |
366 } |
| |
367 } |
| |
368 |
| |
369 static void endElement(void *userdata, const char *name) |
| |
370 { |
| |
371 gjconn j = (gjconn) userdata; |
| |
372 xmlnode x; |
| |
373 jpacket p; |
| |
374 |
| |
375 if (j->current == NULL) { |
| |
376 /* we got </stream:stream> */ |
| |
377 STATE_EVT(JCONN_STATE_OFF) |
| |
378 return; |
| |
379 } |
| |
380 |
| |
381 x = xmlnode_get_parent(j->current); |
| |
382 |
| |
383 if (!x) { |
| |
384 /* it is time to fire the event */ |
| |
385 p = jpacket_new(j->current); |
| |
386 |
| |
387 if (j->on_packet) |
| |
388 (j->on_packet) (j, p); |
| |
389 else |
| |
390 xmlnode_free(j->current); |
| |
391 } |
| |
392 |
| |
393 j->current = x; |
| |
394 } |
| |
395 |
| |
396 static void jabber_callback(gpointer data, gint source, GdkInputCondition condition) |
| |
397 { |
| |
398 struct gaim_connection *gc = (struct gaim_connection *)data; |
| |
399 struct jabber_data *jd = (struct jabber_data *)gc->proto_data; |
| |
400 |
| |
401 gjab_recv(jd->jc); |
| |
402 } |
| |
403 |
| |
404 static void charData(void *userdata, const char *s, int slen) |
| |
405 { |
| |
406 gjconn j = (gjconn) userdata; |
| |
407 |
| |
408 if (j->current) |
| |
409 xmlnode_insert_cdata(j->current, s, slen); |
| |
410 } |
| |
411 |
| |
412 static void gjab_connected(gpointer data, gint source, GdkInputCondition cond) |
| |
413 { |
| |
414 xmlnode x; |
| |
415 char *t, *t2; |
| |
416 struct gaim_connection *gc = data; |
| |
417 struct jabber_data *jd; |
| |
418 gjconn j; |
| |
419 |
| |
420 if (!g_slist_find(connections, gc)) { |
| |
421 close(source); |
| |
422 return; |
| |
423 } |
| |
424 |
| |
425 jd = gc->proto_data; |
| |
426 j = jd->jc; |
| |
427 |
| |
428 if (source == -1) { |
| |
429 STATE_EVT(JCONN_STATE_OFF) |
| |
430 return; |
| |
431 } |
| |
432 |
| |
433 if (j->fd != source) |
| |
434 j->fd = source; |
| |
435 |
| |
436 j->state = JCONN_STATE_CONNECTED; |
| |
437 STATE_EVT(JCONN_STATE_CONNECTED) |
| |
438 |
| |
439 /* start stream */ |
| |
440 x = jutil_header(NS_CLIENT, j->user->server); |
| |
441 t = xmlnode2str(x); |
| |
442 /* this is ugly, we can create the string here instead of jutil_header */ |
| |
443 /* what do you think about it? -madcat */ |
| |
444 t2 = strstr(t, "/>"); |
| |
445 *t2++ = '>'; |
| |
446 *t2 = '\0'; |
| |
447 gjab_send_raw(j, "<?xml version='1.0'?>"); |
| |
448 gjab_send_raw(j, t); |
| |
449 xmlnode_free(x); |
| |
450 |
| |
451 j->state = JCONN_STATE_ON; |
| |
452 STATE_EVT(JCONN_STATE_ON); |
| |
453 |
| |
454 gc = GJ_GC(j); |
| |
455 gc->inpa = gdk_input_add(j->fd, GDK_INPUT_READ, jabber_callback, gc); |
| |
456 } |
| |
457 |
| |
458 static void gjab_start(gjconn j) |
| |
459 { |
| |
460 struct aim_user *user; |
| |
461 int port; |
| |
462 |
| |
463 if (!j || j->state != JCONN_STATE_OFF) |
| |
464 return; |
| |
465 |
| |
466 user = GJ_GC(j)->user; |
| |
467 port = user->proto_opt[USEROPT_PORT][0] ? atoi(user->proto_opt[USEROPT_PORT]) : DEFAULT_PORT; |
| |
468 |
| |
469 j->parser = XML_ParserCreate(NULL); |
| |
470 XML_SetUserData(j->parser, (void *)j); |
| |
471 XML_SetElementHandler(j->parser, startElement, endElement); |
| |
472 XML_SetCharacterDataHandler(j->parser, charData); |
| |
473 |
| |
474 j->fd = proxy_connect(j->user->server, port, gjab_connected, GJ_GC(j)); |
| |
475 if (!user->gc || (j->fd < 0)) { |
| |
476 STATE_EVT(JCONN_STATE_OFF) |
| |
477 return; |
| |
478 } |
| |
479 } |
| |
480 |
| |
481 static struct conversation *find_chat(struct gaim_connection *gc, char *name) |
| |
482 { |
| |
483 GSList *bcs = gc->buddy_chats; |
| |
484 struct conversation *b = NULL; |
| |
485 char *chat = g_strdup(normalize(name)); |
| |
486 |
| |
487 while (bcs) { |
| |
488 b = bcs->data; |
| |
489 if (!strcasecmp(normalize(b->name), chat)) |
| |
490 break; |
| |
491 b = NULL; |
| |
492 bcs = bcs->next; |
| |
493 } |
| |
494 |
| |
495 g_free(chat); |
| |
496 return b; |
| |
497 } |
| |
498 |
| |
499 static struct jabber_chat *find_existing_chat(struct gaim_connection *gc, jid chat) |
| |
500 { |
| |
501 GSList *bcs = ((struct jabber_data *)gc->proto_data)->existing_chats; |
| |
502 struct jabber_chat *jc = NULL; |
| |
503 |
| |
504 while (bcs) { |
| |
505 jc = bcs->data; |
| |
506 if (!jid_cmpx(chat, jc->Jid, JID_USER | JID_SERVER)) |
| |
507 break; |
| |
508 jc = NULL; |
| |
509 bcs = bcs->next; |
| |
510 } |
| |
511 |
| |
512 return jc; |
| |
513 } |
| |
514 |
| |
515 static struct jabber_chat *find_pending_chat(struct gaim_connection *gc, jid chat) |
| |
516 { |
| |
517 GSList *bcs = ((struct jabber_data *)gc->proto_data)->pending_chats; |
| |
518 struct jabber_chat *jc = NULL; |
| |
519 |
| |
520 while (bcs) { |
| |
521 jc = bcs->data; |
| |
522 if (!jid_cmpx(chat, jc->Jid, JID_USER | JID_SERVER)) |
| |
523 break; |
| |
524 jc = NULL; |
| |
525 bcs = bcs->next; |
| |
526 } |
| |
527 |
| |
528 return jc; |
| |
529 } |
| |
530 |
| |
531 static gboolean find_chat_buddy(struct conversation *b, char *name) |
| |
532 { |
| |
533 GList *m = b->in_room; |
| |
534 |
| |
535 while (m) { |
| |
536 if (!strcmp(m->data, name)) |
| |
537 return TRUE; |
| |
538 m = m->next; |
| |
539 } |
| |
540 |
| |
541 return FALSE; |
| |
542 } |
| |
543 |
| |
544 static void jabber_handlemessage(gjconn j, jpacket p) |
| |
545 { |
| |
546 xmlnode y, xmlns, subj; |
| |
547 |
| |
548 char *from = NULL, *msg = NULL, *type = NULL, *topic = NULL; |
| |
549 char m[BUF_LONG * 2]; |
| |
550 |
| |
551 type = xmlnode_get_attrib(p->x, "type"); |
| |
552 |
| |
553 if (!type || !strcasecmp(type, "normal") || !strcasecmp(type, "chat")) { |
| |
554 |
| |
555 /* XXX namespaces could be handled better. (mid) */ |
| |
556 if ((xmlns = xmlnode_get_tag(p->x, "x"))) |
| |
557 type = xmlnode_get_attrib(xmlns, "xmlns"); |
| |
558 |
| |
559 from = jid_full(p->from); |
| |
560 /* |
| |
561 if ((y = xmlnode_get_tag(p->x, "html"))) { |
| |
562 msg = xmlnode_get_data(y); |
| |
563 } else |
| |
564 */ |
| |
565 if ((y = xmlnode_get_tag(p->x, "body"))) { |
| |
566 msg = xmlnode_get_data(y); |
| |
567 } |
| |
568 |
| |
569 msg = utf8_to_str(msg); |
| |
570 |
| |
571 if (!from) |
| |
572 return; |
| |
573 |
| |
574 if (type && !strcasecmp(type, "jabber:x:conference")) { |
| |
575 char *room; |
| |
576 |
| |
577 room = xmlnode_get_attrib(xmlns, "jid"); |
| |
578 |
| |
579 serv_got_chat_invite(GJ_GC(j), room, 0, from, msg); |
| |
580 } else if (msg) { /* whisper */ |
| |
581 struct jabber_chat *jc; |
| |
582 g_snprintf(m, sizeof(m), "%s", msg); |
| |
583 if (((jc = find_existing_chat(GJ_GC(j), p->from)) != NULL) && jc->b) |
| |
584 serv_got_chat_in(GJ_GC(j), jc->b->id, p->from->resource, 1, m, time(NULL)); |
| |
585 else { |
| |
586 if (find_conversation(jid_full(p->from))) |
| |
587 serv_got_im(GJ_GC(j), jid_full(p->from), m, 0, time(NULL)); |
| |
588 else { |
| |
589 from = g_strdup_printf("%s@%s", p->from->user, p->from->server); |
| |
590 serv_got_im(GJ_GC(j), from, m, 0, time(NULL)); |
| |
591 g_free(from); |
| |
592 } |
| |
593 } |
| |
594 } |
| |
595 |
| |
596 if (msg) |
| |
597 g_free(msg); |
| |
598 |
| |
599 } else if (!strcasecmp(type, "error")) { |
| |
600 if ((y = xmlnode_get_tag(p->x, "error"))) { |
| |
601 type = xmlnode_get_attrib(y, "code"); |
| |
602 msg = xmlnode_get_data(y); |
| |
603 } |
| |
604 |
| |
605 if (msg) { |
| |
606 from = g_strdup_printf("Error %s", type ? type : ""); |
| |
607 do_error_dialog(msg, from); |
| |
608 g_free(from); |
| |
609 } |
| |
610 } else if (!strcasecmp(type, "groupchat")) { |
| |
611 struct jabber_chat *jc; |
| |
612 static int i = 0; |
| |
613 |
| |
614 /* |
| |
615 if ((y = xmlnode_get_tag(p->x, "html"))) { |
| |
616 msg = xmlnode_get_data(y); |
| |
617 } else |
| |
618 */ |
| |
619 if ((y = xmlnode_get_tag(p->x, "body"))) { |
| |
620 msg = xmlnode_get_data(y); |
| |
621 } |
| |
622 |
| |
623 msg = utf8_to_str(msg); |
| |
624 |
| |
625 if ((subj = xmlnode_get_tag(p->x, "subject"))) { |
| |
626 topic = xmlnode_get_data(subj); |
| |
627 } |
| |
628 topic = utf8_to_str(topic); |
| |
629 |
| |
630 jc = find_existing_chat(GJ_GC(j), p->from); |
| |
631 if (!jc) { |
| |
632 /* we're not in this chat. are we supposed to be? */ |
| |
633 struct jabber_data *jd = GJ_GC(j)->proto_data; |
| |
634 if ((jc = find_pending_chat(GJ_GC(j), p->from)) != NULL) { |
| |
635 /* yes, we're supposed to be. so now we are. */ |
| |
636 jc->b = serv_got_joined_chat(GJ_GC(j), i++, p->from->user); |
| |
637 jc->id = jc->b->id; |
| |
638 jd->existing_chats = g_slist_append(jd->existing_chats, jc); |
| |
639 jd->pending_chats = g_slist_remove(jd->pending_chats, jc); |
| |
640 } else { |
| |
641 /* no, we're not supposed to be. */ |
| |
642 g_free(msg); |
| |
643 return; |
| |
644 } |
| |
645 } |
| |
646 if (p->from->resource) { |
| |
647 if (!y) { |
| |
648 if (!find_chat_buddy(jc->b, p->from->resource)) |
| |
649 add_chat_buddy(jc->b, p->from->resource); |
| |
650 else if ((y = xmlnode_get_tag(p->x, "status"))) { |
| |
651 char buf[8192]; |
| |
652 msg = xmlnode_get_data(y); |
| |
653 g_snprintf(buf, sizeof(buf), "%s now has status: %s", |
| |
654 p->from->resource, msg); |
| |
655 write_to_conv(jc->b, buf, WFLAG_SYSTEM, NULL, time(NULL)); |
| |
656 } |
| |
657 } else if (jc->b && msg) { |
| |
658 char buf[8192]; |
| |
659 |
| |
660 if (topic) { |
| |
661 char tbuf[8192]; |
| |
662 g_snprintf(tbuf, sizeof(tbuf), "%s", topic); |
| |
663 chat_set_topic(jc->b, p->from->resource, tbuf); |
| |
664 } |
| |
665 |
| |
666 |
| |
667 g_snprintf(buf, sizeof(buf), "%s", msg); |
| |
668 serv_got_chat_in(GJ_GC(j), jc->b->id, p->from->resource, 0, buf, time(NULL)); |
| |
669 } |
| |
670 } else { /* message from the server */ |
| |
671 if(jc->b && topic) { |
| |
672 char tbuf[8192]; |
| |
673 g_snprintf(tbuf, sizeof(tbuf), "%s", topic); |
| |
674 chat_set_topic(jc->b, "", tbuf); |
| |
675 } |
| |
676 } |
| |
677 |
| |
678 g_free(msg); |
| |
679 g_free(topic); |
| |
680 |
| |
681 } else { |
| |
682 debug_printf("unhandled message %s\n", type); |
| |
683 } |
| |
684 } |
| |
685 |
| |
686 static void jabber_handlepresence(gjconn j, jpacket p) |
| |
687 { |
| |
688 char *to, *from, *type; |
| |
689 struct buddy *b = NULL; |
| |
690 jid who; |
| |
691 char *buddy; |
| |
692 xmlnode y; |
| |
693 char *show; |
| |
694 int state = UC_NORMAL; |
| |
695 GSList *resources; |
| |
696 char *res; |
| |
697 struct conversation *cnv = NULL; |
| |
698 struct jabber_chat *jc = NULL; |
| |
699 |
| |
700 to = xmlnode_get_attrib(p->x, "to"); |
| |
701 from = xmlnode_get_attrib(p->x, "from"); |
| |
702 type = xmlnode_get_attrib(p->x, "type"); |
| |
703 |
| |
704 if ((y = xmlnode_get_tag(p->x, "show"))) { |
| |
705 show = xmlnode_get_data(y); |
| |
706 if (!show) { |
| |
707 state = UC_NORMAL; |
| |
708 } else if (!strcasecmp(show, "away")) { |
| |
709 state = UC_AWAY; |
| |
710 } else if (!strcasecmp(show, "chat")) { |
| |
711 state = UC_CHAT; |
| |
712 } else if (!strcasecmp(show, "xa")) { |
| |
713 state = UC_XA; |
| |
714 } else if (!strcasecmp(show, "dnd")) { |
| |
715 state = UC_DND; |
| |
716 } |
| |
717 } else { |
| |
718 state = UC_NORMAL; |
| |
719 } |
| |
720 |
| |
721 who = jid_new(j->p, from); |
| |
722 if (who->user == NULL) { |
| |
723 /* FIXME: transport */ |
| |
724 return; |
| |
725 } |
| |
726 |
| |
727 buddy = g_strdup_printf("%s@%s", who->user, who->server); |
| |
728 |
| |
729 /* um. we're going to check if it's a chat. if it isn't, and there are pending |
| |
730 * chats, create the chat. if there aren't pending chats, add the buddy. */ |
| |
731 if ((cnv = find_chat(GJ_GC(j), who->user)) == NULL) { |
| |
732 static int i = 0x70; |
| |
733 struct jabber_data *jd = GJ_GC(j)->proto_data; |
| |
734 if ((jc = find_pending_chat(GJ_GC(j), who)) != NULL) { |
| |
735 jc->b = cnv = serv_got_joined_chat(GJ_GC(j), i++, who->user); |
| |
736 jc->id = jc->b->id; |
| |
737 jd->existing_chats = g_slist_append(jd->existing_chats, jc); |
| |
738 jd->pending_chats = g_slist_remove(jd->pending_chats, jc); |
| |
739 } else if (!(b = find_buddy(GJ_GC(j), buddy))) { |
| |
740 b = add_buddy(GJ_GC(j), "Buddies", buddy, buddy); |
| |
741 do_export(GJ_GC(j)); |
| |
742 } |
| |
743 } |
| |
744 |
| |
745 if (!cnv) { |
| |
746 resources = b->proto_data; |
| |
747 res = who->resource; |
| |
748 if (res) |
| |
749 while (resources) { |
| |
750 if (!strcmp(res, resources->data)) |
| |
751 break; |
| |
752 resources = resources->next; |
| |
753 } |
| |
754 |
| |
755 if (type && (strcasecmp(type, "unavailable") == 0)) { |
| |
756 if (resources) { |
| |
757 g_free(resources->data); |
| |
758 b->proto_data = g_slist_remove(b->proto_data, resources->data); |
| |
759 } |
| |
760 if (!b->proto_data) { |
| |
761 serv_got_update(GJ_GC(j), buddy, 0, 0, 0, 0, 0, 0); |
| |
762 } |
| |
763 } else { |
| |
764 /* keep track of away msg same as yahoo plugin */ |
| |
765 struct jabber_data *jd = GJ_GC(j)->proto_data; |
| |
766 gpointer val = g_hash_table_lookup(jd->hash, b->name); |
| |
767 if (val) |
| |
768 g_free(val); |
| |
769 g_hash_table_insert(jd->hash, g_strdup(b->name), g_strdup(xmlnode_get_tag_data(p->x, "status"))); |
| |
770 |
| |
771 |
| |
772 if (!resources) { |
| |
773 b->proto_data = g_slist_append(b->proto_data, g_strdup(res)); |
| |
774 } |
| |
775 |
| |
776 serv_got_update(GJ_GC(j), buddy, 1, 0, 0, 0, state, 0); |
| |
777 |
| |
778 } |
| |
779 } else { |
| |
780 if (who->resource) { |
| |
781 if (type && !strcasecmp(type, "unavailable")) { |
| |
782 struct jabber_data *jd; |
| |
783 if (!jc && !(jc = find_existing_chat(GJ_GC(j), who))) { |
| |
784 g_free(buddy); |
| |
785 return; |
| |
786 } |
| |
787 jd = jc->gc->proto_data; |
| |
788 if (strcmp(who->resource, jc->Jid->resource) && jc->b) { |
| |
789 remove_chat_buddy(jc->b, who->resource); |
| |
790 return; |
| |
791 } |
| |
792 |
| |
793 jd->existing_chats = g_slist_remove(jd->existing_chats, jc); |
| |
794 serv_got_chat_left(GJ_GC(j), jc->id); |
| |
795 g_free(jc); |
| |
796 } else { |
| |
797 if ((!jc && !(jc = find_existing_chat(GJ_GC(j), who))) || !jc->b) { |
| |
798 g_free(buddy); |
| |
799 return; |
| |
800 } |
| |
801 if (!find_chat_buddy(jc->b, who->resource)) |
| |
802 add_chat_buddy(jc->b, who->resource); |
| |
803 else if ((y = xmlnode_get_tag(p->x, "status"))) { |
| |
804 char buf[8192]; |
| |
805 char *msg = xmlnode_get_data(y); |
| |
806 g_snprintf(buf, sizeof(buf), "%s now has status: %s", |
| |
807 p->from->resource, msg); |
| |
808 write_to_conv(jc->b, buf, WFLAG_SYSTEM, NULL, time(NULL)); |
| |
809 } |
| |
810 } |
| |
811 } |
| |
812 } |
| |
813 |
| |
814 g_free(buddy); |
| |
815 |
| |
816 return; |
| |
817 } |
| |
818 |
| |
819 static void jabber_handles10n(gjconn j, jpacket p) |
| |
820 { |
| |
821 xmlnode g; |
| |
822 char *Jid = xmlnode_get_attrib(p->x, "from"); |
| |
823 char *ask = xmlnode_get_attrib(p->x, "type"); |
| |
824 |
| |
825 g = xmlnode_new_tag("presence"); |
| |
826 xmlnode_put_attrib(g, "to", Jid); |
| |
827 if (!strcmp(ask, "subscribe")) |
| |
828 xmlnode_put_attrib(g, "type", "subscribed"); |
| |
829 else if (!strcmp(ask, "unsubscribe")) |
| |
830 xmlnode_put_attrib(g, "type", "unsubscribed"); |
| |
831 else |
| |
832 return; |
| |
833 |
| |
834 gjab_send(j, g); |
| |
835 } |
| |
836 |
| |
837 static void jabber_handleroster(gjconn j, xmlnode querynode) |
| |
838 { |
| |
839 xmlnode x; |
| |
840 |
| |
841 x = xmlnode_get_firstchild(querynode); |
| |
842 while (x) { |
| |
843 xmlnode g; |
| |
844 char *Jid, *name, *sub, *ask; |
| |
845 jid who; |
| |
846 |
| |
847 Jid = xmlnode_get_attrib(x, "jid"); |
| |
848 name = xmlnode_get_attrib(x, "name"); |
| |
849 sub = xmlnode_get_attrib(x, "subscription"); |
| |
850 ask = xmlnode_get_attrib(x, "ask"); |
| |
851 who = jid_new(j->p, Jid); |
| |
852 |
| |
853 if ((g = xmlnode_get_firstchild(x))) { |
| |
854 while (g) { |
| |
855 if (strncasecmp(xmlnode_get_name(g), "group", 5) == 0) { |
| |
856 struct buddy *b = NULL; |
| |
857 char *groupname, *buddyname; |
| |
858 |
| |
859 if (who->user == NULL) { |
| |
860 /* FIXME: transport */ |
| |
861 g = xmlnode_get_nextsibling(g); |
| |
862 continue; |
| |
863 } |
| |
864 buddyname = g_strdup_printf("%s@%s", who->user, who->server); |
| |
865 groupname = xmlnode_get_data(xmlnode_get_firstchild(g)); |
| |
866 if (groupname == NULL) |
| |
867 groupname = "Buddies"; |
| |
868 if (strcasecmp(sub, "from") && strcasecmp(sub, "none") && |
| |
869 !(b = find_buddy(GJ_GC(j), buddyname))) { |
| |
870 debug_printf("adding buddy: %s\n", buddyname); |
| |
871 b = add_buddy(GJ_GC(j), groupname, buddyname, |
| |
872 name ? name : buddyname); |
| |
873 do_export(GJ_GC(j)); |
| |
874 /* |
| |
875 } else if (b) { |
| |
876 debug_printf("updating buddy: %s/%s\n", buddyname, name); |
| |
877 g_snprintf(b->name, sizeof(b->name), "%s", buddyname); |
| |
878 g_snprintf(b->show, sizeof(b->show), "%s", |
| |
879 name ? name : buddyname); |
| |
880 */ |
| |
881 } |
| |
882 g_free(buddyname); |
| |
883 } |
| |
884 g = xmlnode_get_nextsibling(g); |
| |
885 } |
| |
886 } else { |
| |
887 struct buddy *b; |
| |
888 char *buddyname; |
| |
889 |
| |
890 if (who->user == NULL) { |
| |
891 /* FIXME: transport */ |
| |
892 x = xmlnode_get_nextsibling(x); |
| |
893 continue; |
| |
894 } |
| |
895 buddyname = g_strdup_printf("%s@%s", who->user, who->server); |
| |
896 if (strcasecmp(sub, "from") && strcasecmp(sub, "none") && |
| |
897 !(b = find_buddy(GJ_GC(j), buddyname))) { |
| |
898 debug_printf("adding buddy: %s\n", buddyname); |
| |
899 b = add_buddy(GJ_GC(j), "Buddies", buddyname, name ? name : Jid); |
| |
900 do_export(GJ_GC(j)); |
| |
901 } |
| |
902 g_free(buddyname); |
| |
903 } |
| |
904 |
| |
905 x = xmlnode_get_nextsibling(x); |
| |
906 } |
| |
907 |
| |
908 x = jutil_presnew(0, NULL, "Online"); |
| |
909 gjab_send(j, x); |
| |
910 xmlnode_free(x); |
| |
911 } |
| |
912 |
| |
913 static void jabber_handlevcard(gjconn j, xmlnode querynode, char *from) { |
| |
914 char buf[1024]; |
| |
915 char *fn, *url, *email, *nickname, *status, *desc; |
| |
916 jid who; |
| |
917 char *buddy; |
| |
918 struct jabber_data *jd = GJ_GC(j)->proto_data; |
| |
919 int at = 0; |
| |
920 |
| |
921 who = jid_new(j->p, from); |
| |
922 buddy = g_strdup_printf("%s@%s", who->user, who->server); |
| |
923 |
| |
924 fn = xmlnode_get_tag_data(querynode, "FN"); |
| |
925 url = xmlnode_get_tag_data(querynode, "URL"); |
| |
926 email = xmlnode_get_tag_data(querynode, "EMAIL"); |
| |
927 nickname = xmlnode_get_tag_data(querynode, "NICKNAME"); |
| |
928 desc = xmlnode_get_tag_data(querynode, "DESC"); |
| |
929 status = g_hash_table_lookup(jd->hash, buddy); |
| |
930 if (!status) |
| |
931 status = "Online"; |
| |
932 |
| |
933 at = g_snprintf(buf, sizeof buf, "<B>Jabber ID:</B> %s<BR>", buddy); |
| |
934 if (fn) |
| |
935 at += g_snprintf(buf + at, sizeof(buf) - at, "<B>Full Name:</B> %s<BR>", fn); |
| |
936 if (nickname) |
| |
937 at += g_snprintf(buf + at, sizeof(buf) - at, "<B>Nickname:</B> %s<BR>", nickname); |
| |
938 if (url) |
| |
939 at += g_snprintf(buf + at, sizeof(buf) - at, "<B>URL:</B> <A HREF=\"%s\">%s</A><BR>", |
| |
940 url, url); |
| |
941 if (email) |
| |
942 at += g_snprintf(buf + at, sizeof(buf) - at, |
| |
943 "<B>Email:</B> <A HREF=\"mailto:%s\">%s</A><BR>", email, email); |
| |
944 at += g_snprintf(buf + at, sizeof(buf) - at, "<B>Status:</B> %s\n", status); |
| |
945 if (desc) |
| |
946 at += g_snprintf(buf + at, sizeof(buf) - at, "<HR>%s<br>\n", desc); |
| |
947 |
| |
948 g_show_info_text(buf); |
| |
949 g_free(buddy); |
| |
950 } |
| |
951 |
| |
952 static void jabber_handleauthresp(gjconn j, jpacket p) |
| |
953 { |
| |
954 if (jpacket_subtype(p) == JPACKET__RESULT) { |
| |
955 debug_printf("auth success\n"); |
| |
956 |
| |
957 account_online(GJ_GC(j)); |
| |
958 serv_finish_login(GJ_GC(j)); |
| |
959 |
| |
960 if (bud_list_cache_exists(GJ_GC(j))) |
| |
961 do_import(NULL, GJ_GC(j)); |
| |
962 |
| |
963 ((struct jabber_data *)GJ_GC(j)->proto_data)->did_import = TRUE; |
| |
964 |
| |
965 gjab_reqroster(j); |
| |
966 } else { |
| |
967 xmlnode xerr; |
| |
968 char *errmsg = NULL; |
| |
969 int errcode = 0; |
| |
970 |
| |
971 debug_printf("auth failed\n"); |
| |
972 xerr = xmlnode_get_tag(p->x, "error"); |
| |
973 if (xerr) { |
| |
974 char msg[BUF_LONG]; |
| |
975 errmsg = xmlnode_get_data(xerr); |
| |
976 if (xmlnode_get_attrib(xerr, "code")) { |
| |
977 errcode = atoi(xmlnode_get_attrib(xerr, "code")); |
| |
978 g_snprintf(msg, sizeof(msg), "Error %d: %s", errcode, errmsg); |
| |
979 } else |
| |
980 g_snprintf(msg, sizeof(msg), "%s", errmsg); |
| |
981 hide_login_progress(GJ_GC(j), msg); |
| |
982 } else { |
| |
983 hide_login_progress(GJ_GC(j), "Unknown login error"); |
| |
984 } |
| |
985 |
| |
986 signoff(GJ_GC(j)); |
| |
987 } |
| |
988 } |
| |
989 |
| |
990 static void jabber_handleversion(gjconn j, xmlnode iqnode) { |
| |
991 xmlnode querynode, x; |
| |
992 char *id, *from; |
| |
993 char os[1024]; |
| |
994 struct utsname osinfo; |
| |
995 |
| |
996 uname(&osinfo); |
| |
997 g_snprintf(os, sizeof os, "%s %s %s", osinfo.sysname, osinfo.release, osinfo.machine); |
| |
998 |
| |
999 id = xmlnode_get_attrib(iqnode, "id"); |
| |
1000 from = xmlnode_get_attrib(iqnode, "from"); |
| |
1001 |
| |
1002 x = jutil_iqnew(JPACKET__RESULT, NS_VERSION); |
| |
1003 |
| |
1004 xmlnode_put_attrib(x, "to", from); |
| |
1005 xmlnode_put_attrib(x, "id", id); |
| |
1006 querynode = xmlnode_get_tag(x, "query"); |
| |
1007 xmlnode_insert_cdata(xmlnode_insert_tag(querynode, "name"), PACKAGE, -1); |
| |
1008 xmlnode_insert_cdata(xmlnode_insert_tag(querynode, "version"), VERSION, -1); |
| |
1009 xmlnode_insert_cdata(xmlnode_insert_tag(querynode, "os"), os, -1); |
| |
1010 |
| |
1011 gjab_send(j, x); |
| |
1012 |
| |
1013 xmlnode_free(x); |
| |
1014 } |
| |
1015 |
| |
1016 static void jabber_handletime(gjconn j, xmlnode iqnode) { |
| |
1017 xmlnode querynode, x; |
| |
1018 char *id, *from; |
| |
1019 time_t now_t; |
| |
1020 struct tm *now; |
| |
1021 char buf[1024]; |
| |
1022 |
| |
1023 time(&now_t); |
| |
1024 now = localtime(&now_t); |
| |
1025 |
| |
1026 id = xmlnode_get_attrib(iqnode, "id"); |
| |
1027 from = xmlnode_get_attrib(iqnode, "from"); |
| |
1028 |
| |
1029 x = jutil_iqnew(JPACKET__RESULT, NS_TIME); |
| |
1030 |
| |
1031 xmlnode_put_attrib(x, "to", from); |
| |
1032 xmlnode_put_attrib(x, "id", id); |
| |
1033 querynode = xmlnode_get_tag(x, "query"); |
| |
1034 |
| |
1035 strftime(buf, 1024, "%Y%m%dT%T", now); |
| |
1036 xmlnode_insert_cdata(xmlnode_insert_tag(querynode, "utc"), buf, -1); |
| |
1037 strftime(buf, 1024, "%Z", now); |
| |
1038 xmlnode_insert_cdata(xmlnode_insert_tag(querynode, "tz"), buf, -1); |
| |
1039 strftime(buf, 1024, "%d %b %Y %T", now); |
| |
1040 xmlnode_insert_cdata(xmlnode_insert_tag(querynode, "display"), buf, -1); |
| |
1041 |
| |
1042 gjab_send(j, x); |
| |
1043 |
| |
1044 xmlnode_free(x); |
| |
1045 } |
| |
1046 |
| |
1047 static void jabber_handlelast(gjconn j, xmlnode iqnode) { |
| |
1048 xmlnode x, querytag; |
| |
1049 char *id, *from; |
| |
1050 struct jabber_data *jd = GJ_GC(j)->proto_data; |
| |
1051 char idle_time[32]; |
| |
1052 |
| |
1053 id = xmlnode_get_attrib(iqnode, "id"); |
| |
1054 from = xmlnode_get_attrib(iqnode, "from"); |
| |
1055 |
| |
1056 x = jutil_iqnew(JPACKET__RESULT, "jabber:iq:last"); |
| |
1057 |
| |
1058 xmlnode_put_attrib(x, "to", from); |
| |
1059 xmlnode_put_attrib(x, "id", id); |
| |
1060 querytag = xmlnode_get_tag(x, "query"); |
| |
1061 g_snprintf(idle_time, sizeof idle_time, "%ld", jd->idle ? time(NULL) - jd->idle : 0); |
| |
1062 xmlnode_put_attrib(querytag, "seconds", idle_time); |
| |
1063 |
| |
1064 gjab_send(j, x); |
| |
1065 xmlnode_free(x); |
| |
1066 } |
| |
1067 |
| |
1068 static void jabber_handlepacket(gjconn j, jpacket p) |
| |
1069 { |
| |
1070 switch (p->type) { |
| |
1071 case JPACKET_MESSAGE: |
| |
1072 jabber_handlemessage(j, p); |
| |
1073 break; |
| |
1074 case JPACKET_PRESENCE: |
| |
1075 jabber_handlepresence(j, p); |
| |
1076 break; |
| |
1077 case JPACKET_IQ: |
| |
1078 debug_printf("jpacket_subtype: %d\n", jpacket_subtype(p)); |
| |
1079 |
| |
1080 if (xmlnode_get_attrib(p->x, "id") && (strcmp(xmlnode_get_attrib(p->x, "id"), IQID_AUTH) == 0)) { |
| |
1081 jabber_handleauthresp(j, p); |
| |
1082 break; /* I'm not sure if you like this style, Eric. */ |
| |
1083 } |
| |
1084 |
| |
1085 if (jpacket_subtype(p) == JPACKET__SET) { |
| |
1086 } else if (jpacket_subtype(p) == JPACKET__GET) { |
| |
1087 xmlnode querynode; |
| |
1088 querynode = xmlnode_get_tag(p->x, "query"); |
| |
1089 if(NSCHECK(querynode, NS_VERSION)) { |
| |
1090 jabber_handleversion(j, p->x); |
| |
1091 } else if (NSCHECK(querynode, NS_TIME)) { |
| |
1092 jabber_handletime(j, p->x); |
| |
1093 } else if (NSCHECK(querynode, "jabber:iq:last")) { |
| |
1094 jabber_handlelast(j, p->x); |
| |
1095 } |
| |
1096 } else if (jpacket_subtype(p) == JPACKET__RESULT) { |
| |
1097 xmlnode querynode, vcard; |
| |
1098 char *xmlns, *from; |
| |
1099 |
| |
1100 from = xmlnode_get_attrib(p->x, "from"); |
| |
1101 querynode = xmlnode_get_tag(p->x, "query"); |
| |
1102 xmlns = xmlnode_get_attrib(querynode, "xmlns"); |
| |
1103 vcard = xmlnode_get_tag(p->x, "vCard"); |
| |
1104 |
| |
1105 if (NSCHECK(querynode, NS_ROSTER)) { |
| |
1106 jabber_handleroster(j, querynode); |
| |
1107 } else if (NSCHECK(querynode, NS_VCARD)) { |
| |
1108 jabber_handlevcard(j, querynode, from); |
| |
1109 } else if(vcard) { |
| |
1110 jabber_handlevcard(j, vcard, from); |
| |
1111 } else { |
| |
1112 /* debug_printf("jabber:iq:query: %s\n", xmlns); */ |
| |
1113 } |
| |
1114 |
| |
1115 } else if (jpacket_subtype(p) == JPACKET__ERROR) { |
| |
1116 xmlnode xerr; |
| |
1117 char *from, *errmsg = NULL; |
| |
1118 int errcode = 0; |
| |
1119 |
| |
1120 from = xmlnode_get_attrib(p->x, "from"); |
| |
1121 xerr = xmlnode_get_tag(p->x, "error"); |
| |
1122 if (xerr) { |
| |
1123 errmsg = xmlnode_get_data(xerr); |
| |
1124 if (xmlnode_get_attrib(xerr, "code")) |
| |
1125 errcode = atoi(xmlnode_get_attrib(xerr, "code")); |
| |
1126 } |
| |
1127 |
| |
1128 from = g_strdup_printf("Error %d (%s)", errcode, from); |
| |
1129 do_error_dialog(errmsg, from); |
| |
1130 g_free(from); |
| |
1131 |
| |
1132 } |
| |
1133 |
| |
1134 break; |
| |
1135 |
| |
1136 case JPACKET_S10N: |
| |
1137 jabber_handles10n(j, p); |
| |
1138 break; |
| |
1139 default: |
| |
1140 debug_printf("jabber: packet type %d (%s)\n", p->type, xmlnode2str(p->x)); |
| |
1141 } |
| |
1142 |
| |
1143 xmlnode_free(p->x); |
| |
1144 |
| |
1145 return; |
| |
1146 } |
| |
1147 |
| |
1148 static void jabber_handlestate(gjconn j, int state) |
| |
1149 { |
| |
1150 switch (state) { |
| |
1151 case JCONN_STATE_OFF: |
| |
1152 hide_login_progress(GJ_GC(j), "Unable to connect"); |
| |
1153 signoff(GJ_GC(j)); |
| |
1154 break; |
| |
1155 case JCONN_STATE_CONNECTED: |
| |
1156 set_login_progress(GJ_GC(j), 3, "Connected"); |
| |
1157 break; |
| |
1158 case JCONN_STATE_ON: |
| |
1159 set_login_progress(GJ_GC(j), 5, "Logging in..."); |
| |
1160 gjab_auth(j); |
| |
1161 break; |
| |
1162 default: |
| |
1163 debug_printf("state change: %d\n", state); |
| |
1164 } |
| |
1165 return; |
| |
1166 } |
| |
1167 |
| |
1168 static void jabber_login(struct aim_user *user) |
| |
1169 { |
| |
1170 struct gaim_connection *gc = new_gaim_conn(user); |
| |
1171 struct jabber_data *jd = gc->proto_data = g_new0(struct jabber_data, 1); |
| |
1172 char *loginname = create_valid_jid(user->username, DEFAULT_SERVER, "GAIM"); |
| |
1173 |
| |
1174 jd->hash = g_hash_table_new(g_str_hash, g_str_equal); |
| |
1175 |
| |
1176 set_login_progress(gc, 1, "Connecting"); |
| |
1177 |
| |
1178 if (!(jd->jc = gjab_new(loginname, user->password, gc))) { |
| |
1179 g_free(loginname); |
| |
1180 debug_printf("jabber: unable to connect (jab_new failed)\n"); |
| |
1181 hide_login_progress(gc, "Unable to connect"); |
| |
1182 signoff(gc); |
| |
1183 return; |
| |
1184 } |
| |
1185 |
| |
1186 g_free(loginname); |
| |
1187 gjab_state_handler(jd->jc, jabber_handlestate); |
| |
1188 gjab_packet_handler(jd->jc, jabber_handlepacket); |
| |
1189 gjab_start(jd->jc); |
| |
1190 } |
| |
1191 |
| |
1192 static gboolean jabber_destroy_hash(gpointer key, gpointer val, gpointer data) { |
| |
1193 g_free(key); |
| |
1194 g_free(val); |
| |
1195 return TRUE; |
| |
1196 } |
| |
1197 |
| |
1198 static gboolean jabber_free(gpointer data) |
| |
1199 { |
| |
1200 gjab_delete(data); |
| |
1201 return FALSE; |
| |
1202 } |
| |
1203 |
| |
1204 static void jabber_close(struct gaim_connection *gc) |
| |
1205 { |
| |
1206 struct jabber_data *jd = gc->proto_data; |
| |
1207 g_hash_table_foreach_remove(jd->hash, jabber_destroy_hash, NULL); |
| |
1208 g_hash_table_destroy(jd->hash); |
| |
1209 gdk_input_remove(gc->inpa); |
| |
1210 close(jd->jc->fd); |
| |
1211 gtk_timeout_add(50, jabber_free, jd->jc); |
| |
1212 jd->jc = NULL; |
| |
1213 g_free(jd); |
| |
1214 gc->proto_data = NULL; |
| |
1215 } |
| |
1216 |
| |
1217 static void jabber_send_im(struct gaim_connection *gc, char *who, char *message, int away) |
| |
1218 { |
| |
1219 xmlnode x, y; |
| |
1220 char *realwho; |
| |
1221 gjconn j = ((struct jabber_data *)gc->proto_data)->jc; |
| |
1222 |
| |
1223 if (!who || !message) |
| |
1224 return; |
| |
1225 |
| |
1226 x = xmlnode_new_tag("message"); |
| |
1227 if (!strchr(who, '@')) |
| |
1228 realwho = g_strdup_printf("%s@%s", who, j->user->server); |
| |
1229 else |
| |
1230 realwho = g_strdup(who); |
| |
1231 xmlnode_put_attrib(x, "to", realwho); |
| |
1232 g_free(realwho); |
| |
1233 |
| |
1234 xmlnode_put_attrib(x, "type", "chat"); |
| |
1235 |
| |
1236 if (message && strlen(message)) { |
| |
1237 char *utf8 = str_to_utf8(message); |
| |
1238 y = xmlnode_insert_tag(x, "body"); |
| |
1239 xmlnode_insert_cdata(y, utf8, -1); |
| |
1240 g_free(utf8); |
| |
1241 } |
| |
1242 |
| |
1243 gjab_send(((struct jabber_data *)gc->proto_data)->jc, x); |
| |
1244 xmlnode_free(x); |
| |
1245 } |
| |
1246 |
| |
1247 static void jabber_add_buddy(struct gaim_connection *gc, char *name) |
| |
1248 { |
| |
1249 xmlnode x, y; |
| |
1250 char *realwho; |
| |
1251 gjconn j = ((struct jabber_data *)gc->proto_data)->jc; |
| |
1252 |
| |
1253 if (!((struct jabber_data *)gc->proto_data)->did_import) |
| |
1254 return; |
| |
1255 |
| |
1256 if (!name) |
| |
1257 return; |
| |
1258 |
| |
1259 if (!strcmp(gc->username, name)) |
| |
1260 return; |
| |
1261 |
| |
1262 if (!strchr(name, '@')) |
| |
1263 realwho = g_strdup_printf("%s@%s", name, j->user->server); |
| |
1264 else { |
| |
1265 jid who = jid_new(j->p, name); |
| |
1266 if (who->user == NULL) { |
| |
1267 /* FIXME: transport */ |
| |
1268 return; |
| |
1269 } |
| |
1270 realwho = g_strdup_printf("%s@%s", who->user, who->server); |
| |
1271 } |
| |
1272 |
| |
1273 x = jutil_iqnew(JPACKET__SET, NS_ROSTER); |
| |
1274 y = xmlnode_insert_tag(xmlnode_get_tag(x, "query"), "item"); |
| |
1275 xmlnode_put_attrib(y, "jid", realwho); |
| |
1276 gjab_send(((struct jabber_data *)gc->proto_data)->jc, x); |
| |
1277 xmlnode_free(x); |
| |
1278 |
| |
1279 x = xmlnode_new_tag("presence"); |
| |
1280 xmlnode_put_attrib(x, "to", realwho); |
| |
1281 xmlnode_put_attrib(x, "type", "subscribe"); |
| |
1282 gjab_send(((struct jabber_data *)gc->proto_data)->jc, x); |
| |
1283 |
| |
1284 g_free(realwho); |
| |
1285 } |
| |
1286 |
| |
1287 static void jabber_remove_buddy(struct gaim_connection *gc, char *name) |
| |
1288 { |
| |
1289 xmlnode x, y; |
| |
1290 char *realwho; |
| |
1291 gjconn j = ((struct jabber_data *)gc->proto_data)->jc; |
| |
1292 |
| |
1293 if (!name) |
| |
1294 return; |
| |
1295 |
| |
1296 if (!strchr(name, '@')) |
| |
1297 realwho = g_strdup_printf("%s@%s", name, j->user->server); |
| |
1298 else |
| |
1299 realwho = g_strdup(name); |
| |
1300 |
| |
1301 x = jutil_iqnew(JPACKET__SET, NS_ROSTER); |
| |
1302 y = xmlnode_insert_tag(xmlnode_get_tag(x, "query"), "item"); |
| |
1303 xmlnode_put_attrib(y, "jid", realwho); |
| |
1304 xmlnode_put_attrib(y, "subscription", "remove"); |
| |
1305 gjab_send(((struct jabber_data *)gc->proto_data)->jc, x); |
| |
1306 |
| |
1307 g_free(realwho); |
| |
1308 xmlnode_free(x); |
| |
1309 } |
| |
1310 |
| |
1311 static char **jabber_list_icon(int uc) |
| |
1312 { |
| |
1313 switch (uc) { |
| |
1314 case UC_AWAY: |
| |
1315 return available_away_xpm; |
| |
1316 case UC_CHAT: |
| |
1317 return available_chat_xpm; |
| |
1318 case UC_XA: |
| |
1319 return available_xa_xpm; |
| |
1320 case UC_DND: |
| |
1321 return available_dnd_xpm; |
| |
1322 default: |
| |
1323 return available_xpm; |
| |
1324 } |
| |
1325 } |
| |
1326 |
| |
1327 static void jabber_join_chat(struct gaim_connection *gc, int exch, char *name) |
| |
1328 { |
| |
1329 xmlnode x; |
| |
1330 char *realwho; |
| |
1331 gjconn j = ((struct jabber_data *)gc->proto_data)->jc; |
| |
1332 GSList *pc = ((struct jabber_data *)gc->proto_data)->pending_chats; |
| |
1333 struct jabber_chat *jc; |
| |
1334 |
| |
1335 if (!name) |
| |
1336 return; |
| |
1337 |
| |
1338 jc = g_new0(struct jabber_chat, 1); |
| |
1339 realwho = create_valid_jid(name, DEFAULT_GROUPCHAT, j->user->user); |
| |
1340 jc->Jid = jid_new(j->p, realwho); |
| |
1341 jc->gc = gc; |
| |
1342 debug_printf("%s\n", realwho); |
| |
1343 |
| |
1344 x = jutil_presnew(0, realwho, NULL); |
| |
1345 gjab_send(j, x); |
| |
1346 xmlnode_free(x); |
| |
1347 g_free(realwho); |
| |
1348 |
| |
1349 ((struct jabber_data *)gc->proto_data)->pending_chats = g_slist_append(pc, jc); |
| |
1350 } |
| |
1351 |
| |
1352 static void jabber_chat_invite(struct gaim_connection *gc, int id, char *message, char *name) |
| |
1353 { |
| |
1354 xmlnode x, y; |
| |
1355 GSList *bcs = gc->buddy_chats; |
| |
1356 struct conversation *b; |
| |
1357 struct jabber_data *jd = gc->proto_data; |
| |
1358 gjconn j = jd->jc; |
| |
1359 struct jabber_chat *jc; |
| |
1360 char *realwho, *subject; |
| |
1361 |
| |
1362 if (!name) |
| |
1363 return; |
| |
1364 |
| |
1365 /* find which chat we're inviting to */ |
| |
1366 while (bcs) { |
| |
1367 b = bcs->data; |
| |
1368 if (id == b->id) |
| |
1369 break; |
| |
1370 bcs = bcs->next; |
| |
1371 } |
| |
1372 if (!bcs) |
| |
1373 return; |
| |
1374 |
| |
1375 bcs = jd->existing_chats; |
| |
1376 while (bcs) { |
| |
1377 jc = bcs->data; |
| |
1378 if (jc->b == b) |
| |
1379 break; |
| |
1380 bcs = bcs->next; |
| |
1381 } |
| |
1382 if (!bcs) |
| |
1383 return; |
| |
1384 |
| |
1385 x = xmlnode_new_tag("message"); |
| |
1386 if (!strchr(name, '@')) |
| |
1387 realwho = g_strdup_printf("%s@%s", name, j->user->server); |
| |
1388 else |
| |
1389 realwho = g_strdup(name); |
| |
1390 xmlnode_put_attrib(x, "to", realwho); |
| |
1391 g_free(realwho); |
| |
1392 |
| |
1393 y = xmlnode_insert_tag(x, "x"); |
| |
1394 xmlnode_put_attrib(y, "xmlns", "jabber:x:conference"); |
| |
1395 subject = g_strdup_printf("%s@%s", jc->Jid->user, jc->Jid->server); |
| |
1396 xmlnode_put_attrib(y, "jid", subject); |
| |
1397 g_free(subject); |
| |
1398 |
| |
1399 if (message && strlen(message)) { |
| |
1400 char *utf8 = str_to_utf8(message); |
| |
1401 y = xmlnode_insert_tag(x, "body"); |
| |
1402 xmlnode_insert_cdata(y, utf8, -1); |
| |
1403 g_free(utf8); |
| |
1404 } |
| |
1405 |
| |
1406 gjab_send(((struct jabber_data *)gc->proto_data)->jc, x); |
| |
1407 xmlnode_free(x); |
| |
1408 } |
| |
1409 |
| |
1410 static void jabber_chat_leave(struct gaim_connection *gc, int id) |
| |
1411 { |
| |
1412 GSList *bcs = gc->buddy_chats; |
| |
1413 struct conversation *b; |
| |
1414 struct jabber_data *jd = gc->proto_data; |
| |
1415 gjconn j = jd->jc; |
| |
1416 struct jabber_chat *jc; |
| |
1417 char *realwho; |
| |
1418 xmlnode x; |
| |
1419 |
| |
1420 while (bcs) { |
| |
1421 b = bcs->data; |
| |
1422 if (id == b->id) |
| |
1423 break; |
| |
1424 bcs = bcs->next; |
| |
1425 } |
| |
1426 if (!bcs) |
| |
1427 return; |
| |
1428 |
| |
1429 bcs = jd->existing_chats; |
| |
1430 while (bcs) { |
| |
1431 jc = bcs->data; |
| |
1432 if (jc->b == b) |
| |
1433 break; |
| |
1434 bcs = bcs->next; |
| |
1435 } |
| |
1436 if (!bcs) |
| |
1437 return; |
| |
1438 |
| |
1439 realwho = g_strdup_printf("%s@%s", jc->Jid->user, jc->Jid->server); |
| |
1440 x = jutil_presnew(0, realwho, NULL); |
| |
1441 g_free(realwho); |
| |
1442 xmlnode_put_attrib(x, "type", "unavailable"); |
| |
1443 gjab_send(j, x); |
| |
1444 xmlnode_free(x); |
| |
1445 jc->b = NULL; |
| |
1446 } |
| |
1447 |
| |
1448 static void jabber_chat_send(struct gaim_connection *gc, int id, char *message) |
| |
1449 { |
| |
1450 GSList *bcs = gc->buddy_chats; |
| |
1451 struct conversation *b; |
| |
1452 struct jabber_data *jd = gc->proto_data; |
| |
1453 xmlnode x, y; |
| |
1454 struct jabber_chat *jc; |
| |
1455 char *chatname; |
| |
1456 |
| |
1457 while (bcs) { |
| |
1458 b = bcs->data; |
| |
1459 if (id == b->id) |
| |
1460 break; |
| |
1461 bcs = bcs->next; |
| |
1462 } |
| |
1463 if (!bcs) |
| |
1464 return; |
| |
1465 |
| |
1466 bcs = jd->existing_chats; |
| |
1467 while (bcs) { |
| |
1468 jc = bcs->data; |
| |
1469 if (jc->b == b) |
| |
1470 break; |
| |
1471 bcs = bcs->next; |
| |
1472 } |
| |
1473 if (!bcs) |
| |
1474 return; |
| |
1475 |
| |
1476 x = xmlnode_new_tag("message"); |
| |
1477 xmlnode_put_attrib(x, "from", jid_full(jc->Jid)); |
| |
1478 chatname = g_strdup_printf("%s@%s", jc->Jid->user, jc->Jid->server); |
| |
1479 xmlnode_put_attrib(x, "to", chatname); |
| |
1480 g_free(chatname); |
| |
1481 xmlnode_put_attrib(x, "type", "groupchat"); |
| |
1482 |
| |
1483 if (message && strlen(message)) { |
| |
1484 char *utf8 = str_to_utf8(message); |
| |
1485 y = xmlnode_insert_tag(x, "body"); |
| |
1486 xmlnode_insert_cdata(y, utf8, -1); |
| |
1487 g_free(utf8); |
| |
1488 } |
| |
1489 |
| |
1490 gjab_send(((struct jabber_data *)gc->proto_data)->jc, x); |
| |
1491 xmlnode_free(x); |
| |
1492 } |
| |
1493 |
| |
1494 static void jabber_chat_set_topic(struct gaim_connection *gc, int id, char *topic) |
| |
1495 { |
| |
1496 GSList *bcs = gc->buddy_chats; |
| |
1497 struct conversation *b; |
| |
1498 struct jabber_data *jd = gc->proto_data; |
| |
1499 xmlnode x, y; |
| |
1500 struct jabber_chat *jc; |
| |
1501 char *chatname; |
| |
1502 char buf[8192]; |
| |
1503 |
| |
1504 while (bcs) { |
| |
1505 b = bcs->data; |
| |
1506 if (id == b->id) |
| |
1507 break; |
| |
1508 bcs = bcs->next; |
| |
1509 } |
| |
1510 if (!bcs) |
| |
1511 return; |
| |
1512 |
| |
1513 bcs = jd->existing_chats; |
| |
1514 while (bcs) { |
| |
1515 jc = bcs->data; |
| |
1516 if (jc->b == b) |
| |
1517 break; |
| |
1518 bcs = bcs->next; |
| |
1519 } |
| |
1520 if (!bcs) |
| |
1521 return; |
| |
1522 |
| |
1523 x = xmlnode_new_tag("message"); |
| |
1524 xmlnode_put_attrib(x, "from", jid_full(jc->Jid)); |
| |
1525 chatname = g_strdup_printf("%s@%s", jc->Jid->user, jc->Jid->server); |
| |
1526 xmlnode_put_attrib(x, "to", chatname); |
| |
1527 g_free(chatname); |
| |
1528 xmlnode_put_attrib(x, "type", "groupchat"); |
| |
1529 |
| |
1530 if (topic && strlen(topic)) { |
| |
1531 char *utf8 = str_to_utf8(topic); |
| |
1532 y = xmlnode_insert_tag(x, "subject"); |
| |
1533 xmlnode_insert_cdata(y, utf8, -1); |
| |
1534 y = xmlnode_insert_tag(x, "body"); |
| |
1535 g_snprintf(buf, sizeof(buf), "/me has changed the subject to: %s", utf8); |
| |
1536 xmlnode_insert_cdata(y, buf, -1); |
| |
1537 g_free(utf8); |
| |
1538 } |
| |
1539 |
| |
1540 gjab_send(((struct jabber_data *)gc->proto_data)->jc, x); |
| |
1541 xmlnode_free(x); |
| |
1542 } |
| |
1543 |
| |
1544 static void jabber_chat_whisper(struct gaim_connection *gc, int id, char *who, char *message) |
| |
1545 { |
| |
1546 GSList *bcs = gc->buddy_chats; |
| |
1547 struct conversation *b; |
| |
1548 struct jabber_data *jd = gc->proto_data; |
| |
1549 xmlnode x, y; |
| |
1550 struct jabber_chat *jc; |
| |
1551 char *chatname; |
| |
1552 |
| |
1553 while (bcs) { |
| |
1554 b = bcs->data; |
| |
1555 if (id == b->id) |
| |
1556 break; |
| |
1557 bcs = bcs->next; |
| |
1558 } |
| |
1559 if (!bcs) |
| |
1560 return; |
| |
1561 |
| |
1562 bcs = jd->existing_chats; |
| |
1563 while (bcs) { |
| |
1564 jc = bcs->data; |
| |
1565 if (jc->b == b) |
| |
1566 break; |
| |
1567 bcs = bcs->next; |
| |
1568 } |
| |
1569 if (!bcs) |
| |
1570 return; |
| |
1571 |
| |
1572 x = xmlnode_new_tag("message"); |
| |
1573 xmlnode_put_attrib(x, "from", jid_full(jc->Jid)); |
| |
1574 chatname = g_strdup_printf("%s@%s/%s", jc->Jid->user, jc->Jid->server, who); |
| |
1575 xmlnode_put_attrib(x, "to", chatname); |
| |
1576 g_free(chatname); |
| |
1577 xmlnode_put_attrib(x, "type", "normal"); |
| |
1578 |
| |
1579 if (message && strlen(message)) { |
| |
1580 char *utf8 = str_to_utf8(message); |
| |
1581 y = xmlnode_insert_tag(x, "body"); |
| |
1582 xmlnode_insert_cdata(y, utf8, -1); |
| |
1583 g_free(utf8); |
| |
1584 } |
| |
1585 |
| |
1586 gjab_send(((struct jabber_data *)gc->proto_data)->jc, x); |
| |
1587 xmlnode_free(x); |
| |
1588 } |
| |
1589 |
| |
1590 static GtkWidget *newname = NULL; |
| |
1591 static GtkWidget *newpass1 = NULL; |
| |
1592 static GtkWidget *newpass2 = NULL; |
| |
1593 static GtkWidget *newserv = NULL; |
| |
1594 static jconn regjconn = NULL; |
| |
1595 static int reginpa = 0; |
| |
1596 |
| |
1597 static void newdes() |
| |
1598 { |
| |
1599 newname = newpass1 = newpass2 = newserv = NULL; |
| |
1600 } |
| |
1601 |
| |
1602 static void jabber_draw_new_user(GtkWidget *box) |
| |
1603 { |
| |
1604 GtkWidget *hbox; |
| |
1605 GtkWidget *label; |
| |
1606 |
| |
1607 if (newname) |
| |
1608 return; |
| |
1609 |
| |
1610 label = gtk_label_new("Enter your name, password, and server to register on. If you " |
| |
1611 "already have a Jabber account and do not need to register one, " |
| |
1612 "use the Account Editor to add it to your list of accounts."); |
| |
1613 gtk_label_set_line_wrap(GTK_LABEL(label), TRUE); |
| |
1614 gtk_box_pack_start(GTK_BOX(box), label, FALSE, FALSE, 5); |
| |
1615 gtk_widget_show(label); |
| |
1616 |
| |
1617 hbox = gtk_hbox_new(FALSE, 5); |
| |
1618 gtk_box_pack_start(GTK_BOX(box), hbox, FALSE, FALSE, 5); |
| |
1619 gtk_widget_show(hbox); |
| |
1620 |
| |
1621 label = gtk_label_new("Username:"); |
| |
1622 gtk_box_pack_start(GTK_BOX(hbox), label, FALSE, FALSE, 5); |
| |
1623 gtk_widget_show(label); |
| |
1624 |
| |
1625 newname = gtk_entry_new(); |
| |
1626 gtk_box_pack_end(GTK_BOX(hbox), newname, FALSE, FALSE, 5); |
| |
1627 gtk_signal_connect(GTK_OBJECT(newname), "destroy", GTK_SIGNAL_FUNC(newdes), NULL); |
| |
1628 gtk_widget_show(newname); |
| |
1629 |
| |
1630 hbox = gtk_hbox_new(FALSE, 5); |
| |
1631 gtk_box_pack_start(GTK_BOX(box), hbox, FALSE, FALSE, 5); |
| |
1632 gtk_widget_show(hbox); |
| |
1633 |
| |
1634 label = gtk_label_new("Password:"); |
| |
1635 gtk_box_pack_start(GTK_BOX(hbox), label, FALSE, FALSE, 5); |
| |
1636 gtk_widget_show(label); |
| |
1637 |
| |
1638 newpass1 = gtk_entry_new(); |
| |
1639 gtk_box_pack_end(GTK_BOX(hbox), newpass1, FALSE, FALSE, 5); |
| |
1640 gtk_entry_set_visibility(GTK_ENTRY(newpass1), FALSE); |
| |
1641 gtk_widget_show(newpass1); |
| |
1642 |
| |
1643 hbox = gtk_hbox_new(FALSE, 5); |
| |
1644 gtk_box_pack_start(GTK_BOX(box), hbox, FALSE, FALSE, 5); |
| |
1645 gtk_widget_show(hbox); |
| |
1646 |
| |
1647 label = gtk_label_new("Confirm:"); |
| |
1648 gtk_box_pack_start(GTK_BOX(hbox), label, FALSE, FALSE, 5); |
| |
1649 gtk_widget_show(label); |
| |
1650 |
| |
1651 newpass2 = gtk_entry_new(); |
| |
1652 gtk_box_pack_end(GTK_BOX(hbox), newpass2, FALSE, FALSE, 5); |
| |
1653 gtk_entry_set_visibility(GTK_ENTRY(newpass2), FALSE); |
| |
1654 gtk_widget_show(newpass2); |
| |
1655 |
| |
1656 hbox = gtk_hbox_new(FALSE, 5); |
| |
1657 gtk_box_pack_start(GTK_BOX(box), hbox, FALSE, FALSE, 5); |
| |
1658 gtk_widget_show(hbox); |
| |
1659 |
| |
1660 label = gtk_label_new("Server:"); |
| |
1661 gtk_box_pack_start(GTK_BOX(hbox), label, FALSE, FALSE, 5); |
| |
1662 gtk_widget_show(label); |
| |
1663 |
| |
1664 newserv = gtk_entry_new(); |
| |
1665 gtk_entry_set_text(GTK_ENTRY(newserv), "jabber.org"); |
| |
1666 gtk_box_pack_end(GTK_BOX(hbox), newserv, FALSE, FALSE, 5); |
| |
1667 gtk_widget_show(newserv); |
| |
1668 } |
| |
1669 |
| |
1670 static void regstate(jconn j, int state) |
| |
1671 { |
| |
1672 static int catch = 0; |
| |
1673 switch (state) { |
| |
1674 case JCONN_STATE_OFF: |
| |
1675 gdk_input_remove(reginpa); |
| |
1676 reginpa = 0; |
| |
1677 jab_delete(j); |
| |
1678 break; |
| |
1679 case JCONN_STATE_CONNECTED: |
| |
1680 break; |
| |
1681 case JCONN_STATE_ON: |
| |
1682 if (catch) |
| |
1683 break; |
| |
1684 catch = 1; |
| |
1685 jab_reg(regjconn); |
| |
1686 catch = 0; |
| |
1687 break; |
| |
1688 case JCONN_STATE_AUTH: |
| |
1689 break; |
| |
1690 default: |
| |
1691 break; |
| |
1692 } |
| |
1693 } |
| |
1694 |
| |
1695 static void regpacket(jconn j, jpacket p) |
| |
1696 { |
| |
1697 static int here = 0; |
| |
1698 switch (p->type) { |
| |
1699 case JPACKET_MESSAGE: |
| |
1700 break; |
| |
1701 case JPACKET_PRESENCE: |
| |
1702 break; |
| |
1703 case JPACKET_IQ: |
| |
1704 if (jpacket_subtype(p) == JPACKET__RESULT) { |
| |
1705 xmlnode x, y, z; |
| |
1706 char *user, *id; |
| |
1707 |
| |
1708 if (here == 2) { |
| |
1709 struct aim_user *u; |
| |
1710 user = g_strdup(jid_full(j->user)); |
| |
1711 regjconn = NULL; |
| |
1712 here = 0; |
| |
1713 u = new_user(user, PROTO_JABBER, OPT_USR_REM_PASS); |
| |
1714 g_free(user); |
| |
1715 g_snprintf(u->password, sizeof(u->password), "%s", j->pass); |
| |
1716 save_prefs(); |
| |
1717 xmlnode_free(p->x); |
| |
1718 do_error_dialog("Registration successful! Your account has been" |
| |
1719 " added to the Account Editor.", "Jabber " |
| |
1720 "Registration"); |
| |
1721 gtk_entry_set_text(GTK_ENTRY(newname), ""); |
| |
1722 gtk_entry_set_text(GTK_ENTRY(newpass1), ""); |
| |
1723 gtk_entry_set_text(GTK_ENTRY(newpass2), ""); |
| |
1724 return; |
| |
1725 } else if (here == 1) { |
| |
1726 x = jutil_iqnew(JPACKET__SET, NS_AUTH); |
| |
1727 here = 2; |
| |
1728 } else { /* here == 0 */ |
| |
1729 here = 1; |
| |
1730 x = jutil_iqnew(JPACKET__GET, NS_AUTH); |
| |
1731 } |
| |
1732 |
| |
1733 id = jab_getid(j); |
| |
1734 xmlnode_put_attrib(x, "id", id); |
| |
1735 y = xmlnode_get_tag(x, "query"); |
| |
1736 |
| |
1737 user = j->user->user; |
| |
1738 if (user) |
| |
1739 { |
| |
1740 z = xmlnode_insert_tag(y, "username"); |
| |
1741 xmlnode_insert_cdata(z, user, -1); |
| |
1742 } |
| |
1743 |
| |
1744 if (here == 2) { |
| |
1745 z = xmlnode_insert_tag(y, "resource"); |
| |
1746 xmlnode_insert_cdata(z, j->user->resource, -1); |
| |
1747 z = xmlnode_insert_tag(y, "password"); |
| |
1748 xmlnode_insert_cdata(z, j->pass, -1); |
| |
1749 } |
| |
1750 |
| |
1751 jab_send(j, x); |
| |
1752 xmlnode_free(x); |
| |
1753 } else if (jpacket_subtype(p) == JPACKET__ERROR) { |
| |
1754 xmlnode x = xmlnode_get_tag(p->x, "error"); |
| |
1755 if (x) { |
| |
1756 char buf[8192]; |
| |
1757 g_snprintf(buf, sizeof(buf), "Registration failed: %d %s", |
| |
1758 atoi(xmlnode_get_attrib(x, "code")), |
| |
1759 xmlnode_get_data(xmlnode_get_firstchild(x))); |
| |
1760 do_error_dialog(buf, "Jabber Registration"); |
| |
1761 } else { |
| |
1762 do_error_dialog("Registration failed", "Jabber Registration"); |
| |
1763 } |
| |
1764 regjconn = NULL; |
| |
1765 xmlnode_free(p->x); |
| |
1766 here = 0; |
| |
1767 return; |
| |
1768 } |
| |
1769 break; |
| |
1770 case JPACKET_S10N: |
| |
1771 break; |
| |
1772 default: |
| |
1773 break; |
| |
1774 } |
| |
1775 |
| |
1776 xmlnode_free(p->x); |
| |
1777 } |
| |
1778 |
| |
1779 static void regjcall(gpointer data, gint source, GdkInputCondition cond) |
| |
1780 { |
| |
1781 gjab_recv((gjconn)regjconn); |
| |
1782 } |
| |
1783 |
| |
1784 static void jabber_do_new_user() |
| |
1785 { |
| |
1786 const char *name, *pass1, *pass2, *serv; |
| |
1787 char *tmp; |
| |
1788 char *user; |
| |
1789 |
| |
1790 if (!newname || regjconn) |
| |
1791 return; |
| |
1792 |
| |
1793 pass1 = gtk_entry_get_text(GTK_ENTRY(newpass1)); |
| |
1794 pass2 = gtk_entry_get_text(GTK_ENTRY(newpass2)); |
| |
1795 if (pass1[0] == 0 || pass2[0] == 0) { |
| |
1796 do_error_dialog("Please enter the same valid password in both password entry boxes", |
| |
1797 "Registration error"); |
| |
1798 return; |
| |
1799 } |
| |
1800 if (strcmp(pass1, pass2)) { |
| |
1801 do_error_dialog("Mismatched passwords, please verify that both passwords are the same", |
| |
1802 "Registration error"); |
| |
1803 return; |
| |
1804 } |
| |
1805 name = gtk_entry_get_text(GTK_ENTRY(newname)); |
| |
1806 serv = gtk_entry_get_text(GTK_ENTRY(newserv)); |
| |
1807 if (name[0] == 0 || serv[0] == 0) { |
| |
1808 do_error_dialog("Please enter a valid username and server", "Registration error"); |
| |
1809 return; |
| |
1810 } |
| |
1811 |
| |
1812 user = g_strdup_printf("%s@%s/GAIM", name, serv); |
| |
1813 tmp = g_strdup(pass1); |
| |
1814 regjconn = jab_new(user, tmp); |
| |
1815 g_free(tmp); |
| |
1816 g_free(user); |
| |
1817 |
| |
1818 jab_state_handler(regjconn, regstate); |
| |
1819 jab_packet_handler(regjconn, regpacket); |
| |
1820 |
| |
1821 jab_start(regjconn); |
| |
1822 reginpa = gdk_input_add(jab_getfd(regjconn), GDK_INPUT_READ, regjcall, NULL); |
| |
1823 } |
| |
1824 |
| |
1825 static char *jabber_normalize(const char *s) |
| |
1826 { |
| |
1827 static char buf[BUF_LEN]; |
| |
1828 char *t, *u; |
| |
1829 int x = 0; |
| |
1830 |
| |
1831 g_return_val_if_fail((s != NULL), NULL); |
| |
1832 |
| |
1833 u = t = g_strdup(s); |
| |
1834 |
| |
1835 g_strdown(t); |
| |
1836 |
| |
1837 while (*t && (x < BUF_LEN - 1)) { |
| |
1838 if (*t != ' ') |
| |
1839 buf[x++] = *t; |
| |
1840 t++; |
| |
1841 } |
| |
1842 buf[x] = '\0'; |
| |
1843 g_free(u); |
| |
1844 |
| |
1845 if (!strchr(buf, '@')) { |
| |
1846 strcat(buf, "@jabber.org"); /* this isn't always right, but eh */ |
| |
1847 } else if ((u = strchr(strchr(buf, '@'), '/')) != NULL) { |
| |
1848 *u = '\0'; |
| |
1849 } |
| |
1850 |
| |
1851 return buf; |
| |
1852 } |
| |
1853 |
| |
1854 static void jabber_get_info(struct gaim_connection *gc, char *who) { |
| |
1855 xmlnode x; |
| |
1856 char *id; |
| |
1857 struct jabber_data *jd = gc->proto_data; |
| |
1858 gjconn j = jd->jc; |
| |
1859 |
| |
1860 x = jutil_iqnew(JPACKET__GET, NS_VCARD); |
| |
1861 xmlnode_put_attrib(x, "to", who); |
| |
1862 id = gjab_getid(j); |
| |
1863 xmlnode_put_attrib(x, "id", id); |
| |
1864 |
| |
1865 gjab_send(j, x); |
| |
1866 |
| |
1867 xmlnode_free(x); |
| |
1868 |
| |
1869 } |
| |
1870 |
| |
1871 static void jabber_info(GtkObject *obj, char *who) { |
| |
1872 serv_get_info(gtk_object_get_user_data(obj), who); |
| |
1873 } |
| |
1874 |
| |
1875 static void jabber_buddy_menu(GtkWidget *menu, struct gaim_connection *gc, char *who) { |
| |
1876 GtkWidget *button; |
| |
1877 |
| |
1878 button = gtk_menu_item_new_with_label(_("Get Info")); |
| |
1879 gtk_signal_connect(GTK_OBJECT(button), "activate", |
| |
1880 GTK_SIGNAL_FUNC(jabber_info), who); |
| |
1881 gtk_object_set_user_data(GTK_OBJECT(button), gc); |
| |
1882 gtk_menu_append(GTK_MENU(menu), button); |
| |
1883 gtk_widget_show(button); |
| |
1884 } |
| |
1885 |
| |
1886 static GList *jabber_away_states() { |
| |
1887 GList *m = NULL; |
| |
1888 |
| |
1889 m = g_list_append(m, "Online"); |
| |
1890 m = g_list_append(m, "Chatty"); |
| |
1891 m = g_list_append(m, "Away"); |
| |
1892 m = g_list_append(m, "Extended Away"); |
| |
1893 m = g_list_append(m, "Do Not Disturb"); |
| |
1894 |
| |
1895 return m; |
| |
1896 } |
| |
1897 |
| |
1898 static void jabber_set_away(struct gaim_connection *gc, char *state, char *message) |
| |
1899 { |
| |
1900 xmlnode x, y; |
| |
1901 struct jabber_data *jd = gc->proto_data; |
| |
1902 gjconn j = jd->jc; |
| |
1903 |
| |
1904 gc->away = NULL; /* never send an auto-response */ |
| |
1905 |
| |
1906 x = xmlnode_new_tag("presence"); |
| |
1907 |
| |
1908 if (!strcmp(state, GAIM_AWAY_CUSTOM)) { |
| |
1909 /* oh goody. Gaim is telling us what to do. */ |
| |
1910 if (message) { |
| |
1911 /* Gaim wants us to be away */ |
| |
1912 y = xmlnode_insert_tag(x, "show"); |
| |
1913 xmlnode_insert_cdata(y, "away", -1); |
| |
1914 y = xmlnode_insert_tag(x, "status"); |
| |
1915 xmlnode_insert_cdata(y, message, -1); |
| |
1916 gc->away = ""; |
| |
1917 } else { |
| |
1918 /* Gaim wants us to not be away */ |
| |
1919 /* but for Jabber, we can just send presence with no other information. */ |
| |
1920 } |
| |
1921 } else { |
| |
1922 /* state is one of our own strings. it won't be NULL. */ |
| |
1923 if (!strcmp(state, "Online")) { |
| |
1924 /* once again, we don't have to put anything here */ |
| |
1925 } else if (!strcmp(state, "Chatty")) { |
| |
1926 y = xmlnode_insert_tag(x, "show"); |
| |
1927 xmlnode_insert_cdata(y, "chat", -1); |
| |
1928 } else if (!strcmp(state, "Away")) { |
| |
1929 y = xmlnode_insert_tag(x, "show"); |
| |
1930 xmlnode_insert_cdata(y, "away", -1); |
| |
1931 gc->away = ""; |
| |
1932 } else if (!strcmp(state, "Extended Away")) { |
| |
1933 y = xmlnode_insert_tag(x, "show"); |
| |
1934 xmlnode_insert_cdata(y, "xa", -1); |
| |
1935 gc->away = ""; |
| |
1936 } else if (!strcmp(state, "Do Not Disturb")) { |
| |
1937 y = xmlnode_insert_tag(x, "show"); |
| |
1938 xmlnode_insert_cdata(y, "dnd", -1); |
| |
1939 gc->away = ""; |
| |
1940 } |
| |
1941 } |
| |
1942 |
| |
1943 gjab_send(j, x); |
| |
1944 xmlnode_free(x); |
| |
1945 } |
| |
1946 |
| |
1947 static void jabber_set_idle(struct gaim_connection *gc, int idle) { |
| |
1948 struct jabber_data *jd = (struct jabber_data *)gc->proto_data; |
| |
1949 debug_printf("jabber_set_idle: setting idle %i\n", idle); |
| |
1950 jd->idle = idle ? time(NULL) - idle : idle; |
| |
1951 } |
| |
1952 |
| |
1953 static void jabber_keepalive(struct gaim_connection *gc) { |
| |
1954 struct jabber_data *jd = (struct jabber_data *)gc->proto_data; |
| |
1955 gjab_send_raw(jd->jc, " \t "); |
| |
1956 } |
| |
1957 |
| |
1958 static void jabber_print_option(GtkEntry *entry, struct aim_user *user) |
| |
1959 { |
| |
1960 int entrynum; |
| |
1961 |
| |
1962 entrynum = (int)gtk_object_get_user_data(GTK_OBJECT(entry)); |
| |
1963 |
| |
1964 if (entrynum == USEROPT_PORT) { |
| |
1965 g_snprintf(user->proto_opt[USEROPT_PORT], |
| |
1966 sizeof(user->proto_opt[USEROPT_PORT]), "%s", gtk_entry_get_text(entry)); |
| |
1967 } |
| |
1968 } |
| |
1969 |
| |
1970 static void jabber_user_opts(GtkWidget *book, struct aim_user *user) |
| |
1971 { |
| |
1972 GtkWidget *vbox; |
| |
1973 GtkWidget *hbox; |
| |
1974 GtkWidget *label; |
| |
1975 GtkWidget *entry; |
| |
1976 |
| |
1977 vbox = gtk_vbox_new(FALSE, 5); |
| |
1978 gtk_container_set_border_width(GTK_CONTAINER(vbox), 5); |
| |
1979 gtk_notebook_append_page(GTK_NOTEBOOK(book), vbox, gtk_label_new("Jabber Options")); |
| |
1980 gtk_widget_show(vbox); |
| |
1981 |
| |
1982 hbox = gtk_hbox_new(FALSE, 5); |
| |
1983 gtk_box_pack_start(GTK_BOX(vbox), hbox, FALSE, FALSE, 0); |
| |
1984 gtk_widget_show(hbox); |
| |
1985 |
| |
1986 label = gtk_label_new("Port:"); |
| |
1987 gtk_box_pack_start(GTK_BOX(hbox), label, FALSE, FALSE, 0); |
| |
1988 gtk_widget_show(label); |
| |
1989 |
| |
1990 entry = gtk_entry_new(); |
| |
1991 gtk_box_pack_end(GTK_BOX(hbox), entry, FALSE, FALSE, 0); |
| |
1992 gtk_object_set_user_data(GTK_OBJECT(entry), (void *)USEROPT_PORT); |
| |
1993 gtk_signal_connect(GTK_OBJECT(entry), "changed", GTK_SIGNAL_FUNC(jabber_print_option), user); |
| |
1994 if (isdigit(user->proto_opt[USEROPT_PORT][0])) { |
| |
1995 debug_printf("setting text %s\n", user->proto_opt[USEROPT_PORT]); |
| |
1996 gtk_entry_set_text(GTK_ENTRY(entry), user->proto_opt[USEROPT_PORT]); |
| |
1997 } else |
| |
1998 gtk_entry_set_text(GTK_ENTRY(entry), "5222"); |
| |
1999 |
| |
2000 label = gtk_label_new("To set the server, make your username be user@server.org."); |
| |
2001 gtk_label_set_line_wrap(GTK_LABEL(label), TRUE); |
| |
2002 gtk_box_pack_start(GTK_BOX(vbox), label, FALSE, FALSE, 0); |
| |
2003 gtk_widget_show(label); |
| |
2004 |
| |
2005 gtk_widget_show(entry); |
| |
2006 } |
| |
2007 |
| |
2008 static struct prpl *my_protocol = NULL; |
| |
2009 |
| |
2010 void jabber_init(struct prpl *ret) |
| |
2011 { |
| |
2012 /* the NULL's aren't required but they're nice to have */ |
| |
2013 ret->protocol = PROTO_JABBER; |
| |
2014 ret->options = OPT_PROTO_UNIQUE_CHATNAME | OPT_PROTO_CHAT_TOPIC; |
| |
2015 ret->name = jabber_name; |
| |
2016 ret->list_icon = jabber_list_icon; |
| |
2017 ret->away_states = jabber_away_states; |
| |
2018 ret->buddy_menu = jabber_buddy_menu; |
| |
2019 ret->user_opts = jabber_user_opts; |
| |
2020 ret->draw_new_user = jabber_draw_new_user; |
| |
2021 ret->do_new_user = jabber_do_new_user; |
| |
2022 ret->login = jabber_login; |
| |
2023 ret->close = jabber_close; |
| |
2024 ret->send_im = jabber_send_im; |
| |
2025 ret->set_info = NULL; |
| |
2026 ret->get_info = jabber_get_info; |
| |
2027 ret->set_away = jabber_set_away; |
| |
2028 ret->get_away_msg = NULL; |
| |
2029 ret->set_dir = NULL; |
| |
2030 ret->get_dir = NULL; |
| |
2031 ret->dir_search = NULL; |
| |
2032 ret->set_idle = jabber_set_idle; |
| |
2033 ret->change_passwd = NULL; |
| |
2034 ret->add_buddy = jabber_add_buddy; |
| |
2035 ret->add_buddies = NULL; |
| |
2036 ret->remove_buddy = jabber_remove_buddy; |
| |
2037 ret->add_permit = NULL; |
| |
2038 ret->add_deny = NULL; |
| |
2039 ret->rem_permit = NULL; |
| |
2040 ret->rem_deny = NULL; |
| |
2041 ret->set_permit_deny = NULL; |
| |
2042 ret->warn = NULL; |
| |
2043 ret->accept_chat = NULL; |
| |
2044 ret->join_chat = jabber_join_chat; |
| |
2045 ret->chat_invite = jabber_chat_invite; |
| |
2046 ret->chat_leave = jabber_chat_leave; |
| |
2047 ret->chat_whisper = jabber_chat_whisper; |
| |
2048 ret->chat_set_topic = jabber_chat_set_topic; |
| |
2049 ret->chat_send = jabber_chat_send; |
| |
2050 ret->keepalive = jabber_keepalive; |
| |
2051 ret->normalize = jabber_normalize; |
| |
2052 |
| |
2053 my_protocol = ret; |
| |
2054 } |
| |
2055 |
| |
2056 #ifndef STATIC |
| |
2057 |
| |
2058 char *gaim_plugin_init(GModule *handle) |
| |
2059 { |
| |
2060 load_protocol(jabber_init, sizeof(struct prpl)); |
| |
2061 return NULL; |
| |
2062 } |
| |
2063 |
| |
2064 void gaim_plugin_remove() |
| |
2065 { |
| |
2066 struct prpl *p = find_prpl(PROTO_JABBER); |
| |
2067 if (p == my_protocol) |
| |
2068 unload_protocol(p); |
| |
2069 } |
| |
2070 |
| |
2071 char *name() |
| |
2072 { |
| |
2073 return "Jabber"; |
| |
2074 } |
| |
2075 |
| |
2076 char *description() |
| |
2077 { |
| |
2078 return "Allows gaim to use the Jabber protocol"; |
| |
2079 } |
| |
2080 |
| |
2081 #endif |