comparison libpurple/protocols/msn/msg.c @ 15374:5fe8042783c1

Rename gtk/ and libgaim/ to pidgin/ and libpurple/
author Sean Egan <seanegan@gmail.com>
date Sat, 20 Jan 2007 02:32:10 +0000
parents
children 32c366eeeb99
comparison
equal deleted inserted replaced
15373:f79e0f4df793 15374:5fe8042783c1
1 /**
2 * @file msg.c Message functions
3 *
4 * gaim
5 *
6 * Gaim is the legal property of its developers, whose names are too numerous
7 * to list here. Please refer to the COPYRIGHT file distributed with this
8 * source distribution.
9 *
10 * This program is free software; you can redistribute it and/or modify
11 * it under the terms of the GNU General Public License as published by
12 * the Free Software Foundation; either version 2 of the License, or
13 * (at your option) any later version.
14 *
15 * This program is distributed in the hope that it will be useful,
16 * but WITHOUT ANY WARRANTY; without even the implied warranty of
17 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
18 * GNU General Public License for more details.
19 *
20 * You should have received a copy of the GNU General Public License
21 * along with this program; if not, write to the Free Software
22 * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
23 */
24 #include "msn.h"
25 #include "msg.h"
26
27 MsnMessage *
28 msn_message_new(MsnMsgType type)
29 {
30 MsnMessage *msg;
31
32 msg = g_new0(MsnMessage, 1);
33 msg->type = type;
34
35 #ifdef MSN_DEBUG_MSG
36 gaim_debug_info("msn", "message new (%p)(%d)\n", msg, type);
37 #endif
38
39 msg->attr_table = g_hash_table_new_full(g_str_hash, g_str_equal,
40 g_free, g_free);
41
42 msn_message_ref(msg);
43
44 return msg;
45 }
46
47 void
48 msn_message_destroy(MsnMessage *msg)
49 {
50 g_return_if_fail(msg != NULL);
51
52 if (msg->ref_count > 0)
53 {
54 msn_message_unref(msg);
55
56 return;
57 }
58
59 #ifdef MSN_DEBUG_MSG
60 gaim_debug_info("msn", "message destroy (%p)\n", msg);
61 #endif
62
63 if (msg->remote_user != NULL)
64 g_free(msg->remote_user);
65
66 if (msg->body != NULL)
67 g_free(msg->body);
68
69 if (msg->content_type != NULL)
70 g_free(msg->content_type);
71
72 if (msg->charset != NULL)
73 g_free(msg->charset);
74
75 g_hash_table_destroy(msg->attr_table);
76 g_list_free(msg->attr_list);
77
78 g_free(msg);
79 }
80
81 MsnMessage *
82 msn_message_ref(MsnMessage *msg)
83 {
84 g_return_val_if_fail(msg != NULL, NULL);
85
86 msg->ref_count++;
87
88 #ifdef MSN_DEBUG_MSG
89 gaim_debug_info("msn", "message ref (%p)[%d]\n", msg, msg->ref_count);
90 #endif
91
92 return msg;
93 }
94
95 MsnMessage *
96 msn_message_unref(MsnMessage *msg)
97 {
98 g_return_val_if_fail(msg != NULL, NULL);
99 g_return_val_if_fail(msg->ref_count > 0, NULL);
100
101 msg->ref_count--;
102
103 #ifdef MSN_DEBUG_MSG
104 gaim_debug_info("msn", "message unref (%p)[%d]\n", msg, msg->ref_count);
105 #endif
106
107 if (msg->ref_count == 0)
108 {
109 msn_message_destroy(msg);
110
111 return NULL;
112 }
113
114 return msg;
115 }
116
117 MsnMessage *
118 msn_message_new_plain(const char *message)
119 {
120 MsnMessage *msg;
121 char *message_cr;
122
123 msg = msn_message_new(MSN_MSG_TEXT);
124 msn_message_set_attr(msg, "User-Agent", PACKAGE_NAME "/" VERSION);
125 msn_message_set_content_type(msg, "text/plain");
126 msn_message_set_charset(msg, "UTF-8");
127 msn_message_set_flag(msg, 'A');
128 msn_message_set_attr(msg, "X-MMS-IM-Format",
129 "FN=MS%20Sans%20Serif; EF=; CO=0; PF=0");
130
131 message_cr = gaim_str_add_cr(message);
132 msn_message_set_bin_data(msg, message_cr, strlen(message_cr));
133 g_free(message_cr);
134
135 return msg;
136 }
137
138 MsnMessage *
139 msn_message_new_msnslp(void)
140 {
141 MsnMessage *msg;
142
143 msg = msn_message_new(MSN_MSG_SLP);
144
145 msn_message_set_attr(msg, "User-Agent", NULL);
146
147 msg->msnslp_message = TRUE;
148
149 msn_message_set_flag(msg, 'D');
150 msn_message_set_content_type(msg, "application/x-msnmsgrp2p");
151
152 return msg;
153 }
154
155 MsnMessage *
156 msn_message_new_nudge(void)
157 {
158 MsnMessage *msg;
159
160 msg = msn_message_new(MSN_MSG_NUDGE);
161 msn_message_set_content_type(msg, "text/x-msnmsgr-datacast\r\n");
162 msn_message_set_flag(msg, 'N');
163 msn_message_set_attr(msg,"ID","1\r\n");
164
165 return msg;
166 }
167
168 void
169 msn_message_parse_slp_body(MsnMessage *msg, const char *body, size_t len)
170 {
171 MsnSlpHeader header;
172 const char *tmp;
173 int body_len;
174
175 tmp = body;
176
177 if (len < sizeof(header)) {
178 g_return_if_reached();
179 }
180
181 /* Import the header. */
182 memcpy(&header, tmp, sizeof(header));
183 tmp += sizeof(header);
184
185 msg->msnslp_header.session_id = GUINT32_FROM_LE(header.session_id);
186 msg->msnslp_header.id = GUINT32_FROM_LE(header.id);
187 msg->msnslp_header.offset = GUINT64_FROM_LE(header.offset);
188 msg->msnslp_header.total_size = GUINT64_FROM_LE(header.total_size);
189 msg->msnslp_header.length = GUINT32_FROM_LE(header.length);
190 msg->msnslp_header.flags = GUINT32_FROM_LE(header.flags);
191 msg->msnslp_header.ack_id = GUINT32_FROM_LE(header.ack_id);
192 msg->msnslp_header.ack_sub_id = GUINT32_FROM_LE(header.ack_sub_id);
193 msg->msnslp_header.ack_size = GUINT64_FROM_LE(header.ack_size);
194
195 /* Import the body. */
196 body_len = len - (tmp - body);
197 /* msg->body_len = msg->msnslp_header.length; */
198
199 if (body_len > 0) {
200 msg->body_len = len - (tmp - body);
201 msg->body = g_malloc0(msg->body_len + 1);
202 memcpy(msg->body, tmp, msg->body_len);
203 tmp += body_len;
204 }
205 }
206
207 void
208 msn_message_parse_payload(MsnMessage *msg,
209 const char *payload, size_t payload_len)
210 {
211 char *tmp_base, *tmp;
212 const char *content_type;
213 char *end;
214 char **elems, **cur, **tokens;
215
216 g_return_if_fail(payload != NULL);
217
218 tmp_base = tmp = g_malloc0(payload_len + 1);
219 memcpy(tmp_base, payload, payload_len);
220
221 /* Parse the attributes. */
222 end = strstr(tmp, "\r\n\r\n");
223 /* TODO? some clients use \r delimiters instead of \r\n, the official client
224 * doesn't send such messages, but does handle receiving them. We'll just
225 * avoid crashing for now */
226 if (end == NULL) {
227 g_free(tmp_base);
228 g_return_if_reached();
229 }
230 *end = '\0';
231
232 elems = g_strsplit(tmp, "\r\n", 0);
233
234 for (cur = elems; *cur != NULL; cur++)
235 {
236 const char *key, *value;
237
238 tokens = g_strsplit(*cur, ": ", 2);
239
240 key = tokens[0];
241 value = tokens[1];
242
243 if (!strcmp(key, "MIME-Version"))
244 {
245 g_strfreev(tokens);
246 continue;
247 }
248
249 if (!strcmp(key, "Content-Type"))
250 {
251 char *charset, *c;
252
253 if ((c = strchr(value, ';')) != NULL)
254 {
255 if ((charset = strchr(c, '=')) != NULL)
256 {
257 charset++;
258 msn_message_set_charset(msg, charset);
259 }
260
261 *c = '\0';
262 }
263
264 msn_message_set_content_type(msg, value);
265 }
266 else
267 {
268 msn_message_set_attr(msg, key, value);
269 }
270
271 g_strfreev(tokens);
272 }
273
274 g_strfreev(elems);
275
276 /* Proceed to the end of the "\r\n\r\n" */
277 tmp = end + 4;
278
279 /* Now we *should* be at the body. */
280 content_type = msn_message_get_content_type(msg);
281
282 if (content_type != NULL &&
283 !strcmp(content_type, "application/x-msnmsgrp2p"))
284 {
285 MsnSlpHeader header;
286 MsnSlpFooter footer;
287 int body_len;
288
289 if (payload_len - (tmp - tmp_base) < sizeof(header)) {
290 g_free(tmp_base);
291 g_return_if_reached();
292 }
293
294 msg->msnslp_message = TRUE;
295
296 /* Import the header. */
297 memcpy(&header, tmp, sizeof(header));
298 tmp += sizeof(header);
299
300 msg->msnslp_header.session_id = GUINT32_FROM_LE(header.session_id);
301 msg->msnslp_header.id = GUINT32_FROM_LE(header.id);
302 msg->msnslp_header.offset = GUINT64_FROM_LE(header.offset);
303 msg->msnslp_header.total_size = GUINT64_FROM_LE(header.total_size);
304 msg->msnslp_header.length = GUINT32_FROM_LE(header.length);
305 msg->msnslp_header.flags = GUINT32_FROM_LE(header.flags);
306 msg->msnslp_header.ack_id = GUINT32_FROM_LE(header.ack_id);
307 msg->msnslp_header.ack_sub_id = GUINT32_FROM_LE(header.ack_sub_id);
308 msg->msnslp_header.ack_size = GUINT64_FROM_LE(header.ack_size);
309
310 body_len = payload_len - (tmp - tmp_base) - sizeof(footer);
311
312 /* Import the body. */
313 if (body_len > 0) {
314 msg->body_len = body_len;
315 msg->body = g_malloc0(msg->body_len + 1);
316 memcpy(msg->body, tmp, msg->body_len);
317 tmp += body_len;
318 }
319
320 /* Import the footer. */
321 if (body_len >= 0) {
322 memcpy(&footer, tmp, sizeof(footer));
323 tmp += sizeof(footer);
324 msg->msnslp_footer.value = GUINT32_FROM_BE(footer.value);
325 }
326 }
327 else
328 {
329 if (payload_len - (tmp - tmp_base) > 0) {
330 msg->body_len = payload_len - (tmp - tmp_base);
331 msg->body = g_malloc0(msg->body_len + 1);
332 memcpy(msg->body, tmp, msg->body_len);
333 }
334 }
335
336 g_free(tmp_base);
337 }
338
339 MsnMessage *
340 msn_message_new_from_cmd(MsnSession *session, MsnCommand *cmd)
341 {
342 MsnMessage *msg;
343
344 g_return_val_if_fail(cmd != NULL, NULL);
345
346 msg = msn_message_new(MSN_MSG_UNKNOWN);
347
348 msg->remote_user = g_strdup(cmd->params[0]);
349 /* msg->size = atoi(cmd->params[2]); */
350 msg->cmd = cmd;
351
352 return msg;
353 }
354
355 char *
356 msn_message_gen_slp_body(MsnMessage *msg, size_t *ret_size)
357 {
358 MsnSlpHeader header;
359
360 char *tmp, *base;
361 const void *body;
362 size_t len, body_len;
363
364 g_return_val_if_fail(msg != NULL, NULL);
365
366 len = MSN_BUF_LEN;
367
368 base = tmp = g_malloc(len + 1);
369
370 body = msn_message_get_bin_data(msg, &body_len);
371
372 header.session_id = GUINT32_TO_LE(msg->msnslp_header.session_id);
373 header.id = GUINT32_TO_LE(msg->msnslp_header.id);
374 header.offset = GUINT64_TO_LE(msg->msnslp_header.offset);
375 header.total_size = GUINT64_TO_LE(msg->msnslp_header.total_size);
376 header.length = GUINT32_TO_LE(msg->msnslp_header.length);
377 header.flags = GUINT32_TO_LE(msg->msnslp_header.flags);
378 header.ack_id = GUINT32_TO_LE(msg->msnslp_header.ack_id);
379 header.ack_sub_id = GUINT32_TO_LE(msg->msnslp_header.ack_sub_id);
380 header.ack_size = GUINT64_TO_LE(msg->msnslp_header.ack_size);
381
382 memcpy(tmp, &header, 48);
383 tmp += 48;
384
385 if (body != NULL)
386 {
387 memcpy(tmp, body, body_len);
388 tmp += body_len;
389 }
390
391 if (ret_size != NULL)
392 *ret_size = tmp - base;
393
394 return base;
395 }
396
397 char *
398 msn_message_gen_payload(MsnMessage *msg, size_t *ret_size)
399 {
400 GList *l;
401 char *n, *base, *end;
402 int len;
403 size_t body_len;
404 const void *body;
405
406 g_return_val_if_fail(msg != NULL, NULL);
407
408 len = MSN_BUF_LEN;
409
410 base = n = end = g_malloc(len + 1);
411 end += len;
412
413 /* Standard header. */
414 if (msg->charset == NULL)
415 {
416 g_snprintf(n, len,
417 "MIME-Version: 1.0\r\n"
418 "Content-Type: %s\r\n",
419 msg->content_type);
420 }
421 else
422 {
423 g_snprintf(n, len,
424 "MIME-Version: 1.0\r\n"
425 "Content-Type: %s; charset=%s\r\n",
426 msg->content_type, msg->charset);
427 }
428
429 n += strlen(n);
430
431 for (l = msg->attr_list; l != NULL; l = l->next)
432 {
433 const char *key;
434 const char *value;
435
436 key = l->data;
437 value = msn_message_get_attr(msg, key);
438
439 g_snprintf(n, end - n, "%s: %s\r\n", key, value);
440 n += strlen(n);
441 }
442
443 n += g_strlcpy(n, "\r\n", end - n);
444
445 body = msn_message_get_bin_data(msg, &body_len);
446
447 if (msg->msnslp_message)
448 {
449 MsnSlpHeader header;
450 MsnSlpFooter footer;
451
452 header.session_id = GUINT32_TO_LE(msg->msnslp_header.session_id);
453 header.id = GUINT32_TO_LE(msg->msnslp_header.id);
454 header.offset = GUINT64_TO_LE(msg->msnslp_header.offset);
455 header.total_size = GUINT64_TO_LE(msg->msnslp_header.total_size);
456 header.length = GUINT32_TO_LE(msg->msnslp_header.length);
457 header.flags = GUINT32_TO_LE(msg->msnslp_header.flags);
458 header.ack_id = GUINT32_TO_LE(msg->msnslp_header.ack_id);
459 header.ack_sub_id = GUINT32_TO_LE(msg->msnslp_header.ack_sub_id);
460 header.ack_size = GUINT64_TO_LE(msg->msnslp_header.ack_size);
461
462 memcpy(n, &header, 48);
463 n += 48;
464
465 if (body != NULL)
466 {
467 memcpy(n, body, body_len);
468
469 n += body_len;
470 }
471
472 footer.value = GUINT32_TO_BE(msg->msnslp_footer.value);
473
474 memcpy(n, &footer, 4);
475 n += 4;
476 }
477 else
478 {
479 if (body != NULL)
480 {
481 memcpy(n, body, body_len);
482 n += body_len;
483 }
484 }
485
486 if (ret_size != NULL)
487 {
488 *ret_size = n - base;
489
490 if (*ret_size > 1664)
491 *ret_size = 1664;
492 }
493
494 return base;
495 }
496
497 void
498 msn_message_set_flag(MsnMessage *msg, char flag)
499 {
500 g_return_if_fail(msg != NULL);
501 g_return_if_fail(flag != 0);
502
503 msg->flag = flag;
504 }
505
506 char
507 msn_message_get_flag(const MsnMessage *msg)
508 {
509 g_return_val_if_fail(msg != NULL, 0);
510
511 return msg->flag;
512 }
513
514 void
515 msn_message_set_bin_data(MsnMessage *msg, const void *data, size_t len)
516 {
517 g_return_if_fail(msg != NULL);
518
519 /* There is no need to waste memory on data we cannot send anyway */
520 if (len > 1664)
521 len = 1664;
522
523 if (msg->body != NULL)
524 g_free(msg->body);
525
526 if (data != NULL && len > 0)
527 {
528 msg->body = g_malloc0(len + 1);
529 memcpy(msg->body, data, len);
530 msg->body_len = len;
531 }
532 else
533 {
534 msg->body = NULL;
535 msg->body_len = 0;
536 }
537 }
538
539 const void *
540 msn_message_get_bin_data(const MsnMessage *msg, size_t *len)
541 {
542 g_return_val_if_fail(msg != NULL, NULL);
543
544 if (len)
545 *len = msg->body_len;
546
547 return msg->body;
548 }
549
550 void
551 msn_message_set_content_type(MsnMessage *msg, const char *type)
552 {
553 g_return_if_fail(msg != NULL);
554
555 if (msg->content_type != NULL)
556 g_free(msg->content_type);
557
558 msg->content_type = (type != NULL) ? g_strdup(type) : NULL;
559 }
560
561 const char *
562 msn_message_get_content_type(const MsnMessage *msg)
563 {
564 g_return_val_if_fail(msg != NULL, NULL);
565
566 return msg->content_type;
567 }
568
569 void
570 msn_message_set_charset(MsnMessage *msg, const char *charset)
571 {
572 g_return_if_fail(msg != NULL);
573
574 if (msg->charset != NULL)
575 g_free(msg->charset);
576
577 msg->charset = (charset != NULL) ? g_strdup(charset) : NULL;
578 }
579
580 const char *
581 msn_message_get_charset(const MsnMessage *msg)
582 {
583 g_return_val_if_fail(msg != NULL, NULL);
584
585 return msg->charset;
586 }
587
588 void
589 msn_message_set_attr(MsnMessage *msg, const char *attr, const char *value)
590 {
591 const char *temp;
592 char *new_attr;
593
594 g_return_if_fail(msg != NULL);
595 g_return_if_fail(attr != NULL);
596
597 temp = msn_message_get_attr(msg, attr);
598
599 if (value == NULL)
600 {
601 if (temp != NULL)
602 {
603 GList *l;
604
605 for (l = msg->attr_list; l != NULL; l = l->next)
606 {
607 if (!g_ascii_strcasecmp(l->data, attr))
608 {
609 msg->attr_list = g_list_remove(msg->attr_list, l->data);
610
611 break;
612 }
613 }
614
615 g_hash_table_remove(msg->attr_table, attr);
616 }
617
618 return;
619 }
620
621 new_attr = g_strdup(attr);
622
623 g_hash_table_insert(msg->attr_table, new_attr, g_strdup(value));
624
625 if (temp == NULL)
626 msg->attr_list = g_list_append(msg->attr_list, new_attr);
627 }
628
629 const char *
630 msn_message_get_attr(const MsnMessage *msg, const char *attr)
631 {
632 g_return_val_if_fail(msg != NULL, NULL);
633 g_return_val_if_fail(attr != NULL, NULL);
634
635 return g_hash_table_lookup(msg->attr_table, attr);
636 }
637
638 GHashTable *
639 msn_message_get_hashtable_from_body(const MsnMessage *msg)
640 {
641 GHashTable *table;
642 size_t body_len;
643 const char *body;
644 char **elems, **cur, **tokens, *body_str;
645
646 g_return_val_if_fail(msg != NULL, NULL);
647
648 table = g_hash_table_new_full(g_str_hash, g_str_equal, g_free, g_free);
649
650 body = msn_message_get_bin_data(msg, &body_len);
651
652 g_return_val_if_fail(body != NULL, NULL);
653
654 body_str = g_strndup(body, body_len);
655 elems = g_strsplit(body_str, "\r\n", 0);
656 g_free(body_str);
657
658 for (cur = elems; *cur != NULL; cur++)
659 {
660 if (**cur == '\0')
661 break;
662
663 tokens = g_strsplit(*cur, ": ", 2);
664
665 if (tokens[0] != NULL && tokens[1] != NULL)
666 g_hash_table_insert(table, tokens[0], tokens[1]);
667
668 g_free(tokens);
669 }
670
671 g_strfreev(elems);
672
673 return table;
674 }
675
676 char *
677 msn_message_to_string(MsnMessage *msg)
678 {
679 size_t body_len;
680 const char *body;
681
682 g_return_val_if_fail(msg != NULL, NULL);
683 g_return_val_if_fail(msg->type == MSN_MSG_TEXT, NULL);
684
685 body = msn_message_get_bin_data(msg, &body_len);
686
687 return g_strndup(body, body_len);
688 }
689
690 void
691 msn_message_show_readable(MsnMessage *msg, const char *info,
692 gboolean text_body)
693 {
694 GString *str;
695 size_t body_len;
696 const char *body;
697 GList *l;
698
699 g_return_if_fail(msg != NULL);
700
701 str = g_string_new(NULL);
702
703 /* Standard header. */
704 if (msg->charset == NULL)
705 {
706 g_string_append_printf(str,
707 "MIME-Version: 1.0\r\n"
708 "Content-Type: %s\r\n",
709 msg->content_type);
710 }
711 else
712 {
713 g_string_append_printf(str,
714 "MIME-Version: 1.0\r\n"
715 "Content-Type: %s; charset=%s\r\n",
716 msg->content_type, msg->charset);
717 }
718
719 for (l = msg->attr_list; l; l = l->next)
720 {
721 char *key;
722 const char *value;
723
724 key = l->data;
725 value = msn_message_get_attr(msg, key);
726
727 g_string_append_printf(str, "%s: %s\r\n", key, value);
728 }
729
730 g_string_append(str, "\r\n");
731
732 body = msn_message_get_bin_data(msg, &body_len);
733
734 if (msg->msnslp_message)
735 {
736 g_string_append_printf(str, "Session ID: %u\r\n", msg->msnslp_header.session_id);
737 g_string_append_printf(str, "ID: %u\r\n", msg->msnslp_header.id);
738 g_string_append_printf(str, "Offset: %" G_GUINT64_FORMAT "\r\n", msg->msnslp_header.offset);
739 g_string_append_printf(str, "Total size: %" G_GUINT64_FORMAT "\r\n", msg->msnslp_header.total_size);
740 g_string_append_printf(str, "Length: %u\r\n", msg->msnslp_header.length);
741 g_string_append_printf(str, "Flags: 0x%x\r\n", msg->msnslp_header.flags);
742 g_string_append_printf(str, "ACK ID: %u\r\n", msg->msnslp_header.ack_id);
743 g_string_append_printf(str, "SUB ID: %u\r\n", msg->msnslp_header.ack_sub_id);
744 g_string_append_printf(str, "ACK Size: %" G_GUINT64_FORMAT "\r\n", msg->msnslp_header.ack_size);
745
746 #ifdef MSN_DEBUG_SLP_VERBOSE
747 if (body != NULL)
748 {
749 if (text_body)
750 {
751 g_string_append_len(str, body, body_len);
752 if (body[body_len - 1] == '\0')
753 {
754 str->len--;
755 g_string_append(str, " 0x00");
756 }
757 g_string_append(str, "\r\n");
758 }
759 else
760 {
761 int i;
762 for (i = 0; i < msg->body_len; i++)
763 {
764 g_string_append_printf(str, "%.2hhX ", body[i]);
765 if ((i % 16) == 15)
766 g_string_append(str, "\r\n");
767 }
768 g_string_append(str, "\r\n");
769 }
770 }
771 #endif
772
773 g_string_append_printf(str, "Footer: %u\r\n", msg->msnslp_footer.value);
774 }
775 else
776 {
777 if (body != NULL)
778 {
779 g_string_append_len(str, body, body_len);
780 g_string_append(str, "\r\n");
781 }
782 }
783
784 gaim_debug_info("msn", "Message %s:\n{%s}\n", info, str->str);
785
786 g_string_free(str, TRUE);
787 }