filetransfer: contribution by Jakub Adam and Tomáš Hrabčík
[siplcs.git] / src / core / sipmsg.c
blobffec9bb341e8831b414b50e7bf3c48c185fbf74b
1 /**
2 * @file sipmsg.c
4 * gaim
6 * Copyright (C) 2008 Novell, Inc.
7 * Copyright (C) 2005 Thomas Butter <butter@uni-mannheim.de>
9 * This program is free software; you can redistribute it and/or modify
10 * it under the terms of the GNU General Public License as published by
11 * the Free Software Foundation; either version 2 of the License, or
12 * (at your option) any later version.
14 * This program is distributed in the hope that it will be useful,
15 * but WITHOUT ANY WARRANTY; without even the implied warranty of
16 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
17 * GNU General Public License for more details.
19 * You should have received a copy of the GNU General Public License
20 * along with this program; if not, write to the Free Software
21 * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
24 #include <string.h>
25 #include <glib.h>
27 #include "debug.h"
28 #include "mime.h"
30 #include "sipe.h"
31 #include "sipe-utils.h"
32 #include "sipmsg.h"
34 struct sipmsg *sipmsg_parse_msg(const gchar *msg) {
35 const char *tmp = strstr(msg, "\r\n\r\n");
36 char *line;
37 struct sipmsg *smsg;
39 if(!tmp) return NULL;
41 line = g_strndup(msg, tmp - msg);
43 smsg = sipmsg_parse_header(line);
44 smsg->body = g_strdup(tmp + 4);
46 g_free(line);
47 return smsg;
50 int sipmsg_parse_and_append_header(struct sipmsg *msg, gchar **lines) {
51 int i;
52 gchar **parts;
53 gchar *dummy;
54 gchar *dummy2;
55 gchar *tmp;
57 for(i = 0; lines[i] && strlen(lines[i]) > 2; i++) {
58 parts = g_strsplit(lines[i], ":", 2);
59 if(!parts[0] || !parts[1]) {
60 g_strfreev(parts);
61 return FALSE;
63 dummy = parts[1];
64 dummy2 = 0;
65 while(*dummy==' ' || *dummy=='\t') dummy++;
66 dummy2 = g_strdup(dummy);
67 while(lines[i+1] && (lines[i+1][0]==' ' || lines[i+1][0]=='\t')) {
68 i++;
69 dummy = lines[i];
70 while(*dummy==' ' || *dummy=='\t') dummy++;
71 tmp = g_strdup_printf("%s %s",dummy2, dummy);
72 g_free(dummy2);
73 dummy2 = tmp;
75 sipmsg_add_header_now(msg, parts[0], dummy2);
76 g_free(dummy2);
77 g_strfreev(parts);
80 return TRUE;
83 struct sipmsg *sipmsg_parse_header(const gchar *header) {
84 struct sipmsg *msg = g_new0(struct sipmsg,1);
85 gchar **lines = g_strsplit(header,"\r\n",0);
86 gchar **parts;
87 gchar *tmp;
88 gchar *contentlength;
89 if(!lines[0]) {
90 g_strfreev(lines);
91 g_free(msg);
92 return NULL;
94 parts = g_strsplit(lines[0], " ", 3);
95 if(!parts[0] || !parts[1] || !parts[2]) {
96 g_strfreev(parts);
97 g_strfreev(lines);
98 g_free(msg);
99 return NULL;
101 if(strstr(parts[0],"SIP") || strstr(parts[0],"HTTP")) { /* numeric response */
102 msg->method = g_strdup(parts[2]);
103 msg->response = strtol(parts[1],NULL,10);
104 } else { /* request */
105 msg->method = g_strdup(parts[0]);
106 msg->target = g_strdup(parts[1]);
107 msg->response = 0;
109 g_strfreev(parts);
110 if (sipmsg_parse_and_append_header(msg,lines + 1) == FALSE) {
111 g_strfreev(lines);
112 sipmsg_free(msg);
113 return NULL;
115 g_strfreev(lines);
116 contentlength = sipmsg_find_header(msg, "Content-Length");
117 if (contentlength) {
118 msg->bodylen = strtol(contentlength,NULL,10);
119 } else {
120 purple_debug_fatal("sipe", "sipmsg_parse_header(): Content-Length header not found\n");
122 if(msg->response) {
123 g_free(msg->method);
124 tmp = sipmsg_find_header(msg, "CSeq");
125 if(!tmp) {
126 /* SHOULD NOT HAPPEN */
127 msg->method = 0;
128 } else {
129 parts = g_strsplit(tmp, " ", 2);
130 msg->method = g_strdup(parts[1]);
131 g_strfreev(parts);
134 return msg;
137 void sipmsg_print(const struct sipmsg *msg) {
138 GSList *cur;
139 struct siphdrelement *elem;
140 purple_debug(PURPLE_DEBUG_MISC, "sipe", "SIP MSG\n");
141 purple_debug(PURPLE_DEBUG_MISC, "sipe", "response: %d\nmethod: %s\nbodylen: %d\n",msg->response,msg->method,msg->bodylen);
142 if(msg->target) purple_debug(PURPLE_DEBUG_MISC, "sipe", "target: %s\n",msg->target);
143 cur = msg->headers;
144 while(cur) {
145 elem = cur->data;
146 purple_debug(PURPLE_DEBUG_MISC, "sipe", "name: %s value: %s\n",elem->name, elem->value);
147 cur = g_slist_next(cur);
151 char *sipmsg_to_string(const struct sipmsg *msg) {
152 GSList *cur;
153 GString *outstr = g_string_new("");
154 struct siphdrelement *elem;
156 if(msg->response)
157 g_string_append_printf(outstr, "SIP/2.0 %d Unknown\r\n",
158 msg->response);
159 else
160 g_string_append_printf(outstr, "%s %s SIP/2.0\r\n",
161 msg->method, msg->target);
163 cur = msg->headers;
164 while(cur) {
165 elem = cur->data;
166 /*Todo: remove the LFCR in a good way*/
167 /*if(sipe_strequal(elem->name,"Proxy-Authorization"))
168 g_string_append_printf(outstr, "%s: %s", elem->name,
169 elem->value);
170 else */
171 g_string_append_printf(outstr, "%s: %s\r\n", elem->name,
172 elem->value);
173 cur = g_slist_next(cur);
176 g_string_append_printf(outstr, "\r\n%s", msg->bodylen ? msg->body : "");
178 return g_string_free(outstr, FALSE);
182 * Adds header to current message headers at specified position
184 void sipmsg_add_header_now_pos(struct sipmsg *msg, const gchar *name, const gchar *value, int pos) {
185 struct siphdrelement *element = g_new0(struct siphdrelement,1);
187 /* SANITY CHECK: the calling code must be fixed if this happens! */
188 if (!value) {
189 purple_debug(PURPLE_DEBUG_ERROR, "sipe", "sipmsg_add_header_now_pos: NULL value for %s (%d)\n",
190 name, pos);
191 value = "";
194 element->name = g_strdup(name);
195 element->value = g_strdup(value);
196 msg->headers = g_slist_insert(msg->headers, element,pos);
200 * Adds header to current message headers
202 void sipmsg_add_header_now(struct sipmsg *msg, const gchar *name, const gchar *value) {
203 struct siphdrelement *element = g_new0(struct siphdrelement,1);
205 /* SANITY CHECK: the calling code must be fixed if this happens! */
206 if (!value) {
207 purple_debug(PURPLE_DEBUG_ERROR, "sipe", "sipmsg_add_header_now: NULL value for %s\n",
208 name);
209 value = "";
212 element->name = g_strdup(name);
213 element->value = g_strdup(value);
214 msg->headers = g_slist_append(msg->headers, element);
218 * Adds header to separate storage for future merge
220 void sipmsg_add_header(struct sipmsg *msg, const gchar *name, const gchar *value) {
221 struct siphdrelement *element = g_new0(struct siphdrelement,1);
223 /* SANITY CHECK: the calling code must be fixed if this happens! */
224 if (!value) {
225 purple_debug(PURPLE_DEBUG_ERROR, "sipe", "sipmsg_add_header: NULL value for %s\n",
226 name);
227 value = "";
230 element->name = g_strdup(name);
231 element->value = g_strdup(value);
232 msg->new_headers = g_slist_append(msg->new_headers, element);
236 * Removes header if it's not in keepers array
238 void sipmsg_strip_headers(struct sipmsg *msg, const gchar *keepers[]) {
239 GSList *entry;
240 struct siphdrelement *elem;
242 entry = msg->headers;
243 while(entry) {
244 int i = 0;
245 gboolean keeper = FALSE;
247 elem = entry->data;
248 while (keepers[i]) {
249 if (!g_strcasecmp(elem->name, keepers[i])) {
250 keeper = TRUE;
251 break;
253 i++;
256 if (!keeper) {
257 GSList *to_delete = entry;
258 purple_debug_info("sipe", "sipmsg_strip_headers: removing %s\n", elem->name);
259 entry = g_slist_next(entry);
260 msg->headers = g_slist_delete_link(msg->headers, to_delete);
261 g_free(elem->name);
262 g_free(elem->value);
263 g_free(elem);
264 } else {
265 entry = g_slist_next(entry);
271 * Merges newly added headers to message
273 void sipmsg_merge_new_headers(struct sipmsg *msg) {
274 while(msg->new_headers) {
275 msg->headers = g_slist_append(msg->headers, msg->new_headers->data);
276 msg->new_headers = g_slist_remove(msg->new_headers, msg->new_headers->data);
280 void sipmsg_free(struct sipmsg *msg) {
281 struct siphdrelement *elem;
282 while(msg->headers) {
283 elem = msg->headers->data;
284 msg->headers = g_slist_remove(msg->headers,elem);
285 g_free(elem->name);
286 g_free(elem->value);
287 g_free(elem);
289 while(msg->new_headers) {
290 elem = msg->new_headers->data;
291 msg->new_headers = g_slist_remove(msg->new_headers,elem);
292 g_free(elem->name);
293 g_free(elem->value);
294 g_free(elem);
296 g_free(msg->signature);
297 g_free(msg->rand);
298 g_free(msg->num);
299 g_free(msg->method);
300 g_free(msg->target);
301 g_free(msg->body);
302 g_free(msg);
305 void sipmsg_remove_header_now(struct sipmsg *msg, const gchar *name) {
306 struct siphdrelement *elem;
307 GSList *tmp = msg->headers;
308 while(tmp) {
309 elem = tmp->data;
310 // OCS2005 can send the same header in either all caps or mixed case
311 if (g_ascii_strcasecmp(elem->name, name)==0) {
312 msg->headers = g_slist_remove(msg->headers, elem);
313 g_free(elem->name);
314 g_free(elem->value);
315 g_free(elem);
316 return;
318 tmp = g_slist_next(tmp);
320 return;
323 gchar *sipmsg_find_header(const struct sipmsg *msg, const gchar *name) {
324 return sipmsg_find_header_instance (msg, name, 0);
327 gchar *sipmsg_find_header_instance(const struct sipmsg *msg, const gchar *name, int which) {
328 GSList *tmp;
329 struct siphdrelement *elem;
330 int i = 0;
331 tmp = msg->headers;
332 while(tmp) {
333 elem = tmp->data;
334 // OCS2005 can send the same header in either all caps or mixed case
335 if (g_ascii_strcasecmp(elem->name,name)==0) {
336 if (i == which) {
337 return elem->value;
339 i++;
341 tmp = g_slist_next(tmp);
343 return NULL;
346 gchar *sipmsg_find_part_of_header(const char *hdr, const char * before, const char * after, const char * def) {
347 const char *tmp;
348 const char *tmp2;
349 gchar *res2;
350 if (!hdr) {
351 return NULL;
354 //printf("partof %s w/ %s before and %s after\n", hdr, before, after);
356 tmp = before == NULL ? hdr : strstr(hdr, before);
357 if (!tmp) {
358 //printf ("not found, returning null\n");
359 return (gchar *)def;
362 if (before != NULL) {
363 tmp += strlen(before);
364 //printf ("tmp now %s\n", tmp);
367 if (after != NULL && (tmp2 = strstr(tmp, after))) {
368 gchar * res = g_strndup(tmp, tmp2 - tmp);
369 //printf("returning %s\n", res);
370 return res;
372 res2 = g_strdup(tmp);
373 //printf("returning %s\n", res2);
374 return res2;
378 * Parse EndPoints header from INVITE request
379 * Returns a list of end points: contact URI plus optional epid.
380 * You must free the values and the list.
382 * Example headers:
383 * EndPoints: "alice alisson" <sip:alice@atlanta.local>, <sip:bob@atlanta.local>;epid=ebca82d94d, <sip:carol@atlanta.local>
384 * EndPoints: "alice, alisson" <sip:alice@atlanta.local>, <sip:bob@atlanta.local>
385 * EndPoints: "alice alisson" <sip:alice@atlanta.local>, "Super, Man" <sip:super@atlanta.local>
387 * @param header (in) EndPoints header contents
389 * @return GSList with struct sipendpoint as elements
391 GSList *sipmsg_parse_endpoints_header(const gchar *header)
393 GSList *list = NULL;
394 gchar **parts = g_strsplit(header, ",", 0);
395 gchar *part;
396 int i;
398 for (i = 0; (part = parts[i]) != NULL; i++) {
399 /* Does the part contain a URI? */
400 gchar *contact = sipmsg_find_part_of_header(part, "<", ">", NULL);
401 if (contact) {
402 struct sipendpoint *end_point = g_new(struct sipendpoint, 1);
403 end_point->contact = contact;
404 end_point->epid = sipmsg_find_part_of_header(part, "epid=", NULL, NULL);
405 list = g_slist_append(list, end_point);
408 g_strfreev(parts);
410 return(list);
414 * sipmsg_find_auth_header will return the particular WWW-Authenticate
415 * header specified by *name.
417 * Use this function when you want to look for a specific authentication
418 * method such as NTLM or Kerberos
421 gchar *sipmsg_find_auth_header(struct sipmsg *msg, const gchar *name) {
422 GSList *tmp;
423 struct siphdrelement *elem;
424 int name_len = strlen(name);
425 tmp = msg->headers;
426 while(tmp) {
427 elem = tmp->data;
428 //purple_debug(PURPLE_DEBUG_MISC, "sipmsg", "Current header: %s\r\n", elem->value);
429 if (elem && elem->name &&
430 (!g_ascii_strcasecmp(elem->name,"WWW-Authenticate")
431 || !g_ascii_strcasecmp(elem->name,"Authentication-Info")) ) {
432 if (!g_strncasecmp((gchar *)elem->value, name, name_len)) {
433 //purple_debug(PURPLE_DEBUG_MISC, "sipmsg", "elem->value: %s\r\n", elem->value);
434 return elem->value;
437 //purple_debug(PURPLE_DEBUG_MISC, "sipmsg", "moving to next header\r\n");
438 tmp = g_slist_next(tmp);
440 purple_debug(PURPLE_DEBUG_MISC, "sipmsg", "Did not found auth header %s\r\n", name);
441 return NULL;
444 gchar *sipmsg_get_x_mms_im_format(gchar *msgr) {
445 gchar *msgr2;
446 gsize msgr_dec64_len;
447 guchar *msgr_dec64;
448 gchar *msgr_utf8;
449 gchar **lines;
450 gchar **parts;
451 gchar *x_mms_im_format;
452 gchar *tmp;
454 if (!msgr) return NULL;
455 msgr2 = g_strdup(msgr);
456 while (strlen(msgr2) % 4 != 0) {
457 gchar *tmp_msgr2 = msgr2;
458 msgr2 = g_strdup_printf("%s=", msgr2);
459 g_free(tmp_msgr2);
461 msgr_dec64 = purple_base64_decode(msgr2, &msgr_dec64_len);
462 msgr_utf8 = g_convert((gchar *) msgr_dec64, msgr_dec64_len, "UTF-8", "UTF-16LE", NULL, NULL, NULL);
463 g_free(msgr_dec64);
464 g_free(msgr2);
465 lines = g_strsplit(msgr_utf8,"\r\n\r\n",0);
466 g_free(msgr_utf8);
467 //@TODO: make extraction like parsing of message headers.
468 parts = g_strsplit(lines[0],"X-MMS-IM-Format:",0);
469 x_mms_im_format = g_strdup(parts[1]);
470 g_strfreev(parts);
471 g_strfreev(lines);
472 tmp = x_mms_im_format;
473 if (x_mms_im_format) {
474 while(*x_mms_im_format==' ' || *x_mms_im_format=='\t') x_mms_im_format++;
476 x_mms_im_format = g_strdup(x_mms_im_format);
477 g_free(tmp);
478 return x_mms_im_format;
481 gchar *sipmsg_get_msgr_string(gchar *x_mms_im_format) {
482 gchar *msgr_orig;
483 gsize msgr_utf16_len;
484 gchar *msgr_utf16;
485 gchar *msgr_enc;
486 gchar *res;
487 int len;
489 if (!x_mms_im_format) return NULL;
490 msgr_orig = g_strdup_printf("X-MMS-IM-Format: %s\r\n\r\n", x_mms_im_format);
491 msgr_utf16 = g_convert(msgr_orig, -1, "UTF-16LE", "UTF-8", NULL, &msgr_utf16_len, NULL);
492 g_free(msgr_orig);
493 msgr_enc = purple_base64_encode((guchar *) msgr_utf16, msgr_utf16_len);
494 g_free(msgr_utf16);
495 len = strlen(msgr_enc);
496 while (msgr_enc[len - 1] == '=') len--;
497 res = g_strndup(msgr_enc, len);
498 g_free(msgr_enc);
499 return res;
502 gchar *sipmsg_apply_x_mms_im_format(const char *x_mms_im_format, gchar *body) {
503 char *pre, *post;
504 gchar *res;
506 if (!x_mms_im_format) {
507 return body ? g_strdup(body) : NULL;
509 msn_parse_format(x_mms_im_format, &pre, &post);
510 res = g_strdup_printf("%s%s%s", pre ? pre : "", body ? body : "", post ? post : "");
511 g_free(pre);
512 g_free(post);
513 return res;
516 /* ms-text-format: text/plain; charset=UTF-8;msgr=WAAtAE0...DIADQAKAA0ACgA;ms-body=SGk= */
517 gchar *get_html_message(const gchar *ms_text_format_in, const gchar *body_in)
519 gchar *msgr;
520 gchar *res;
521 gchar *ms_text_format = NULL;
522 gchar *body = NULL;
524 if (g_str_has_prefix(ms_text_format_in, "multipart/related") ||
525 g_str_has_prefix(ms_text_format_in, "multipart/alternative")) {
526 char *doc = g_strdup_printf("Content-Type: %s\r\n\r\n%s", ms_text_format_in, body_in);
527 PurpleMimeDocument *mime;
528 GList* parts;
530 mime = purple_mime_document_parse(doc);
531 parts = purple_mime_document_get_parts(mime);
532 while (parts) {
533 const gchar *content_type = purple_mime_part_get_field(parts->data, "Content-Type");
534 if (content_type) {
535 const gchar *content = purple_mime_part_get_data(parts->data);
536 guint length = purple_mime_part_get_length(parts->data);
538 /* if no other format has stored */
539 if (!ms_text_format && g_str_has_prefix(content_type, "text/plain")) {
540 ms_text_format = g_strdup(content_type);
541 body = g_strndup(content, length);
542 /* preferred format */
543 } else if (g_str_has_prefix(ms_text_format, "text/html")) {
544 g_free(ms_text_format);
545 g_free(body);
546 ms_text_format = g_strdup(content_type);
547 body = g_strndup(content, length);
548 break;
551 parts = parts->next;
553 g_free(doc);
554 if (mime)
555 purple_mime_document_free(mime);
556 } else {
557 ms_text_format = g_strdup(ms_text_format_in);
558 body = g_strdup(body_in);
561 if (body) {
562 res = body;
563 } else {
564 gchar *tmp = sipmsg_find_part_of_header(ms_text_format, "ms-body=", NULL, NULL);
565 if (!tmp) {
566 g_free(ms_text_format);
567 return NULL;
569 res = (gchar *) purple_base64_decode(tmp, NULL);
570 g_free(tmp);
571 if (!res) {
572 g_free(ms_text_format);
573 return NULL;
577 if (!g_str_has_prefix(ms_text_format, "text/html")) { // NOT html
578 char *tmp = res;
579 res = g_markup_escape_text(res, -1); // as this is not html
580 g_free(tmp);
583 msgr = sipmsg_find_part_of_header(ms_text_format, "msgr=", ";", NULL);
584 if (msgr) {
585 gchar *x_mms_im_format = sipmsg_get_x_mms_im_format(msgr);
586 gchar *tmp = res;
587 g_free(msgr);
588 res = sipmsg_apply_x_mms_im_format(x_mms_im_format, res);
589 g_free(tmp);
590 g_free(x_mms_im_format);
593 g_free(ms_text_format);
595 return res;
602 //------------------------------------------------------------------------------------------
603 //TEMP solution to include it here (copy from purple's msn protocol
604 //How to reuse msn's util methods from sipe?
606 // from internal.h for linux compilation
607 #ifndef _WIN32
608 #define MSG_LEN 2048
609 #define BUF_LEN MSG_LEN
610 #endif
611 void
612 msn_parse_format(const char *mime, char **pre_ret, char **post_ret)
614 char *cur;
615 GString *pre = g_string_new(NULL);
616 GString *post = g_string_new(NULL);
617 unsigned int colors[3];
619 if (pre_ret != NULL) *pre_ret = NULL;
620 if (post_ret != NULL) *post_ret = NULL;
622 cur = strstr(mime, "FN=");
624 if (cur && (*(cur = cur + 3) != ';'))
626 pre = g_string_append(pre, "<FONT FACE=\"");
628 while (*cur && *cur != ';')
630 pre = g_string_append_c(pre, *cur);
631 cur++;
634 pre = g_string_append(pre, "\">");
635 post = g_string_prepend(post, "</FONT>");
638 cur = strstr(mime, "EF=");
640 if (cur && (*(cur = cur + 3) != ';'))
642 while (*cur && *cur != ';')
644 pre = g_string_append_c(pre, '<');
645 pre = g_string_append_c(pre, *cur);
646 pre = g_string_append_c(pre, '>');
647 post = g_string_prepend_c(post, '>');
648 post = g_string_prepend_c(post, *cur);
649 post = g_string_prepend_c(post, '/');
650 post = g_string_prepend_c(post, '<');
651 cur++;
655 cur = strstr(mime, "CO=");
657 if (cur && (*(cur = cur + 3) != ';'))
659 int i;
661 i = sscanf(cur, "%02x%02x%02x;", &colors[0], &colors[1], &colors[2]);
663 if (i > 0)
665 char tag[64];
667 if (i == 1)
669 colors[1] = 0;
670 colors[2] = 0;
672 else if (i == 2)
674 unsigned int temp = colors[0];
676 colors[0] = colors[1];
677 colors[1] = temp;
678 colors[2] = 0;
680 else if (i == 3)
682 unsigned int temp = colors[2];
684 colors[2] = colors[0];
685 colors[0] = temp;
688 g_snprintf(tag, sizeof(tag),
689 "<FONT COLOR=\"#%02hhx%02hhx%02hhx\">",
690 colors[0], colors[1], colors[2]);
692 pre = g_string_append(pre, tag);
693 post = g_string_prepend(post, "</FONT>");
697 cur = strstr(mime, "RL=");
699 if (cur && (*(cur = cur + 3) != ';'))
701 if (*cur == '1')
703 /* RTL text was received */
704 pre = g_string_append(pre, "<SPAN style=\"direction:rtl;text-align:right;\">");
705 post = g_string_prepend(post, "</SPAN>");
709 cur = g_strdup(purple_url_decode(pre->str));
710 g_string_free(pre, TRUE);
712 if (pre_ret != NULL)
713 *pre_ret = cur;
714 else
715 g_free(cur);
717 cur = g_strdup(purple_url_decode(post->str));
718 g_string_free(post, TRUE);
720 if (post_ret != NULL)
721 *post_ret = cur;
722 else
723 g_free(cur);
726 static const char *
727 encode_spaces(const char *str)
729 static char buf[BUF_LEN];
730 const char *c;
731 char *d;
733 g_return_val_if_fail(str != NULL, NULL);
735 for (c = str, d = buf; *c != '\0'; c++)
737 if (*c == ' ')
739 *d++ = '%';
740 *d++ = '2';
741 *d++ = '0';
743 else
744 *d++ = *c;
746 *d = '\0';
748 return buf;
751 void
752 msn_import_html(const char *html, char **attributes, char **message)
754 int len, retcount = 0;
755 const char *c;
756 char *msg;
757 char *fontface = NULL;
758 char fonteffect[4];
759 char fontcolor[7];
760 char direction = '0';
762 gboolean has_bold = FALSE;
763 gboolean has_italic = FALSE;
764 gboolean has_underline = FALSE;
765 gboolean has_strikethrough = FALSE;
767 g_return_if_fail(html != NULL);
768 g_return_if_fail(attributes != NULL);
769 g_return_if_fail(message != NULL);
771 len = strlen(html);
772 msg = g_malloc0(len + 1);
774 memset(fontcolor, 0, sizeof(fontcolor));
775 strcat(fontcolor, "0");
776 memset(fonteffect, 0, sizeof(fonteffect));
778 for (c = html; *c != '\0';)
780 if (*c == '<')
782 if (!g_ascii_strncasecmp(c + 1, "br>", 3))
784 msg[retcount++] = '\r';
785 msg[retcount++] = '\n';
786 c += 4;
788 else if (!g_ascii_strncasecmp(c + 1, "i>", 2))
790 if (!has_italic)
792 strcat(fonteffect, "I");
793 has_italic = TRUE;
795 c += 3;
797 else if (!g_ascii_strncasecmp(c + 1, "b>", 2))
799 if (!has_bold)
801 strcat(fonteffect, "B");
802 has_bold = TRUE;
804 c += 3;
806 else if (!g_ascii_strncasecmp(c + 1, "u>", 2))
808 if (!has_underline)
810 strcat(fonteffect, "U");
811 has_underline = TRUE;
813 c += 3;
815 else if (!g_ascii_strncasecmp(c + 1, "s>", 2))
817 if (!has_strikethrough)
819 strcat(fonteffect, "S");
820 has_strikethrough = TRUE;
822 c += 3;
824 else if (!g_ascii_strncasecmp(c + 1, "a href=\"", 8))
826 c += 9;
828 if (!g_ascii_strncasecmp(c, "mailto:", 7))
829 c += 7;
831 while ((*c != '\0') && g_ascii_strncasecmp(c, "\">", 2))
832 msg[retcount++] = *c++;
834 if (*c != '\0')
835 c += 2;
837 /* ignore descriptive string */
838 while ((*c != '\0') && g_ascii_strncasecmp(c, "</a>", 4))
839 c++;
841 if (*c != '\0')
842 c += 4;
844 else if (!g_ascii_strncasecmp(c + 1, "span", 4))
846 /* Bi-directional text support using CSS properties in span tags */
847 c += 5;
849 while (*c != '\0' && *c != '>')
851 while (*c == ' ')
852 c++;
853 if (!g_ascii_strncasecmp(c, "dir=\"rtl\"", 9))
855 c += 9;
856 direction = '1';
858 else if (!g_ascii_strncasecmp(c, "style=\"", 7))
860 /* Parse inline CSS attributes */
861 int attr_len = 0;
862 c += 7;
863 while (*(c + attr_len) != '\0' && *(c + attr_len) != '"')
864 attr_len++;
865 if (*(c + attr_len) == '"')
867 char *css_attributes;
868 char *attr_dir;
869 css_attributes = g_strndup(c, attr_len);
870 attr_dir = purple_markup_get_css_property(css_attributes, "direction");
871 g_free(css_attributes);
872 if (attr_dir && (!g_ascii_strncasecmp(attr_dir, "RTL", 3)))
873 direction = '1';
874 g_free(attr_dir);
878 else
880 c++;
883 if (*c == '>')
884 c++;
886 else if (!g_ascii_strncasecmp(c + 1, "font", 4))
888 c += 5;
890 while ((*c != '\0') && !g_ascii_strncasecmp(c, " ", 1))
891 c++;
893 if (!g_ascii_strncasecmp(c, "color=\"#", 7))
895 c += 8;
897 fontcolor[0] = *(c + 4);
898 fontcolor[1] = *(c + 5);
899 fontcolor[2] = *(c + 2);
900 fontcolor[3] = *(c + 3);
901 fontcolor[4] = *c;
902 fontcolor[5] = *(c + 1);
904 c += 8;
906 else if (!g_ascii_strncasecmp(c, "face=\"", 6))
908 const char *end = NULL;
909 const char *comma = NULL;
910 unsigned int namelen = 0;
912 c += 6;
913 end = strchr(c, '\"');
914 comma = strchr(c, ',');
916 if (comma == NULL || comma > end)
917 namelen = (unsigned int)(end - c);
918 else
919 namelen = (unsigned int)(comma - c);
921 g_free(fontface);
922 fontface = g_strndup(c, namelen);
923 c = end + 2;
925 else
927 /* Drop all unrecognized/misparsed font tags */
928 while ((*c != '\0') && g_ascii_strncasecmp(c, "\">", 2))
929 c++;
931 if (*c != '\0')
932 c += 2;
935 else
937 while ((*c != '\0') && (*c != '>'))
938 c++;
939 if (*c != '\0')
940 c++;
943 else if (*c == '&')
945 if (!g_ascii_strncasecmp(c, "&lt;", 4))
947 msg[retcount++] = '<';
948 c += 4;
950 else if (!g_ascii_strncasecmp(c, "&gt;", 4))
952 msg[retcount++] = '>';
953 c += 4;
955 else if (!g_ascii_strncasecmp(c, "&nbsp;", 6))
957 msg[retcount++] = ' ';
958 c += 6;
960 else if (!g_ascii_strncasecmp(c, "&quot;", 6))
962 msg[retcount++] = '"';
963 c += 6;
965 else if (!g_ascii_strncasecmp(c, "&amp;", 5))
967 msg[retcount++] = '&';
968 c += 5;
970 else if (!g_ascii_strncasecmp(c, "&apos;", 6))
972 msg[retcount++] = '\'';
973 c += 6;
975 else
976 msg[retcount++] = *c++;
978 else
979 msg[retcount++] = *c++;
982 if (fontface == NULL)
983 fontface = g_strdup("MS Sans Serif");
985 *attributes = g_strdup_printf("FN=%s; EF=%s; CO=%s; PF=0; RL=%c",
986 encode_spaces(fontface),
987 fonteffect, fontcolor, direction);
988 *message = msg;
990 g_free(fontface);
992 // End of TEMP
995 Local Variables:
996 mode: c
997 c-file-style: "bsd"
998 indent-tabs-mode: t
999 tab-width: 8
1000 End: