core cleanup: fix thinko in previous commit
[siplcs.git] / src / core / sipmsg.c
blobc510855a5ef39f55d4b0a22816dfd6236f2386d1
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 <stdlib.h>
25 #include <string.h>
26 #include <time.h>
28 #include <glib.h>
30 #include "debug.h"
31 #include "mime.h"
32 #include "util.h"
34 #include "sipmsg.h"
35 #include "sipe-utils.h"
37 struct sipmsg *sipmsg_parse_msg(const gchar *msg) {
38 const char *tmp = strstr(msg, "\r\n\r\n");
39 char *line;
40 struct sipmsg *smsg;
42 if(!tmp) return NULL;
44 line = g_strndup(msg, tmp - msg);
46 smsg = sipmsg_parse_header(line);
47 smsg->body = g_strdup(tmp + 4);
49 g_free(line);
50 return smsg;
53 struct sipmsg *sipmsg_parse_header(const gchar *header) {
54 struct sipmsg *msg = g_new0(struct sipmsg,1);
55 gchar **lines = g_strsplit(header,"\r\n",0);
56 gchar **parts;
57 const gchar *contentlength;
58 if(!lines[0]) {
59 g_strfreev(lines);
60 g_free(msg);
61 return NULL;
63 parts = g_strsplit(lines[0], " ", 3);
64 if(!parts[0] || !parts[1] || !parts[2]) {
65 g_strfreev(parts);
66 g_strfreev(lines);
67 g_free(msg);
68 return NULL;
70 if(strstr(parts[0],"SIP") || strstr(parts[0],"HTTP")) { /* numeric response */
71 msg->method = g_strdup(parts[2]);
72 msg->response = strtol(parts[1],NULL,10);
73 } else { /* request */
74 msg->method = g_strdup(parts[0]);
75 msg->target = g_strdup(parts[1]);
76 msg->response = 0;
78 g_strfreev(parts);
79 if (sipe_utils_parse_lines(&msg->headers,lines + 1) == FALSE) {
80 g_strfreev(lines);
81 sipmsg_free(msg);
82 return NULL;
84 g_strfreev(lines);
85 contentlength = sipmsg_find_header(msg, "Content-Length");
86 if (contentlength) {
87 msg->bodylen = strtol(contentlength,NULL,10);
88 } else {
89 purple_debug_fatal("sipe", "sipmsg_parse_header(): Content-Length header not found\n");
91 if(msg->response) {
92 const gchar *tmp;
93 g_free(msg->method);
94 tmp = sipmsg_find_header(msg, "CSeq");
95 if(!tmp) {
96 /* SHOULD NOT HAPPEN */
97 msg->method = 0;
98 } else {
99 parts = g_strsplit(tmp, " ", 2);
100 msg->method = g_strdup(parts[1]);
101 g_strfreev(parts);
104 return msg;
107 void sipmsg_print(const struct sipmsg *msg) {
108 GSList *cur;
109 struct sipnameval *elem;
110 purple_debug(PURPLE_DEBUG_MISC, "sipe", "SIP MSG\n");
111 purple_debug(PURPLE_DEBUG_MISC, "sipe", "response: %d\nmethod: %s\nbodylen: %d\n",msg->response,msg->method,msg->bodylen);
112 if(msg->target) purple_debug(PURPLE_DEBUG_MISC, "sipe", "target: %s\n",msg->target);
113 cur = msg->headers;
114 while(cur) {
115 elem = cur->data;
116 purple_debug(PURPLE_DEBUG_MISC, "sipe", "name: %s value: %s\n",elem->name, elem->value);
117 cur = g_slist_next(cur);
121 char *sipmsg_to_string(const struct sipmsg *msg) {
122 GSList *cur;
123 GString *outstr = g_string_new("");
124 struct sipnameval *elem;
126 if(msg->response)
127 g_string_append_printf(outstr, "SIP/2.0 %d Unknown\r\n",
128 msg->response);
129 else
130 g_string_append_printf(outstr, "%s %s SIP/2.0\r\n",
131 msg->method, msg->target);
133 cur = msg->headers;
134 while(cur) {
135 elem = cur->data;
136 /*Todo: remove the LFCR in a good way*/
137 /*if(sipe_strequal(elem->name,"Proxy-Authorization"))
138 g_string_append_printf(outstr, "%s: %s", elem->name,
139 elem->value);
140 else */
141 g_string_append_printf(outstr, "%s: %s\r\n", elem->name,
142 elem->value);
143 cur = g_slist_next(cur);
146 g_string_append_printf(outstr, "\r\n%s", msg->bodylen ? msg->body : "");
148 return g_string_free(outstr, FALSE);
152 * Adds header to current message headers at specified position
154 void sipmsg_add_header_now_pos(struct sipmsg *msg, const gchar *name, const gchar *value, int pos) {
155 struct sipnameval *element = g_new0(struct sipnameval,1);
157 /* SANITY CHECK: the calling code must be fixed if this happens! */
158 if (!value) {
159 purple_debug(PURPLE_DEBUG_ERROR, "sipe", "sipmsg_add_header_now_pos: NULL value for %s (%d)\n",
160 name, pos);
161 value = "";
164 element->name = g_strdup(name);
165 element->value = g_strdup(value);
166 msg->headers = g_slist_insert(msg->headers, element,pos);
170 * Adds header to current message headers
172 void sipmsg_add_header_now(struct sipmsg *msg, const gchar *name, const gchar *value) {
173 struct sipnameval *element = g_new0(struct sipnameval,1);
175 /* SANITY CHECK: the calling code must be fixed if this happens! */
176 if (!value) {
177 purple_debug(PURPLE_DEBUG_ERROR, "sipe", "sipmsg_add_header_now: NULL value for %s\n",
178 name);
179 value = "";
182 element->name = g_strdup(name);
183 element->value = g_strdup(value);
184 msg->headers = g_slist_append(msg->headers, element);
188 * Adds header to separate storage for future merge
190 void sipmsg_add_header(struct sipmsg *msg, const gchar *name, const gchar *value) {
191 struct sipnameval *element = g_new0(struct sipnameval,1);
193 /* SANITY CHECK: the calling code must be fixed if this happens! */
194 if (!value) {
195 purple_debug(PURPLE_DEBUG_ERROR, "sipe", "sipmsg_add_header: NULL value for %s\n",
196 name);
197 value = "";
200 element->name = g_strdup(name);
201 element->value = g_strdup(value);
202 msg->new_headers = g_slist_append(msg->new_headers, element);
206 * Removes header if it's not in keepers array
208 void sipmsg_strip_headers(struct sipmsg *msg, const gchar *keepers[]) {
209 GSList *entry;
210 struct sipnameval *elem;
212 entry = msg->headers;
213 while(entry) {
214 int i = 0;
215 gboolean keeper = FALSE;
217 elem = entry->data;
218 while (keepers[i]) {
219 if (!g_strcasecmp(elem->name, keepers[i])) {
220 keeper = TRUE;
221 break;
223 i++;
226 if (!keeper) {
227 GSList *to_delete = entry;
228 purple_debug_info("sipe", "sipmsg_strip_headers: removing %s\n", elem->name);
229 entry = g_slist_next(entry);
230 msg->headers = g_slist_delete_link(msg->headers, to_delete);
231 g_free(elem->name);
232 g_free(elem->value);
233 g_free(elem);
234 } else {
235 entry = g_slist_next(entry);
241 * Merges newly added headers to message
243 void sipmsg_merge_new_headers(struct sipmsg *msg) {
244 while(msg->new_headers) {
245 msg->headers = g_slist_append(msg->headers, msg->new_headers->data);
246 msg->new_headers = g_slist_remove(msg->new_headers, msg->new_headers->data);
250 void sipmsg_free(struct sipmsg *msg) {
251 sipe_utils_nameval_free(msg->headers);
252 sipe_utils_nameval_free(msg->new_headers);
253 g_free(msg->signature);
254 g_free(msg->rand);
255 g_free(msg->num);
256 g_free(msg->method);
257 g_free(msg->target);
258 g_free(msg->body);
259 g_free(msg);
262 void sipmsg_remove_header_now(struct sipmsg *msg, const gchar *name) {
263 struct sipnameval *elem;
264 GSList *tmp = msg->headers;
265 while(tmp) {
266 elem = tmp->data;
267 // OCS2005 can send the same header in either all caps or mixed case
268 if (sipe_strcase_equal(elem->name, name)) {
269 msg->headers = g_slist_remove(msg->headers, elem);
270 g_free(elem->name);
271 g_free(elem->value);
272 g_free(elem);
273 return;
275 tmp = g_slist_next(tmp);
277 return;
280 const gchar *sipmsg_find_header(const struct sipmsg *msg, const gchar *name) {
281 return sipe_utils_nameval_find_instance (msg->headers, name, 0);
284 const gchar *sipmsg_find_header_instance(const struct sipmsg *msg, const gchar *name, int which) {
285 return sipe_utils_nameval_find_instance(msg->headers, name, which);
288 gchar *sipmsg_find_part_of_header(const char *hdr, const char * before, const char * after, const char * def) {
289 const char *tmp;
290 const char *tmp2;
291 gchar *res2;
292 if (!hdr) {
293 return NULL;
296 //printf("partof %s w/ %s before and %s after\n", hdr, before, after);
298 tmp = before == NULL ? hdr : strstr(hdr, before);
299 if (!tmp) {
300 //printf ("not found, returning null\n");
301 return (gchar *)def;
304 if (before != NULL) {
305 tmp += strlen(before);
306 //printf ("tmp now %s\n", tmp);
309 if (after != NULL && (tmp2 = strstr(tmp, after))) {
310 gchar * res = g_strndup(tmp, tmp2 - tmp);
311 //printf("returning %s\n", res);
312 return res;
314 res2 = g_strdup(tmp);
315 //printf("returning %s\n", res2);
316 return res2;
320 * Parse EndPoints header from INVITE request
321 * Returns a list of end points: contact URI plus optional epid.
322 * You must free the values and the list.
324 * Example headers:
325 * EndPoints: "alice alisson" <sip:alice@atlanta.local>, <sip:bob@atlanta.local>;epid=ebca82d94d, <sip:carol@atlanta.local>
326 * EndPoints: "alice, alisson" <sip:alice@atlanta.local>, <sip:bob@atlanta.local>
327 * EndPoints: "alice alisson" <sip:alice@atlanta.local>, "Super, Man" <sip:super@atlanta.local>
329 * @param header (in) EndPoints header contents
331 * @return GSList with struct sipendpoint as elements
333 GSList *sipmsg_parse_endpoints_header(const gchar *header)
335 GSList *list = NULL;
336 gchar **parts = g_strsplit(header, ",", 0);
337 gchar *part;
338 int i;
340 for (i = 0; (part = parts[i]) != NULL; i++) {
341 /* Does the part contain a URI? */
342 gchar *contact = sipmsg_find_part_of_header(part, "<", ">", NULL);
343 if (contact) {
344 struct sipendpoint *end_point = g_new(struct sipendpoint, 1);
345 end_point->contact = contact;
346 end_point->epid = sipmsg_find_part_of_header(part, "epid=", NULL, NULL);
347 list = g_slist_append(list, end_point);
350 g_strfreev(parts);
352 return(list);
356 * sipmsg_find_auth_header will return the particular WWW-Authenticate
357 * header specified by *name.
359 * Use this function when you want to look for a specific authentication
360 * method such as NTLM or Kerberos
363 gchar *sipmsg_find_auth_header(struct sipmsg *msg, const gchar *name) {
364 GSList *tmp;
365 struct sipnameval *elem;
366 int name_len = strlen(name);
367 tmp = msg->headers;
368 while(tmp) {
369 elem = tmp->data;
370 //purple_debug(PURPLE_DEBUG_MISC, "sipmsg", "Current header: %s\r\n", elem->value);
371 if (elem && elem->name &&
372 (sipe_strcase_equal(elem->name,"WWW-Authenticate") ||
373 sipe_strcase_equal(elem->name,"Authentication-Info")) ) {
374 if (!g_strncasecmp((gchar *)elem->value, name, name_len)) {
375 //purple_debug(PURPLE_DEBUG_MISC, "sipmsg", "elem->value: %s\r\n", elem->value);
376 return elem->value;
379 //purple_debug(PURPLE_DEBUG_MISC, "sipmsg", "moving to next header\r\n");
380 tmp = g_slist_next(tmp);
382 purple_debug(PURPLE_DEBUG_MISC, "sipmsg", "Did not found auth header %s\r\n", name);
383 return NULL;
386 gchar *sipmsg_get_x_mms_im_format(gchar *msgr) {
387 gchar *msgr2;
388 gsize msgr_dec64_len;
389 guchar *msgr_dec64;
390 gchar *msgr_utf8;
391 gchar **lines;
392 gchar **parts;
393 gchar *x_mms_im_format;
394 gchar *tmp;
396 if (!msgr) return NULL;
397 msgr2 = g_strdup(msgr);
398 while (strlen(msgr2) % 4 != 0) {
399 gchar *tmp_msgr2 = msgr2;
400 msgr2 = g_strdup_printf("%s=", msgr2);
401 g_free(tmp_msgr2);
403 msgr_dec64 = g_base64_decode(msgr2, &msgr_dec64_len);
404 msgr_utf8 = g_convert((gchar *) msgr_dec64, msgr_dec64_len, "UTF-8", "UTF-16LE", NULL, NULL, NULL);
405 g_free(msgr_dec64);
406 g_free(msgr2);
407 lines = g_strsplit(msgr_utf8,"\r\n\r\n",0);
408 g_free(msgr_utf8);
409 //@TODO: make extraction like parsing of message headers.
410 parts = g_strsplit(lines[0],"X-MMS-IM-Format:",0);
411 x_mms_im_format = g_strdup(parts[1]);
412 g_strfreev(parts);
413 g_strfreev(lines);
414 tmp = x_mms_im_format;
415 if (x_mms_im_format) {
416 while(*x_mms_im_format==' ' || *x_mms_im_format=='\t') x_mms_im_format++;
418 x_mms_im_format = g_strdup(x_mms_im_format);
419 g_free(tmp);
420 return x_mms_im_format;
423 gchar *sipmsg_get_msgr_string(gchar *x_mms_im_format) {
424 gchar *msgr_orig;
425 gsize msgr_utf16_len;
426 gchar *msgr_utf16;
427 gchar *msgr_enc;
428 gchar *res;
429 int len;
431 if (!x_mms_im_format) return NULL;
432 msgr_orig = g_strdup_printf("X-MMS-IM-Format: %s\r\n\r\n", x_mms_im_format);
433 msgr_utf16 = g_convert(msgr_orig, -1, "UTF-16LE", "UTF-8", NULL, &msgr_utf16_len, NULL);
434 g_free(msgr_orig);
435 msgr_enc = g_base64_encode((guchar *) msgr_utf16, msgr_utf16_len);
436 g_free(msgr_utf16);
437 len = strlen(msgr_enc);
438 while (msgr_enc[len - 1] == '=') len--;
439 res = g_strndup(msgr_enc, len);
440 g_free(msgr_enc);
441 return res;
444 gchar *sipmsg_apply_x_mms_im_format(const char *x_mms_im_format, gchar *body) {
445 char *pre, *post;
446 gchar *res;
448 if (!x_mms_im_format) {
449 return body ? g_strdup(body) : NULL;
451 msn_parse_format(x_mms_im_format, &pre, &post);
452 res = g_strdup_printf("%s%s%s", pre ? pre : "", body ? body : "", post ? post : "");
453 g_free(pre);
454 g_free(post);
455 return res;
458 /* ms-text-format: text/plain; charset=UTF-8;msgr=WAAtAE0...DIADQAKAA0ACgA;ms-body=SGk= */
459 gchar *get_html_message(const gchar *ms_text_format_in, const gchar *body_in)
461 gchar *msgr;
462 gchar *res;
463 gchar *ms_text_format = NULL;
464 gchar *body = NULL;
466 if (g_str_has_prefix(ms_text_format_in, "multipart/related") ||
467 g_str_has_prefix(ms_text_format_in, "multipart/alternative")) {
468 char *doc = g_strdup_printf("Content-Type: %s\r\n\r\n%s", ms_text_format_in, body_in);
469 PurpleMimeDocument *mime;
470 GList* parts;
472 mime = purple_mime_document_parse(doc);
473 parts = purple_mime_document_get_parts(mime);
474 while (parts) {
475 const gchar *content_type = purple_mime_part_get_field(parts->data, "Content-Type");
476 if (content_type) {
477 const gchar *content = purple_mime_part_get_data(parts->data);
478 guint length = purple_mime_part_get_length(parts->data);
480 /* if no other format has stored */
481 if (!ms_text_format && g_str_has_prefix(content_type, "text/plain")) {
482 ms_text_format = g_strdup(content_type);
483 body = g_strndup(content, length);
484 /* preferred format */
485 } else if (g_str_has_prefix(ms_text_format, "text/html")) {
486 g_free(ms_text_format);
487 g_free(body);
488 ms_text_format = g_strdup(content_type);
489 body = g_strndup(content, length);
490 break;
493 parts = parts->next;
495 g_free(doc);
496 if (mime)
497 purple_mime_document_free(mime);
498 } else {
499 ms_text_format = g_strdup(ms_text_format_in);
500 body = g_strdup(body_in);
503 if (body) {
504 res = body;
505 } else {
506 gchar *tmp = sipmsg_find_part_of_header(ms_text_format, "ms-body=", NULL, NULL);
507 gsize len;
508 if (!tmp) {
509 g_free(ms_text_format);
510 return NULL;
512 res = (gchar *) g_base64_decode(tmp, &len);
513 g_free(tmp);
514 if (!res) {
515 g_free(ms_text_format);
516 return NULL;
520 if (!g_str_has_prefix(ms_text_format, "text/html")) { // NOT html
521 char *tmp = res;
522 res = g_markup_escape_text(res, -1); // as this is not html
523 g_free(tmp);
526 msgr = sipmsg_find_part_of_header(ms_text_format, "msgr=", ";", NULL);
527 if (msgr) {
528 gchar *x_mms_im_format = sipmsg_get_x_mms_im_format(msgr);
529 gchar *tmp = res;
530 g_free(msgr);
531 res = sipmsg_apply_x_mms_im_format(x_mms_im_format, res);
532 g_free(tmp);
533 g_free(x_mms_im_format);
536 g_free(ms_text_format);
538 return res;
545 //------------------------------------------------------------------------------------------
546 //TEMP solution to include it here (copy from purple's msn protocol
547 //How to reuse msn's util methods from sipe?
549 /* from internal.h */
550 #define MSG_LEN 2048
551 #define BUF_LEN MSG_LEN
553 static
554 gchar *sipmsg_uri_unescape(const gchar *string)
556 gchar *unescaped, *tmp;
558 if (!string) return(NULL);
560 #if GLIB_CHECK_VERSION(2,16,0)
561 unescaped = g_uri_unescape_string(string, NULL);
562 #else
563 /* loosely based on libpurple/util.c:purple_url_decode() */
565 gsize i = 0;
566 gsize len = strlen(string);
568 unescaped = g_malloc(len + 1);
569 while (len-- > 0) {
570 gchar c = *string++;
571 if ((len >= 2) && (c == '%')) {
572 char hex[3];
573 strncpy(hex, string, 2);
574 hex[2] = '\0';
575 c = strtol(hex, NULL, 16);
576 string += 2;
577 len -= 2;
579 unescaped[i++] = c;
581 unescaped[i] = '\0';
583 #endif
585 if (!g_utf8_validate(unescaped, -1, (const gchar **)&tmp))
586 *tmp = '\0';
588 return(unescaped);
591 void
592 msn_parse_format(const char *mime, char **pre_ret, char **post_ret)
594 char *cur;
595 GString *pre = g_string_new(NULL);
596 GString *post = g_string_new(NULL);
597 unsigned int colors[3];
599 if (pre_ret != NULL) *pre_ret = NULL;
600 if (post_ret != NULL) *post_ret = NULL;
602 cur = strstr(mime, "FN=");
604 if (cur && (*(cur = cur + 3) != ';'))
606 pre = g_string_append(pre, "<FONT FACE=\"");
608 while (*cur && *cur != ';')
610 pre = g_string_append_c(pre, *cur);
611 cur++;
614 pre = g_string_append(pre, "\">");
615 post = g_string_prepend(post, "</FONT>");
618 cur = strstr(mime, "EF=");
620 if (cur && (*(cur = cur + 3) != ';'))
622 while (*cur && *cur != ';')
624 pre = g_string_append_c(pre, '<');
625 pre = g_string_append_c(pre, *cur);
626 pre = g_string_append_c(pre, '>');
627 post = g_string_prepend_c(post, '>');
628 post = g_string_prepend_c(post, *cur);
629 post = g_string_prepend_c(post, '/');
630 post = g_string_prepend_c(post, '<');
631 cur++;
635 cur = strstr(mime, "CO=");
637 if (cur && (*(cur = cur + 3) != ';'))
639 int i;
641 i = sscanf(cur, "%02x%02x%02x;", &colors[0], &colors[1], &colors[2]);
643 if (i > 0)
645 char tag[64];
647 if (i == 1)
649 colors[1] = 0;
650 colors[2] = 0;
652 else if (i == 2)
654 unsigned int temp = colors[0];
656 colors[0] = colors[1];
657 colors[1] = temp;
658 colors[2] = 0;
660 else if (i == 3)
662 unsigned int temp = colors[2];
664 colors[2] = colors[0];
665 colors[0] = temp;
668 g_snprintf(tag, sizeof(tag),
669 "<FONT COLOR=\"#%02hhx%02hhx%02hhx\">",
670 colors[0], colors[1], colors[2]);
672 pre = g_string_append(pre, tag);
673 post = g_string_prepend(post, "</FONT>");
677 cur = strstr(mime, "RL=");
679 if (cur && (*(cur = cur + 3) != ';'))
681 if (*cur == '1')
683 /* RTL text was received */
684 pre = g_string_append(pre, "<SPAN style=\"direction:rtl;text-align:right;\">");
685 post = g_string_prepend(post, "</SPAN>");
689 cur = sipmsg_uri_unescape(pre->str);
690 g_string_free(pre, TRUE);
692 if (pre_ret != NULL)
693 *pre_ret = cur;
694 else
695 g_free(cur);
697 cur = sipmsg_uri_unescape(post->str);
698 g_string_free(post, TRUE);
700 if (post_ret != NULL)
701 *post_ret = cur;
702 else
703 g_free(cur);
706 static const char *
707 encode_spaces(const char *str)
709 static char buf[BUF_LEN];
710 const char *c;
711 char *d;
713 g_return_val_if_fail(str != NULL, NULL);
715 for (c = str, d = buf; *c != '\0'; c++)
717 if (*c == ' ')
719 *d++ = '%';
720 *d++ = '2';
721 *d++ = '0';
723 else
724 *d++ = *c;
726 *d = '\0';
728 return buf;
731 void
732 msn_import_html(const char *html, char **attributes, char **message)
734 int len, retcount = 0;
735 const char *c;
736 char *msg;
737 char *fontface = NULL;
738 char fonteffect[4];
739 char fontcolor[7];
740 char direction = '0';
742 gboolean has_bold = FALSE;
743 gboolean has_italic = FALSE;
744 gboolean has_underline = FALSE;
745 gboolean has_strikethrough = FALSE;
747 g_return_if_fail(html != NULL);
748 g_return_if_fail(attributes != NULL);
749 g_return_if_fail(message != NULL);
751 len = strlen(html);
752 msg = g_malloc0(len + 1);
754 memset(fontcolor, 0, sizeof(fontcolor));
755 strcat(fontcolor, "0");
756 memset(fonteffect, 0, sizeof(fonteffect));
758 for (c = html; *c != '\0';)
760 if (*c == '<')
762 if (!g_ascii_strncasecmp(c + 1, "br>", 3))
764 msg[retcount++] = '\r';
765 msg[retcount++] = '\n';
766 c += 4;
768 else if (!g_ascii_strncasecmp(c + 1, "i>", 2))
770 if (!has_italic)
772 strcat(fonteffect, "I");
773 has_italic = TRUE;
775 c += 3;
777 else if (!g_ascii_strncasecmp(c + 1, "b>", 2))
779 if (!has_bold)
781 strcat(fonteffect, "B");
782 has_bold = TRUE;
784 c += 3;
786 else if (!g_ascii_strncasecmp(c + 1, "u>", 2))
788 if (!has_underline)
790 strcat(fonteffect, "U");
791 has_underline = TRUE;
793 c += 3;
795 else if (!g_ascii_strncasecmp(c + 1, "s>", 2))
797 if (!has_strikethrough)
799 strcat(fonteffect, "S");
800 has_strikethrough = TRUE;
802 c += 3;
804 else if (!g_ascii_strncasecmp(c + 1, "a href=\"", 8))
806 c += 9;
808 if (!g_ascii_strncasecmp(c, "mailto:", 7))
809 c += 7;
811 while ((*c != '\0') && g_ascii_strncasecmp(c, "\">", 2))
812 msg[retcount++] = *c++;
814 if (*c != '\0')
815 c += 2;
817 /* ignore descriptive string */
818 while ((*c != '\0') && g_ascii_strncasecmp(c, "</a>", 4))
819 c++;
821 if (*c != '\0')
822 c += 4;
824 else if (!g_ascii_strncasecmp(c + 1, "span", 4))
826 /* Bi-directional text support using CSS properties in span tags */
827 c += 5;
829 while (*c != '\0' && *c != '>')
831 while (*c == ' ')
832 c++;
833 if (!g_ascii_strncasecmp(c, "dir=\"rtl\"", 9))
835 c += 9;
836 direction = '1';
838 else if (!g_ascii_strncasecmp(c, "style=\"", 7))
840 /* Parse inline CSS attributes */
841 int attr_len = 0;
842 c += 7;
843 while (*(c + attr_len) != '\0' && *(c + attr_len) != '"')
844 attr_len++;
845 if (*(c + attr_len) == '"')
847 char *css_attributes;
848 char *attr_dir;
849 css_attributes = g_strndup(c, attr_len);
850 attr_dir = purple_markup_get_css_property(css_attributes, "direction");
851 g_free(css_attributes);
852 if (attr_dir && (!g_ascii_strncasecmp(attr_dir, "RTL", 3)))
853 direction = '1';
854 g_free(attr_dir);
858 else
860 c++;
863 if (*c == '>')
864 c++;
866 else if (!g_ascii_strncasecmp(c + 1, "font", 4))
868 c += 5;
870 while ((*c != '\0') && !g_ascii_strncasecmp(c, " ", 1))
871 c++;
873 if (!g_ascii_strncasecmp(c, "color=\"#", 7))
875 c += 8;
877 fontcolor[0] = *(c + 4);
878 fontcolor[1] = *(c + 5);
879 fontcolor[2] = *(c + 2);
880 fontcolor[3] = *(c + 3);
881 fontcolor[4] = *c;
882 fontcolor[5] = *(c + 1);
884 c += 8;
886 else if (!g_ascii_strncasecmp(c, "face=\"", 6))
888 const char *end = NULL;
889 const char *comma = NULL;
890 unsigned int namelen = 0;
892 c += 6;
893 end = strchr(c, '\"');
894 comma = strchr(c, ',');
896 if (comma == NULL || comma > end)
897 namelen = (unsigned int)(end - c);
898 else
899 namelen = (unsigned int)(comma - c);
901 g_free(fontface);
902 fontface = g_strndup(c, namelen);
903 c = end + 2;
905 else
907 /* Drop all unrecognized/misparsed font tags */
908 while ((*c != '\0') && g_ascii_strncasecmp(c, "\">", 2))
909 c++;
911 if (*c != '\0')
912 c += 2;
915 else
917 while ((*c != '\0') && (*c != '>'))
918 c++;
919 if (*c != '\0')
920 c++;
923 else if (*c == '&')
925 if (!g_ascii_strncasecmp(c, "&lt;", 4))
927 msg[retcount++] = '<';
928 c += 4;
930 else if (!g_ascii_strncasecmp(c, "&gt;", 4))
932 msg[retcount++] = '>';
933 c += 4;
935 else if (!g_ascii_strncasecmp(c, "&nbsp;", 6))
937 msg[retcount++] = ' ';
938 c += 6;
940 else if (!g_ascii_strncasecmp(c, "&quot;", 6))
942 msg[retcount++] = '"';
943 c += 6;
945 else if (!g_ascii_strncasecmp(c, "&amp;", 5))
947 msg[retcount++] = '&';
948 c += 5;
950 else if (!g_ascii_strncasecmp(c, "&apos;", 6))
952 msg[retcount++] = '\'';
953 c += 6;
955 else
956 msg[retcount++] = *c++;
958 else
959 msg[retcount++] = *c++;
962 if (fontface == NULL)
963 fontface = g_strdup("MS Sans Serif");
965 *attributes = g_strdup_printf("FN=%s; EF=%s; CO=%s; PF=0; RL=%c",
966 encode_spaces(fontface),
967 fonteffect, fontcolor, direction);
968 *message = msg;
970 g_free(fontface);
972 // End of TEMP
975 Local Variables:
976 mode: c
977 c-file-style: "bsd"
978 indent-tabs-mode: t
979 tab-width: 8
980 End: