libgaim/protocols/msn/msg.c

branch
cpw.khc.msnp14
changeset 20472
6a6d2ef151e6
parent 13912
463b4fa9f067
parent 14932
d2b3faf48b39
equal deleted inserted replaced
13912:463b4fa9f067 20472:6a6d2ef151e6
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 #define MSN_DEBUG_MSG
27
28 MsnMessage *
29 msn_message_new(MsnMsgType type)
30 {
31 MsnMessage *msg;
32
33 msg = g_new0(MsnMessage, 1);
34 msg->type = type;
35
36 #ifdef MSN_DEBUG_MSG
37 gaim_debug_info("msn", "message new (%p)(%d)\n", msg, type);
38 #endif
39
40 msg->attr_table = g_hash_table_new_full(g_str_hash, g_str_equal,
41 g_free, g_free);
42
43 msn_message_ref(msg);
44
45 return msg;
46 }
47
48 void
49 msn_message_destroy(MsnMessage *msg)
50 {
51 g_return_if_fail(msg != NULL);
52
53 if (msg->ref_count > 0)
54 {
55 msn_message_unref(msg);
56
57 return;
58 }
59
60 #ifdef MSN_DEBUG_MSG
61 gaim_debug_info("msn", "message destroy (%p)\n", msg);
62 #endif
63
64 if (msg->remote_user != NULL)
65 g_free(msg->remote_user);
66
67 if (msg->body != NULL)
68 g_free(msg->body);
69
70 if (msg->content_type != NULL)
71 g_free(msg->content_type);
72
73 if (msg->charset != NULL)
74 g_free(msg->charset);
75
76 g_hash_table_destroy(msg->attr_table);
77 g_list_free(msg->attr_list);
78
79 g_free(msg);
80 }
81
82 MsnMessage *
83 msn_message_ref(MsnMessage *msg)
84 {
85 g_return_val_if_fail(msg != NULL, NULL);
86
87 msg->ref_count++;
88
89 #ifdef MSN_DEBUG_MSG
90 gaim_debug_info("msn", "message ref (%p)[%d]\n", msg, msg->ref_count);
91 #endif
92
93 return msg;
94 }
95
96 MsnMessage *
97 msn_message_unref(MsnMessage *msg)
98 {
99 g_return_val_if_fail(msg != NULL, NULL);
100 g_return_val_if_fail(msg->ref_count > 0, NULL);
101
102 msg->ref_count--;
103
104 #ifdef MSN_DEBUG_MSG
105 gaim_debug_info("msn", "message unref (%p)[%d]\n", msg, msg->ref_count);
106 #endif
107
108 if (msg->ref_count == 0){
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; CS=86;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 const char *line_dem,const char *body_dem)
211 {
212 char *tmp_base, *tmp;
213 const char *content_type;
214 char *end;
215 char **elems, **cur, **tokens;
216
217 g_return_if_fail(payload != NULL);
218 // gaim_debug_info("MaYuan","payload:{%s}\n",payload);
219 tmp_base = tmp = g_malloc0(payload_len + 1);
220 memcpy(tmp_base, payload, payload_len);
221
222 /* Parse the attributes. */
223 end = strstr(tmp, body_dem);
224 /* TODO? some clients use \r delimiters instead of \r\n, the official client
225 * doesn't send such messages, but does handle receiving them. We'll just
226 * avoid crashing for now */
227 if (end == NULL) {
228 g_free(tmp_base);
229 g_return_if_reached();
230 }
231 *end = '\0';
232
233 elems = g_strsplit(tmp, line_dem, 0);
234
235 for (cur = elems; *cur != NULL; cur++){
236 const char *key, *value;
237
238 tokens = g_strsplit(*cur, ": ", 2);
239
240 key = tokens[0];
241 value = tokens[1];
242
243 /*if not MIME content ,then return*/
244 if (!strcmp(key, "MIME-Version")){
245 g_strfreev(tokens);
246 continue;
247 }
248
249 if (!strcmp(key, "Content-Type")){
250 char *charset, *c;
251
252 if ((c = strchr(value, ';')) != NULL){
253 if ((charset = strchr(c, '=')) != NULL) {
254 charset++;
255 msn_message_set_charset(msg, charset);
256 }
257
258 *c = '\0';
259 }
260
261 msn_message_set_content_type(msg, value);
262 }else{
263 msn_message_set_attr(msg, key, value);
264 }
265
266 g_strfreev(tokens);
267 }
268
269 g_strfreev(elems);
270
271 /* Proceed to the end of the "\r\n\r\n" */
272 tmp = end + strlen(body_dem);
273
274 /* Now we *should* be at the body. */
275 content_type = msn_message_get_content_type(msg);
276
277 if (content_type != NULL &&
278 !strcmp(content_type, "application/x-msnmsgrp2p")){
279 MsnSlpHeader header;
280 MsnSlpFooter footer;
281 int body_len;
282
283 if (payload_len - (tmp - tmp_base) < sizeof(header)) {
284 g_free(tmp_base);
285 g_return_if_reached();
286 }
287
288 msg->msnslp_message = TRUE;
289
290 /* Import the header. */
291 memcpy(&header, tmp, sizeof(header));
292 tmp += sizeof(header);
293
294 msg->msnslp_header.session_id = GUINT32_FROM_LE(header.session_id);
295 msg->msnslp_header.id = GUINT32_FROM_LE(header.id);
296 msg->msnslp_header.offset = GUINT64_FROM_LE(header.offset);
297 msg->msnslp_header.total_size = GUINT64_FROM_LE(header.total_size);
298 msg->msnslp_header.length = GUINT32_FROM_LE(header.length);
299 msg->msnslp_header.flags = GUINT32_FROM_LE(header.flags);
300 msg->msnslp_header.ack_id = GUINT32_FROM_LE(header.ack_id);
301 msg->msnslp_header.ack_sub_id = GUINT32_FROM_LE(header.ack_sub_id);
302 msg->msnslp_header.ack_size = GUINT64_FROM_LE(header.ack_size);
303
304 body_len = payload_len - (tmp - tmp_base) - sizeof(footer);
305
306 /* Import the body. */
307 if (body_len > 0) {
308 msg->body_len = body_len;
309 msg->body = g_malloc0(msg->body_len + 1);
310 memcpy(msg->body, tmp, msg->body_len);
311 tmp += body_len;
312 }
313
314 /* Import the footer. */
315 if (body_len >= 0) {
316 memcpy(&footer, tmp, sizeof(footer));
317 tmp += sizeof(footer);
318 msg->msnslp_footer.value = GUINT32_FROM_BE(footer.value);
319 }
320 }else{
321 if (payload_len - (tmp - tmp_base) > 0) {
322 msg->body_len = payload_len - (tmp - tmp_base);
323 msg->body = g_malloc0(msg->body_len + 1);
324 memcpy(msg->body, tmp, msg->body_len);
325 }
326 }
327
328 g_free(tmp_base);
329 }
330
331 MsnMessage *
332 msn_message_new_from_cmd(MsnSession *session, MsnCommand *cmd)
333 {
334 MsnMessage *msg;
335
336 g_return_val_if_fail(cmd != NULL, NULL);
337
338 msg = msn_message_new(MSN_MSG_UNKNOWN);
339
340 msg->remote_user = g_strdup(cmd->params[0]);
341 /* msg->size = atoi(cmd->params[2]); */
342 msg->cmd = cmd;
343
344 return msg;
345 }
346
347 char *
348 msn_message_gen_slp_body(MsnMessage *msg, size_t *ret_size)
349 {
350 MsnSlpHeader header;
351
352 char *tmp, *base;
353 const void *body;
354 size_t len, body_len;
355
356 g_return_val_if_fail(msg != NULL, NULL);
357
358 len = MSN_BUF_LEN;
359
360 base = tmp = g_malloc(len + 1);
361
362 body = msn_message_get_bin_data(msg, &body_len);
363
364 header.session_id = GUINT32_TO_LE(msg->msnslp_header.session_id);
365 header.id = GUINT32_TO_LE(msg->msnslp_header.id);
366 header.offset = GUINT64_TO_LE(msg->msnslp_header.offset);
367 header.total_size = GUINT64_TO_LE(msg->msnslp_header.total_size);
368 header.length = GUINT32_TO_LE(msg->msnslp_header.length);
369 header.flags = GUINT32_TO_LE(msg->msnslp_header.flags);
370 header.ack_id = GUINT32_TO_LE(msg->msnslp_header.ack_id);
371 header.ack_sub_id = GUINT32_TO_LE(msg->msnslp_header.ack_sub_id);
372 header.ack_size = GUINT64_TO_LE(msg->msnslp_header.ack_size);
373
374 memcpy(tmp, &header, 48);
375 tmp += 48;
376
377 if (body != NULL)
378 {
379 memcpy(tmp, body, body_len);
380 tmp += body_len;
381 }
382
383 if (ret_size != NULL)
384 *ret_size = tmp - base;
385
386 return base;
387 }
388
389 char *
390 msn_message_gen_payload(MsnMessage *msg, size_t *ret_size)
391 {
392 GList *l;
393 char *n, *base, *end;
394 int len;
395 size_t body_len;
396 const void *body;
397
398 g_return_val_if_fail(msg != NULL, NULL);
399
400 len = MSN_BUF_LEN;
401
402 base = n = end = g_malloc(len + 1);
403 end += len;
404
405 /* Standard header. */
406 if (msg->charset == NULL)
407 {
408 g_snprintf(n, len,
409 "MIME-Version: 1.0\r\n"
410 "Content-Type: %s\r\n",
411 msg->content_type);
412 }
413 else
414 {
415 g_snprintf(n, len,
416 "MIME-Version: 1.0\r\n"
417 "Content-Type: %s; charset=%s\r\n",
418 msg->content_type, msg->charset);
419 }
420
421 n += strlen(n);
422
423 for (l = msg->attr_list; l != NULL; l = l->next)
424 {
425 const char *key;
426 const char *value;
427
428 key = l->data;
429 value = msn_message_get_attr(msg, key);
430
431 g_snprintf(n, end - n, "%s: %s\r\n", key, value);
432 n += strlen(n);
433 }
434
435 n += g_strlcpy(n, "\r\n", end - n);
436
437 body = msn_message_get_bin_data(msg, &body_len);
438
439 if (msg->msnslp_message)
440 {
441 MsnSlpHeader header;
442 MsnSlpFooter footer;
443
444 header.session_id = GUINT32_TO_LE(msg->msnslp_header.session_id);
445 header.id = GUINT32_TO_LE(msg->msnslp_header.id);
446 header.offset = GUINT64_TO_LE(msg->msnslp_header.offset);
447 header.total_size = GUINT64_TO_LE(msg->msnslp_header.total_size);
448 header.length = GUINT32_TO_LE(msg->msnslp_header.length);
449 header.flags = GUINT32_TO_LE(msg->msnslp_header.flags);
450 header.ack_id = GUINT32_TO_LE(msg->msnslp_header.ack_id);
451 header.ack_sub_id = GUINT32_TO_LE(msg->msnslp_header.ack_sub_id);
452 header.ack_size = GUINT64_TO_LE(msg->msnslp_header.ack_size);
453
454 memcpy(n, &header, 48);
455 n += 48;
456
457 if (body != NULL)
458 {
459 memcpy(n, body, body_len);
460
461 n += body_len;
462 }
463
464 footer.value = GUINT32_TO_BE(msg->msnslp_footer.value);
465
466 memcpy(n, &footer, 4);
467 n += 4;
468 }
469 else
470 {
471 if (body != NULL){
472 memcpy(n, body, body_len);
473 n += body_len;
474 *n = '\0';
475 }
476 }
477
478 if (ret_size != NULL){
479 *ret_size = n - base;
480
481 if (*ret_size > 1664)
482 *ret_size = 1664;
483 }
484
485 return base;
486 }
487
488 void
489 msn_message_set_flag(MsnMessage *msg, char flag)
490 {
491 g_return_if_fail(msg != NULL);
492 g_return_if_fail(flag != 0);
493
494 msg->flag = flag;
495 }
496
497 char
498 msn_message_get_flag(const MsnMessage *msg)
499 {
500 g_return_val_if_fail(msg != NULL, 0);
501
502 return msg->flag;
503 }
504
505 void
506 msn_message_set_bin_data(MsnMessage *msg, const void *data, size_t len)
507 {
508 g_return_if_fail(msg != NULL);
509
510 /* There is no need to waste memory on data we cannot send anyway */
511 if (len > 1664)
512 len = 1664;
513
514 if (msg->body != NULL)
515 g_free(msg->body);
516
517 if (data != NULL && len > 0){
518 msg->body = g_malloc0(len + 1);
519 memcpy(msg->body, data, len);
520 msg->body_len = len;
521 }else{
522 msg->body = NULL;
523 msg->body_len = 0;
524 }
525 }
526
527 const void *
528 msn_message_get_bin_data(const MsnMessage *msg, size_t *len)
529 {
530 g_return_val_if_fail(msg != NULL, NULL);
531
532 if (len)
533 *len = msg->body_len;
534
535 return msg->body;
536 }
537
538 void
539 msn_message_set_content_type(MsnMessage *msg, const char *type)
540 {
541 g_return_if_fail(msg != NULL);
542
543 if (msg->content_type != NULL)
544 g_free(msg->content_type);
545
546 msg->content_type = (type != NULL) ? g_strdup(type) : NULL;
547 }
548
549 const char *
550 msn_message_get_content_type(const MsnMessage *msg)
551 {
552 g_return_val_if_fail(msg != NULL, NULL);
553
554 return msg->content_type;
555 }
556
557 void
558 msn_message_set_charset(MsnMessage *msg, const char *charset)
559 {
560 g_return_if_fail(msg != NULL);
561
562 if (msg->charset != NULL)
563 g_free(msg->charset);
564
565 msg->charset = (charset != NULL) ? g_strdup(charset) : NULL;
566 }
567
568 const char *
569 msn_message_get_charset(const MsnMessage *msg)
570 {
571 g_return_val_if_fail(msg != NULL, NULL);
572
573 return msg->charset;
574 }
575
576 void
577 msn_message_set_attr(MsnMessage *msg, const char *attr, const char *value)
578 {
579 const char *temp;
580 char *new_attr;
581
582 g_return_if_fail(msg != NULL);
583 g_return_if_fail(attr != NULL);
584
585 temp = msn_message_get_attr(msg, attr);
586
587 if (value == NULL)
588 {
589 if (temp != NULL)
590 {
591 GList *l;
592
593 for (l = msg->attr_list; l != NULL; l = l->next)
594 {
595 if (!g_ascii_strcasecmp(l->data, attr))
596 {
597 msg->attr_list = g_list_remove(msg->attr_list, l->data);
598
599 break;
600 }
601 }
602
603 g_hash_table_remove(msg->attr_table, attr);
604 }
605
606 return;
607 }
608
609 new_attr = g_strdup(attr);
610
611 g_hash_table_insert(msg->attr_table, new_attr, g_strdup(value));
612
613 if (temp == NULL)
614 msg->attr_list = g_list_append(msg->attr_list, new_attr);
615 }
616
617 const char *
618 msn_message_get_attr(const MsnMessage *msg, const char *attr)
619 {
620 g_return_val_if_fail(msg != NULL, NULL);
621 g_return_val_if_fail(attr != NULL, NULL);
622
623 return g_hash_table_lookup(msg->attr_table, attr);
624 }
625
626 GHashTable *
627 msn_message_get_hashtable_from_body(const MsnMessage *msg)
628 {
629 GHashTable *table;
630 size_t body_len;
631 const char *body;
632 char **elems, **cur, **tokens, *body_str;
633
634 g_return_val_if_fail(msg != NULL, NULL);
635
636 table = g_hash_table_new_full(g_str_hash, g_str_equal, g_free, g_free);
637
638 body = msn_message_get_bin_data(msg, &body_len);
639
640 g_return_val_if_fail(body != NULL, NULL);
641
642 body_str = g_strndup(body, body_len);
643 elems = g_strsplit(body_str, "\r\n", 0);
644 g_free(body_str);
645
646 for (cur = elems; *cur != NULL; cur++)
647 {
648 if (**cur == '\0')
649 break;
650
651 tokens = g_strsplit(*cur, ": ", 2);
652
653 if (tokens[0] != NULL && tokens[1] != NULL)
654 g_hash_table_insert(table, tokens[0], tokens[1]);
655
656 g_free(tokens);
657 }
658
659 g_strfreev(elems);
660
661 return table;
662 }
663
664 char *
665 msn_message_to_string(MsnMessage *msg)
666 {
667 size_t body_len;
668 const char *body;
669
670 g_return_val_if_fail(msg != NULL, NULL);
671 g_return_val_if_fail(msg->type == MSN_MSG_TEXT, NULL);
672
673 body = msn_message_get_bin_data(msg, &body_len);
674
675 return g_strndup(body, body_len);
676 }
677
678 void
679 msn_message_show_readable(MsnMessage *msg, const char *info,
680 gboolean text_body)
681 {
682 GString *str;
683 size_t body_len;
684 const char *body;
685 GList *l;
686
687 g_return_if_fail(msg != NULL);
688
689 str = g_string_new(NULL);
690
691 /* Standard header. */
692 if (msg->charset == NULL)
693 {
694 g_string_append_printf(str,
695 "MIME-Version: 1.0\r\n"
696 "Content-Type: %s\r\n",
697 msg->content_type);
698 }
699 else
700 {
701 g_string_append_printf(str,
702 "MIME-Version: 1.0\r\n"
703 "Content-Type: %s; charset=%s\r\n",
704 msg->content_type, msg->charset);
705 }
706
707 for (l = msg->attr_list; l; l = l->next)
708 {
709 char *key;
710 const char *value;
711
712 key = l->data;
713 value = msn_message_get_attr(msg, key);
714
715 g_string_append_printf(str, "%s: %s\r\n", key, value);
716 }
717
718 g_string_append(str, "\r\n");
719
720 body = msn_message_get_bin_data(msg, &body_len);
721
722 if (msg->msnslp_message)
723 {
724 g_string_append_printf(str, "Session ID: %u\r\n", msg->msnslp_header.session_id);
725 g_string_append_printf(str, "ID: %u\r\n", msg->msnslp_header.id);
726 g_string_append_printf(str, "Offset: %" G_GUINT64_FORMAT "\r\n", msg->msnslp_header.offset);
727 g_string_append_printf(str, "Total size: %" G_GUINT64_FORMAT "\r\n", msg->msnslp_header.total_size);
728 g_string_append_printf(str, "Length: %u\r\n", msg->msnslp_header.length);
729 g_string_append_printf(str, "Flags: 0x%x\r\n", msg->msnslp_header.flags);
730 g_string_append_printf(str, "ACK ID: %u\r\n", msg->msnslp_header.ack_id);
731 g_string_append_printf(str, "SUB ID: %u\r\n", msg->msnslp_header.ack_sub_id);
732 g_string_append_printf(str, "ACK Size: %" G_GUINT64_FORMAT "\r\n", msg->msnslp_header.ack_size);
733
734 #ifdef MSN_DEBUG_SLP_VERBOSE
735 if (body != NULL)
736 {
737 if (text_body)
738 {
739 g_string_append_len(str, body, body_len);
740 if (body[body_len - 1] == '\0')
741 {
742 str->len--;
743 g_string_append(str, " 0x00");
744 }
745 g_string_append(str, "\r\n");
746 }
747 else
748 {
749 int i;
750 for (i = 0; i < msg->body_len; i++)
751 {
752 g_string_append_printf(str, "%.2hhX ", body[i]);
753 if ((i % 16) == 15)
754 g_string_append(str, "\r\n");
755 }
756 g_string_append(str, "\r\n");
757 }
758 }
759 #endif
760
761 g_string_append_printf(str, "Footer: %u\r\n", msg->msnslp_footer.value);
762 }
763 else
764 {
765 if (body != NULL)
766 {
767 g_string_append_len(str, body, body_len);
768 g_string_append(str, "\r\n");
769 }
770 }
771
772 gaim_debug_info("msn", "Message %s:\n{%s}\n", info, str->str);
773
774 g_string_free(str, TRUE);
775 }
776

mercurial