src/protocols/jabber/message.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 "debug.h"
24 #include "notify.h"
25 #include "server.h"
26 #include "util.h"
27
28 #include "buddy.h"
29 #include "chat.h"
30 #include "message.h"
31 #include "xmlnode.h"
32
33 #define JABBER_TYPING_NOTIFY_INT 15
34
35 void jabber_message_free(JabberMessage *jm)
36 {
37 if(jm->from)
38 g_free(jm->from);
39 if(jm->to)
40 g_free(jm->to);
41 if(jm->subject)
42 g_free(jm->subject);
43 if(jm->body)
44 g_free(jm->body);
45 if(jm->xhtml)
46 g_free(jm->xhtml);
47 if(jm->password)
48 g_free(jm->password);
49 if(jm->etc)
50 g_list_free(jm->etc);
51
52 g_free(jm);
53 }
54
55 static void handle_chat(JabberMessage *jm)
56 {
57 JabberID *jid = jabber_id_new(jm->from);
58 char *from;
59
60 JabberBuddy *jb;
61 JabberBuddyResource *jbr;
62
63 if(!jid)
64 return;
65
66 jb = jabber_buddy_find(jm->js, jm->from, TRUE);
67 jbr = jabber_buddy_find_resource(jb, jid->resource);
68
69 if(jabber_find_unnormalized_conv(jm->from, jm->js->gc->account)) {
70 from = g_strdup(jm->from);
71 } else if(jid->node) {
72 if(jid->resource) {
73 GaimConversation *conv;
74
75 from = g_strdup_printf("%s@%s", jid->node, jid->domain);
76 conv = jabber_find_unnormalized_conv(from, jm->js->gc->account);
77 if(conv) {
78 gaim_conversation_set_name(conv, jm->from);
79 }
80 g_free(from);
81 }
82 from = g_strdup(jm->from);
83 } else {
84 from = g_strdup(jid->domain);
85 }
86
87 if(!jm->xhtml && !jm->body) {
88 if(jm->events & JABBER_MESSAGE_EVENT_COMPOSING)
89 serv_got_typing(jm->js->gc, from, 0, GAIM_TYPING);
90 else
91 serv_got_typing_stopped(jm->js->gc, from);
92 } else {
93 if(jbr) {
94 if(jm->events & JABBER_MESSAGE_EVENT_COMPOSING)
95 jbr->capabilities |= JABBER_CAP_COMPOSING;
96 if(jbr->thread_id)
97 g_free(jbr->thread_id);
98 jbr->thread_id = g_strdup(jbr->thread_id);
99 }
100 serv_got_im(jm->js->gc, from, jm->xhtml ? jm->xhtml : jm->body, 0,
101 jm->sent);
102 }
103
104 g_free(from);
105 jabber_id_free(jid);
106 }
107
108 static void handle_headline(JabberMessage *jm)
109 {
110 char *title;
111 GString *body = g_string_new("");
112 GList *etc;
113
114 title = g_strdup_printf(_("Message from %s"), jm->from);
115
116 if(jm->xhtml)
117 g_string_append(body, jm->xhtml);
118 else if(jm->body)
119 g_string_append(body, jm->body);
120
121 for(etc = jm->etc; etc; etc = etc->next) {
122 xmlnode *x = etc->data;
123 const char *xmlns = xmlnode_get_attrib(x, "xmlns");
124 if(xmlns && !strcmp(xmlns, "jabber:x:oob")) {
125 xmlnode *url, *desc;
126 char *urltxt, *desctxt;
127
128 url = xmlnode_get_child(x, "url");
129 desc = xmlnode_get_child(x, "desc");
130
131 if(!url || !desc)
132 continue;
133
134 urltxt = xmlnode_get_data(url);
135 desctxt = xmlnode_get_data(desc);
136
137 /* I'm all about ugly hacks */
138 if(body->len && !strcmp(body->str, jm->body))
139 g_string_printf(body, "<a href='%s'>%s</a>",
140 urltxt, desctxt);
141 else
142 g_string_append_printf(body, "<br/><a href='%s'>%s</a>",
143 urltxt, desctxt);
144
145 g_free(urltxt);
146 g_free(desctxt);
147 }
148 }
149
150 gaim_notify_formatted(jm->js->gc, title, jm->subject ? jm->subject : title,
151 NULL, body->str, NULL, NULL);
152
153 g_free(title);
154 g_string_free(body, TRUE);
155 }
156
157 static void handle_groupchat(JabberMessage *jm)
158 {
159 JabberID *jid = jabber_id_new(jm->from);
160 JabberChat *chat;
161
162 if(!jid)
163 return;
164
165 chat = jabber_chat_find(jm->js, jid->node, jid->domain);
166
167 if(!chat)
168 return;
169
170 if(jm->subject) {
171 gaim_conv_chat_set_topic(GAIM_CONV_CHAT(chat->conv), jid->resource,
172 jm->subject);
173 if(!jm->xhtml && !jm->body) {
174 char *msg, *tmp, *tmp2;
175 tmp = g_markup_escape_text(jm->subject, -1);
176 tmp2 = gaim_markup_linkify(tmp);
177 if(jid->resource)
178 msg = g_strdup_printf(_("%s has set the topic to: %s"), jid->resource, tmp2);
179 else
180 msg = g_strdup_printf(_("The topic is: %s"), tmp2);
181 gaim_conv_chat_write(GAIM_CONV_CHAT(chat->conv), "", msg, GAIM_MESSAGE_SYSTEM, jm->sent);
182 g_free(tmp);
183 g_free(tmp2);
184 g_free(msg);
185 }
186 }
187
188 if(jm->xhtml || jm->body) {
189 if(jid->resource)
190 serv_got_chat_in(jm->js->gc, chat->id, jid->resource,
191 jm->delayed ? GAIM_MESSAGE_DELAYED : 0,
192 jm->xhtml ? jm->xhtml : jm->body, jm->sent);
193 else if(chat->muc)
194 gaim_conv_chat_write(GAIM_CONV_CHAT(chat->conv), "",
195 jm->xhtml ? jm->xhtml : jm->body,
196 GAIM_MESSAGE_SYSTEM, jm->sent);
197 }
198
199 jabber_id_free(jid);
200 }
201
202 static void handle_groupchat_invite(JabberMessage *jm)
203 {
204 GHashTable *components;
205 JabberID *jid = jabber_id_new(jm->to);
206
207 if(!jid)
208 return;
209
210 components = g_hash_table_new_full(g_str_hash, g_str_equal, g_free, g_free);
211
212 g_hash_table_replace(components, g_strdup("room"), g_strdup(jid->node));
213 g_hash_table_replace(components, g_strdup("server"), g_strdup(jid->domain));
214 g_hash_table_replace(components, g_strdup("handle"),
215 g_strdup(jm->js->user->node));
216 g_hash_table_replace(components, g_strdup("password"),
217 g_strdup(jm->password));
218
219 jabber_id_free(jid);
220 serv_got_chat_invite(jm->js->gc, jm->to, jm->from, jm->body, components);
221 }
222
223 static void handle_error(JabberMessage *jm)
224 {
225 char *buf;
226
227 if(!jm->body)
228 return;
229
230 buf = g_strdup_printf(_("Message delivery to %s failed: %s"),
231 jm->from, jm->error);
232
233 gaim_notify_formatted(jm->js->gc, _("Jabber Message Error"), _("Jabber Message Error"), buf,
234 jm->xhtml ? jm->xhtml : jm->body, NULL, NULL);
235
236 g_free(buf);
237 }
238
239 void jabber_message_parse(JabberStream *js, xmlnode *packet)
240 {
241 JabberMessage *jm;
242 const char *type;
243 xmlnode *child;
244
245 if(strcmp(packet->name, "message"))
246 return;
247
248 jm = g_new0(JabberMessage, 1);
249 jm->js = js;
250 jm->sent = time(NULL);
251 jm->delayed = FALSE;
252
253 type = xmlnode_get_attrib(packet, "type");
254
255 if(type) {
256 if(!strcmp(type, "normal"))
257 jm->type = JABBER_MESSAGE_NORMAL;
258 else if(!strcmp(type, "chat"))
259 jm->type = JABBER_MESSAGE_CHAT;
260 else if(!strcmp(type, "groupchat"))
261 jm->type = JABBER_MESSAGE_GROUPCHAT;
262 else if(!strcmp(type, "headline"))
263 jm->type = JABBER_MESSAGE_HEADLINE;
264 else if(!strcmp(type, "error"))
265 jm->type = JABBER_MESSAGE_ERROR;
266 else
267 jm->type = JABBER_MESSAGE_OTHER;
268 } else {
269 jm->type = JABBER_MESSAGE_NORMAL;
270 }
271
272 jm->from = g_strdup(xmlnode_get_attrib(packet, "from"));
273 jm->to = g_strdup(xmlnode_get_attrib(packet, "to"));
274
275 for(child = packet->child; child; child = child->next) {
276 if(child->type != XMLNODE_TYPE_TAG)
277 continue;
278
279 if(!strcmp(child->name, "subject")) {
280 if(!jm->subject)
281 jm->subject = xmlnode_get_data(child);
282 } else if(!strcmp(child->name, "thread")) {
283 if(!jm->thread_id)
284 jm->thread_id = xmlnode_get_data(child);
285 } else if(!strcmp(child->name, "body")) {
286 if(!jm->body)
287 jm->body = xmlnode_to_str(child, NULL);
288 } else if(!strcmp(child->name, "html")) {
289 if(!jm->xhtml && xmlnode_get_child(child, "body"))
290 jm->xhtml = xmlnode_to_str(child, NULL);
291 } else if(!strcmp(child->name, "error")) {
292 const char *code = xmlnode_get_attrib(child, "code");
293 char *code_txt = NULL;
294 char *text = xmlnode_get_data(child);
295
296 if(code)
297 code_txt = g_strdup_printf(_(" (Code %s)"), code);
298
299 if(!jm->error)
300 jm->error = g_strdup_printf("%s%s", text ? text : "",
301 code_txt ? code_txt : "");
302
303 g_free(code_txt);
304 g_free(text);
305 } else if(!strcmp(child->name, "x")) {
306 const char *xmlns = xmlnode_get_attrib(child, "xmlns");
307 if(xmlns && !strcmp(xmlns, "jabber:x:event")) {
308 if(xmlnode_get_child(child, "composing"))
309 jm->events |= JABBER_MESSAGE_EVENT_COMPOSING;
310 } else if(xmlns && !strcmp(xmlns, "jabber:x:delay")) {
311 const char *timestamp = xmlnode_get_attrib(child, "stamp");
312 jm->delayed = TRUE;
313 if(timestamp)
314 jm->sent = gaim_str_to_time(timestamp, TRUE);
315 } else if(xmlns && !strcmp(xmlns, "jabber:x:conference") &&
316 jm->type != JABBER_MESSAGE_GROUPCHAT_INVITE &&
317 jm->type != JABBER_MESSAGE_ERROR) {
318 const char *jid = xmlnode_get_attrib(child, "jid");
319 if(jid) {
320 jm->type = JABBER_MESSAGE_GROUPCHAT_INVITE;
321 g_free(jm->to);
322 jm->to = g_strdup(jid);
323 }
324 } else if(xmlns && !strcmp(xmlns,
325 "http://jabber.org/protocol/muc#user") &&
326 jm->type != JABBER_MESSAGE_ERROR) {
327 xmlnode *invite = xmlnode_get_child(child, "invite");
328 if(invite) {
329 xmlnode *reason, *password;
330 const char *jid = xmlnode_get_attrib(invite, "from");
331 g_free(jm->to);
332 jm->to = jm->from;
333 jm->from = g_strdup(jid);
334 if((reason = xmlnode_get_child(invite, "reason"))) {
335 g_free(jm->body);
336 jm->body = xmlnode_get_data(reason);
337 }
338 if((password = xmlnode_get_child(child, "password")))
339 jm->password = xmlnode_get_data(password);
340
341 jm->type = JABBER_MESSAGE_GROUPCHAT_INVITE;
342 }
343 } else {
344 jm->etc = g_list_append(jm->etc, child);
345 }
346 }
347 }
348
349 switch(jm->type) {
350 case JABBER_MESSAGE_NORMAL:
351 case JABBER_MESSAGE_CHAT:
352 handle_chat(jm);
353 break;
354 case JABBER_MESSAGE_HEADLINE:
355 handle_headline(jm);
356 break;
357 case JABBER_MESSAGE_GROUPCHAT:
358 handle_groupchat(jm);
359 break;
360 case JABBER_MESSAGE_GROUPCHAT_INVITE:
361 handle_groupchat_invite(jm);
362 break;
363 case JABBER_MESSAGE_ERROR:
364 handle_error(jm);
365 break;
366 case JABBER_MESSAGE_OTHER:
367 gaim_debug(GAIM_DEBUG_INFO, "jabber",
368 "Received message of unknown type: %s\n", type);
369 break;
370 }
371 jabber_message_free(jm);
372 }
373
374 void jabber_message_send(JabberMessage *jm)
375 {
376 xmlnode *message, *child;
377 const char *type = NULL;
378
379 message = xmlnode_new("message");
380
381 switch(jm->type) {
382 case JABBER_MESSAGE_NORMAL:
383 type = "normal";
384 break;
385 case JABBER_MESSAGE_CHAT:
386 case JABBER_MESSAGE_GROUPCHAT_INVITE:
387 type = "chat";
388 break;
389 case JABBER_MESSAGE_HEADLINE:
390 type = "headline";
391 break;
392 case JABBER_MESSAGE_GROUPCHAT:
393 type = "groupchat";
394 break;
395 case JABBER_MESSAGE_ERROR:
396 type = "error";
397 break;
398 case JABBER_MESSAGE_OTHER:
399 type = NULL;
400 break;
401 }
402
403 if(type)
404 xmlnode_set_attrib(message, "type", type);
405
406 xmlnode_set_attrib(message, "to", jm->to);
407
408 if(jm->thread_id) {
409 child = xmlnode_new_child(message, "thread");
410 xmlnode_insert_data(child, jm->thread_id, -1);
411 }
412
413 if(jm->events || (!jm->body && !jm->xhtml && !jm->subject)) {
414 child = xmlnode_new_child(message, "x");
415 xmlnode_set_attrib(child, "xmlns", "jabber:x:event");
416 if(jm->events & JABBER_MESSAGE_EVENT_COMPOSING)
417 xmlnode_new_child(child, "composing");
418 }
419
420 if(jm->subject) {
421 child = xmlnode_new_child(message, "subject");
422 xmlnode_insert_data(child, jm->subject, -1);
423 }
424
425 if(jm->body) {
426 child = xmlnode_new_child(message, "body");
427 xmlnode_insert_data(child, jm->body, -1);
428 }
429
430 if(jm->xhtml) {
431 child = xmlnode_from_str(jm->xhtml, -1);
432 if(child) {
433 xmlnode_insert_child(message, child);
434 } else {
435 gaim_debug(GAIM_DEBUG_ERROR, "jabber",
436 "XHTML translation/validation failed, returning: %s\n",
437 jm->xhtml);
438 }
439 }
440
441 jabber_send(jm->js, message);
442
443 xmlnode_free(message);
444 }
445
446 int jabber_message_send_im(GaimConnection *gc, const char *who, const char *msg,
447 GaimMessageFlags flags)
448 {
449 JabberMessage *jm;
450 JabberBuddy *jb;
451 JabberBuddyResource *jbr;
452 char *buf;
453 char *xhtml;
454 char *resource;
455
456 if(!who || !msg)
457 return 0;
458
459 resource = jabber_get_resource(who);
460
461 jb = jabber_buddy_find(gc->proto_data, who, TRUE);
462 jbr = jabber_buddy_find_resource(jb, resource);
463
464 g_free(resource);
465
466 jm = g_new0(JabberMessage, 1);
467 jm->js = gc->proto_data;
468 jm->type = JABBER_MESSAGE_CHAT;
469 jm->events = JABBER_MESSAGE_EVENT_COMPOSING;
470 jm->to = g_strdup(who);
471 if(jbr && jbr->thread_id)
472 jm->thread_id = jbr->thread_id;
473
474 buf = g_strdup_printf("<html xmlns='http://jabber.org/protocol/xhtml-im'><body xmlns='http://www.w3.org/1999/xhtml'>%s</body></html>", msg);
475
476 gaim_markup_html_to_xhtml(buf, &xhtml, &jm->body);
477 g_free(buf);
478
479 if(!jbr || jbr->capabilities & JABBER_CAP_XHTML)
480 jm->xhtml = xhtml;
481 else
482 g_free(xhtml);
483
484 jabber_message_send(jm);
485 jabber_message_free(jm);
486 return 1;
487 }
488
489 int jabber_message_send_chat(GaimConnection *gc, int id, const char *msg, GaimMessageFlags flags)
490 {
491 JabberChat *chat;
492 JabberMessage *jm;
493 JabberStream *js;
494 char *buf;
495
496 if(!msg || !gc)
497 return 0;
498
499 js = gc->proto_data;
500 chat = jabber_chat_find_by_id(js, id);
501
502 if(!chat)
503 return 0;
504
505 jm = g_new0(JabberMessage, 1);
506 jm->js = gc->proto_data;
507 jm->type = JABBER_MESSAGE_GROUPCHAT;
508 jm->to = g_strdup_printf("%s@%s", chat->room, chat->server);
509
510 buf = g_strdup_printf("<html xmlns='http://jabber.org/protocol/xhtml-im'><body xmlns='http://www.w3.org/1999/xhtml'>%s</body></html>", msg);
511 gaim_markup_html_to_xhtml(buf, &jm->xhtml, &jm->body);
512 g_free(buf);
513
514 if(!chat->xhtml) {
515 g_free(jm->xhtml);
516 jm->xhtml = NULL;
517 }
518
519 jabber_message_send(jm);
520 jabber_message_free(jm);
521
522 return 1;
523 }
524
525 int jabber_send_typing(GaimConnection *gc, const char *who, int typing)
526 {
527 JabberMessage *jm;
528 JabberBuddy *jb;
529 JabberBuddyResource *jbr;
530 char *resource = jabber_get_resource(who);
531
532 jb = jabber_buddy_find(gc->proto_data, who, TRUE);
533 jbr = jabber_buddy_find_resource(jb, resource);
534
535 g_free(resource);
536
537 if(!jbr || !(jbr->capabilities & JABBER_CAP_COMPOSING))
538 return 0;
539
540 jm = g_new0(JabberMessage, 1);
541 jm->js = gc->proto_data;
542 jm->type = JABBER_MESSAGE_CHAT;
543 jm->to = g_strdup(who);
544
545 if(typing == GAIM_TYPING)
546 jm->events = JABBER_MESSAGE_EVENT_COMPOSING;
547
548 jabber_message_send(jm);
549 jabber_message_free(jm);
550
551 return JABBER_TYPING_NOTIFY_INT;
552 }
553

mercurial