Updated to release 1.7.1
[siplcs.git] / src / sipmsg.c
blobf962d58f1a028ca91d153800f29b30db8f78749b
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 "sipmsg.h"
33 struct sipmsg *sipmsg_parse_msg(const gchar *msg) {
34 const char *tmp = strstr(msg, "\r\n\r\n");
35 char *line;
36 struct sipmsg *smsg;
38 if(!tmp) return NULL;
40 line = g_strndup(msg, tmp - msg);
42 smsg = sipmsg_parse_header(line);
43 smsg->body = g_strdup(tmp + 4);
45 g_free(line);
46 return smsg;
49 struct sipmsg *sipmsg_parse_header(const gchar *header) {
50 struct sipmsg *msg = g_new0(struct sipmsg,1);
51 gchar **lines = g_strsplit(header,"\r\n",0);
52 gchar **parts;
53 gchar *dummy;
54 gchar *dummy2;
55 gchar *tmp;
56 gchar *contentlength;
57 int i=1;
58 if(!lines[0]) return NULL;
59 parts = g_strsplit(lines[0], " ", 3);
60 if(!parts[0] || !parts[1] || !parts[2]) {
61 g_strfreev(parts);
62 g_strfreev(lines);
63 g_free(msg);
64 return NULL;
66 if(strstr(parts[0],"SIP")) { /* numeric response */
67 msg->method = g_strdup(parts[2]);
68 msg->response = strtol(parts[1],NULL,10);
69 } else { /* request */
70 msg->method = g_strdup(parts[0]);
71 msg->target = g_strdup(parts[1]);
72 msg->response = 0;
74 g_strfreev(parts);
75 for(i=1; lines[i] && strlen(lines[i])>2; i++) {
76 parts = g_strsplit(lines[i], ":", 2);
77 if(!parts[0] || !parts[1]) {
78 g_strfreev(parts);
79 g_strfreev(lines);
80 sipmsg_free(msg);
81 return NULL;
83 dummy = parts[1];
84 dummy2 = 0;
85 while(*dummy==' ' || *dummy=='\t') dummy++;
86 dummy2 = g_strdup(dummy);
87 while(lines[i+1] && (lines[i+1][0]==' ' || lines[i+1][0]=='\t')) {
88 i++;
89 dummy = lines[i];
90 while(*dummy==' ' || *dummy=='\t') dummy++;
91 tmp = g_strdup_printf("%s %s",dummy2, dummy);
92 g_free(dummy2);
93 dummy2 = tmp;
95 sipmsg_add_header_now(msg, parts[0], dummy2);
96 g_free(dummy2);
97 g_strfreev(parts);
99 g_strfreev(lines);
100 contentlength = sipmsg_find_header(msg, "Content-Length");
101 if (contentlength) {
102 msg->bodylen = strtol(contentlength,NULL,10);
103 } else {
104 purple_debug_fatal("sipe", "sipmsg_parse_header(): Content-Length header not found\n");
106 if(msg->response) {
107 g_free(msg->method);
108 tmp = sipmsg_find_header(msg, "CSeq");
109 if(!tmp) {
110 /* SHOULD NOT HAPPEN */
111 msg->method = 0;
112 } else {
113 parts = g_strsplit(tmp, " ", 2);
114 msg->method = g_strdup(parts[1]);
115 g_strfreev(parts);
118 return msg;
121 void sipmsg_print(const struct sipmsg *msg) {
122 GSList *cur;
123 struct siphdrelement *elem;
124 purple_debug(PURPLE_DEBUG_MISC, "sipe", "SIP MSG\n");
125 purple_debug(PURPLE_DEBUG_MISC, "sipe", "response: %d\nmethod: %s\nbodylen: %d\n",msg->response,msg->method,msg->bodylen);
126 if(msg->target) purple_debug(PURPLE_DEBUG_MISC, "sipe", "target: %s\n",msg->target);
127 cur = msg->headers;
128 while(cur) {
129 elem = cur->data;
130 purple_debug(PURPLE_DEBUG_MISC, "sipe", "name: %s value: %s\n",elem->name, elem->value);
131 cur = g_slist_next(cur);
135 char *sipmsg_to_string(const struct sipmsg *msg) {
136 GSList *cur;
137 GString *outstr = g_string_new("");
138 struct siphdrelement *elem;
140 if(msg->response)
141 g_string_append_printf(outstr, "SIP/2.0 %d Unknown\r\n",
142 msg->response);
143 else
144 g_string_append_printf(outstr, "%s %s SIP/2.0\r\n",
145 msg->method, msg->target);
147 cur = msg->headers;
148 while(cur) {
149 elem = cur->data;
150 /*Todo: remove the LFCR in a good way*/
151 /*if(!strcmp(elem->name,"Proxy-Authorization"))
152 g_string_append_printf(outstr, "%s: %s", elem->name,
153 elem->value);
154 else */
155 g_string_append_printf(outstr, "%s: %s\r\n", elem->name,
156 elem->value);
157 cur = g_slist_next(cur);
160 g_string_append_printf(outstr, "\r\n%s", msg->bodylen ? msg->body : "");
162 return g_string_free(outstr, FALSE);
166 * Adds header to current message headers at specified position
168 void sipmsg_add_header_now_pos(struct sipmsg *msg, const gchar *name, const gchar *value, int pos) {
169 struct siphdrelement *element = g_new0(struct siphdrelement,1);
170 element->name = g_strdup(name);
171 element->value = g_strdup(value);
172 msg->headers = g_slist_insert(msg->headers, element,pos);
176 * Adds header to current message headers
178 void sipmsg_add_header_now(struct sipmsg *msg, const gchar *name, const gchar *value) {
179 struct siphdrelement *element = g_new0(struct siphdrelement,1);
180 element->name = g_strdup(name);
181 element->value = g_strdup(value);
182 msg->headers = g_slist_append(msg->headers, element);
186 * Adds header to separate storage for future merge
188 void sipmsg_add_header(struct sipmsg *msg, const gchar *name, const gchar *value) {
189 struct siphdrelement *element = g_new0(struct siphdrelement,1);
190 element->name = g_strdup(name);
191 element->value = g_strdup(value);
192 msg->new_headers = g_slist_append(msg->new_headers, element);
196 * Removes header if it's not in keepers array
198 void sipmsg_strip_headers(struct sipmsg *msg, const gchar *keepers[]) {
199 GSList *entry;
200 struct siphdrelement *elem;
202 entry = msg->headers;
203 while(entry) {
204 int i = 0;
205 gboolean keeper = FALSE;
207 elem = entry->data;
208 while (keepers[i]) {
209 if (!g_strcasecmp(elem->name, keepers[i])) {
210 keeper = TRUE;
211 break;
213 i++;
216 if (!keeper) {
217 GSList *to_delete = entry;
218 purple_debug_info("sipe", "sipmsg_strip_headers: removing %s\n", elem->name);
219 entry = g_slist_next(entry);
220 msg->headers = g_slist_delete_link(msg->headers, to_delete);
221 g_free(elem->name);
222 g_free(elem->value);
223 g_free(elem);
224 } else {
225 entry = g_slist_next(entry);
231 * Merges newly added headers to message
233 void sipmsg_merge_new_headers(struct sipmsg *msg) {
234 while(msg->new_headers) {
235 msg->headers = g_slist_append(msg->headers, msg->new_headers->data);
236 msg->new_headers = g_slist_remove(msg->new_headers, msg->new_headers->data);
240 void sipmsg_free(struct sipmsg *msg) {
241 struct siphdrelement *elem;
242 while(msg->headers) {
243 elem = msg->headers->data;
244 msg->headers = g_slist_remove(msg->headers,elem);
245 g_free(elem->name);
246 g_free(elem->value);
247 g_free(elem);
249 while(msg->new_headers) {
250 elem = msg->new_headers->data;
251 msg->new_headers = g_slist_remove(msg->new_headers,elem);
252 g_free(elem->name);
253 g_free(elem->value);
254 g_free(elem);
256 g_free(msg->signature);
257 g_free(msg->rand);
258 g_free(msg->num);
259 g_free(msg->method);
260 g_free(msg->target);
261 g_free(msg->body);
262 g_free(msg);
265 void sipmsg_remove_header_now(struct sipmsg *msg, const gchar *name) {
266 struct siphdrelement *elem;
267 GSList *tmp = msg->headers;
268 while(tmp) {
269 elem = tmp->data;
270 // OCS2005 can send the same header in either all caps or mixed case
271 if (g_ascii_strcasecmp(elem->name, name)==0) {
272 msg->headers = g_slist_remove(msg->headers, elem);
273 g_free(elem->name);
274 g_free(elem->value);
275 g_free(elem);
276 return;
278 tmp = g_slist_next(tmp);
280 return;
283 gchar *sipmsg_find_header(const struct sipmsg *msg, const gchar *name) {
284 return sipmsg_find_header_instance (msg, name, 0);
287 gchar *sipmsg_find_header_instance(const struct sipmsg *msg, const gchar *name, int which) {
288 GSList *tmp;
289 struct siphdrelement *elem;
290 int i = 0;
291 tmp = msg->headers;
292 while(tmp) {
293 elem = tmp->data;
294 // OCS2005 can send the same header in either all caps or mixed case
295 if (g_ascii_strcasecmp(elem->name,name)==0) {
296 if (i == which) {
297 return elem->value;
299 i++;
301 tmp = g_slist_next(tmp);
303 return NULL;
306 gchar *sipmsg_find_part_of_header(const char *hdr, const char * before, const char * after, const char * def) {
307 const char *tmp;
308 const char *tmp2;
309 gchar *res2;
310 if (!hdr) {
311 return NULL;
314 //printf("partof %s w/ %s before and %s after\n", hdr, before, after);
316 tmp = before == NULL ? hdr : strstr(hdr, before);
317 if (!tmp) {
318 //printf ("not found, returning null\n");
319 return (gchar *)def;
322 if (before != NULL) {
323 tmp += strlen(before);
324 //printf ("tmp now %s\n", tmp);
327 if (after != NULL && (tmp2 = strstr(tmp, after))) {
328 gchar * res = g_strndup(tmp, tmp2 - tmp);
329 //printf("returning %s\n", res);
330 return res;
332 res2 = g_strdup(tmp);
333 //printf("returning %s\n", res2);
334 return res2;
338 * Parse EndPoints header from INVITE request
339 * Returns a list of end points: contact URI plus optional epid.
340 * You must free the values and the list.
342 * Example headers:
343 * EndPoints: "alice alisson" <sip:alice@atlanta.local>, <sip:bob@atlanta.local>;epid=ebca82d94d, <sip:carol@atlanta.local>
344 * EndPoints: "alice, alisson" <sip:alice@atlanta.local>, <sip:bob@atlanta.local>
345 * EndPoints: "alice alisson" <sip:alice@atlanta.local>, "Super, Man" <sip:super@atlanta.local>
347 * @param header (in) EndPoints header contents
349 * @return GSList with struct sipendpoint as elements
351 GSList *sipmsg_parse_endpoints_header(const gchar *header)
353 GSList *list = NULL;
354 gchar **parts = g_strsplit(header, ",", 0);
355 gchar *part;
356 int i;
358 for (i = 0; (part = parts[i]) != NULL; i++) {
359 /* Does the part contain a URI? */
360 gchar *contact = sipmsg_find_part_of_header(part, "<", ">", NULL);
361 if (contact) {
362 struct sipendpoint *end_point = g_new(struct sipendpoint, 1);
363 end_point->contact = contact;
364 end_point->epid = sipmsg_find_part_of_header(part, "epid=", NULL, NULL);
365 list = g_slist_append(list, end_point);
368 g_strfreev(parts);
370 return(list);
374 * sipmsg_find_auth_header will return the particular WWW-Authenticate
375 * header specified by *name.
377 * Use this function when you want to look for a specific authentication
378 * method such as NTLM or Kerberos
381 gchar *sipmsg_find_auth_header(struct sipmsg *msg, const gchar *name) {
382 GSList *tmp;
383 struct siphdrelement *elem;
384 int name_len = strlen(name);
385 tmp = msg->headers;
386 while(tmp) {
387 elem = tmp->data;
388 //purple_debug(PURPLE_DEBUG_MISC, "sipmsg", "Current header: %s\r\n", elem->value);
389 if (elem && elem->name &&
390 (!g_ascii_strcasecmp(elem->name,"WWW-Authenticate")
391 || !g_ascii_strcasecmp(elem->name,"Authentication-Info")) ) {
392 if (!g_strncasecmp((gchar *)elem->value, name, name_len)) {
393 //purple_debug(PURPLE_DEBUG_MISC, "sipmsg", "elem->value: %s\r\n", elem->value);
394 return elem->value;
397 //purple_debug(PURPLE_DEBUG_MISC, "sipmsg", "moving to next header\r\n");
398 tmp = g_slist_next(tmp);
400 purple_debug(PURPLE_DEBUG_MISC, "sipmsg", "Did not found auth header %s\r\n", name);
401 return NULL;
404 gchar *sipmsg_get_x_mms_im_format(gchar *msgr) {
405 gchar *msgr2;
406 gsize msgr_dec64_len;
407 guchar *msgr_dec64;
408 gchar *msgr_utf8;
409 gchar **lines;
410 gchar **parts;
411 gchar *x_mms_im_format;
412 gchar *tmp;
414 if (!msgr) return NULL;
415 msgr2 = g_strdup(msgr);
416 while (strlen(msgr2) % 4 != 0) {
417 gchar *tmp_msgr2 = msgr2;
418 msgr2 = g_strdup_printf("%s=", msgr2);
419 g_free(tmp_msgr2);
421 msgr_dec64 = purple_base64_decode(msgr2, &msgr_dec64_len);
422 msgr_utf8 = g_convert((gchar *) msgr_dec64, msgr_dec64_len, "UTF-8", "UTF-16LE", NULL, NULL, NULL);
423 g_free(msgr_dec64);
424 g_free(msgr2);
425 lines = g_strsplit(msgr_utf8,"\r\n\r\n",0);
426 g_free(msgr_utf8);
427 //@TODO: make extraction like parsing of message headers.
428 parts = g_strsplit(lines[0],"X-MMS-IM-Format:",0);
429 x_mms_im_format = g_strdup(parts[1]);
430 g_strfreev(parts);
431 g_strfreev(lines);
432 tmp = x_mms_im_format;
433 if (x_mms_im_format) {
434 while(*x_mms_im_format==' ' || *x_mms_im_format=='\t') x_mms_im_format++;
436 x_mms_im_format = g_strdup(x_mms_im_format);
437 g_free(tmp);
438 return x_mms_im_format;
441 gchar *sipmsg_get_msgr_string(gchar *x_mms_im_format) {
442 gchar *msgr_orig;
443 gsize msgr_utf16_len;
444 gchar *msgr_utf16;
445 gchar *msgr_enc;
446 gchar *res;
447 int len;
449 if (!x_mms_im_format) return NULL;
450 msgr_orig = g_strdup_printf("X-MMS-IM-Format: %s\r\n\r\n", x_mms_im_format);
451 msgr_utf16 = g_convert(msgr_orig, -1, "UTF-16LE", "UTF-8", NULL, &msgr_utf16_len, NULL);
452 g_free(msgr_orig);
453 msgr_enc = purple_base64_encode((guchar *) msgr_utf16, msgr_utf16_len);
454 g_free(msgr_utf16);
455 len = strlen(msgr_enc);
456 while (msgr_enc[len - 1] == '=') len--;
457 res = g_strndup(msgr_enc, len);
458 g_free(msgr_enc);
459 return res;
462 gchar *sipmsg_apply_x_mms_im_format(const char *x_mms_im_format, gchar *body) {
463 char *pre, *post;
464 gchar *res;
466 if (!x_mms_im_format) {
467 return body ? g_strdup(body) : NULL;
469 msn_parse_format(x_mms_im_format, &pre, &post);
470 res = g_strdup_printf("%s%s%s", pre ? pre : "", body ? body : "", post ? post : "");
471 g_free(pre);
472 g_free(post);
473 return res;
476 /* ms-text-format: text/plain; charset=UTF-8;msgr=WAAtAE0...DIADQAKAA0ACgA;ms-body=SGk= */
477 gchar *get_html_message(const gchar *ms_text_format_in, const gchar *body_in)
479 gchar *tmp_html;
480 gchar *msgr;
481 gchar *res;
482 gchar *ms_text_format = NULL;
483 gchar *body = NULL;
485 if (!strncmp(ms_text_format_in, "multipart/related", 21)) {
486 char *doc = g_strdup_printf("Content-Type: %s\r\n\r\n%s", ms_text_format_in, body_in);
487 PurpleMimeDocument *mime;
488 GList* parts;
490 mime = purple_mime_document_parse(doc);
491 parts = purple_mime_document_get_parts(mime);
492 while (parts) {
493 const gchar *content = purple_mime_part_get_data(parts->data);
494 guint length = purple_mime_part_get_length(parts->data);
495 const gchar *content_type = purple_mime_part_get_field(parts->data, "Content-Type");
496 if (content_type && !strncmp(content_type, "text/plain", 10) && !ms_text_format) /* if no other format has stored */
498 ms_text_format = g_strdup(content_type);
499 body = g_strndup(content, length);
501 else if (content_type && !strncmp(ms_text_format, "text/html", 9)) /* preferred format */
503 g_free(ms_text_format);
504 g_free(body);
505 ms_text_format = g_strdup(content_type);
506 body = g_strndup(content, length);
507 break;
509 parts = parts->next;
511 g_free(doc);
512 if (mime)
513 purple_mime_document_free(mime);
514 } else {
515 ms_text_format = g_strdup(ms_text_format_in);
516 body = g_strdup(body_in);
519 if (body) {
520 res = g_strdup(body);
521 } else {
522 res = sipmsg_find_part_of_header(ms_text_format, "ms-body=", NULL, NULL);
523 if (!res) return NULL;
524 tmp_html = res;
525 res = (gchar *) purple_base64_decode(res, NULL);
526 g_free(tmp_html);
529 if (!res) {
530 return NULL;
533 if (strncmp(ms_text_format, "text/html", 9)) { // NOT html
534 tmp_html = res;
535 res = g_markup_escape_text(res, -1); // as this is not html
536 g_free(tmp_html);
539 msgr = sipmsg_find_part_of_header(ms_text_format, "msgr=", ";", NULL);
540 if (msgr) {
541 gchar *x_mms_im_format = sipmsg_get_x_mms_im_format(msgr);
542 g_free(msgr);
543 tmp_html = res;
544 res = sipmsg_apply_x_mms_im_format(x_mms_im_format, res);
545 g_free(tmp_html);
546 g_free(x_mms_im_format);
549 g_free(ms_text_format);
550 g_free(body);
552 return res;
559 //------------------------------------------------------------------------------------------
560 //TEMP solution to include it here (copy from purple's msn protocol
561 //How to reuse msn's util methods from sipe?
563 // from internal.h for linux compilation
564 #ifndef _WIN32
565 #define MSG_LEN 2048
566 #define BUF_LEN MSG_LEN
567 #endif
568 void
569 msn_parse_format(const char *mime, char **pre_ret, char **post_ret)
571 char *cur;
572 GString *pre = g_string_new(NULL);
573 GString *post = g_string_new(NULL);
574 unsigned int colors[3];
576 if (pre_ret != NULL) *pre_ret = NULL;
577 if (post_ret != NULL) *post_ret = NULL;
579 cur = strstr(mime, "FN=");
581 if (cur && (*(cur = cur + 3) != ';'))
583 pre = g_string_append(pre, "<FONT FACE=\"");
585 while (*cur && *cur != ';')
587 pre = g_string_append_c(pre, *cur);
588 cur++;
591 pre = g_string_append(pre, "\">");
592 post = g_string_prepend(post, "</FONT>");
595 cur = strstr(mime, "EF=");
597 if (cur && (*(cur = cur + 3) != ';'))
599 while (*cur && *cur != ';')
601 pre = g_string_append_c(pre, '<');
602 pre = g_string_append_c(pre, *cur);
603 pre = g_string_append_c(pre, '>');
604 post = g_string_prepend_c(post, '>');
605 post = g_string_prepend_c(post, *cur);
606 post = g_string_prepend_c(post, '/');
607 post = g_string_prepend_c(post, '<');
608 cur++;
612 cur = strstr(mime, "CO=");
614 if (cur && (*(cur = cur + 3) != ';'))
616 int i;
618 i = sscanf(cur, "%02x%02x%02x;", &colors[0], &colors[1], &colors[2]);
620 if (i > 0)
622 char tag[64];
624 if (i == 1)
626 colors[1] = 0;
627 colors[2] = 0;
629 else if (i == 2)
631 unsigned int temp = colors[0];
633 colors[0] = colors[1];
634 colors[1] = temp;
635 colors[2] = 0;
637 else if (i == 3)
639 unsigned int temp = colors[2];
641 colors[2] = colors[0];
642 colors[0] = temp;
645 g_snprintf(tag, sizeof(tag),
646 "<FONT COLOR=\"#%02hhx%02hhx%02hhx\">",
647 colors[0], colors[1], colors[2]);
649 pre = g_string_append(pre, tag);
650 post = g_string_prepend(post, "</FONT>");
654 cur = strstr(mime, "RL=");
656 if (cur && (*(cur = cur + 3) != ';'))
658 if (*cur == '1')
660 /* RTL text was received */
661 pre = g_string_append(pre, "<SPAN style=\"direction:rtl;text-align:right;\">");
662 post = g_string_prepend(post, "</SPAN>");
666 cur = g_strdup(purple_url_decode(pre->str));
667 g_string_free(pre, TRUE);
669 if (pre_ret != NULL)
670 *pre_ret = cur;
671 else
672 g_free(cur);
674 cur = g_strdup(purple_url_decode(post->str));
675 g_string_free(post, TRUE);
677 if (post_ret != NULL)
678 *post_ret = cur;
679 else
680 g_free(cur);
683 static const char *
684 encode_spaces(const char *str)
686 static char buf[BUF_LEN];
687 const char *c;
688 char *d;
690 g_return_val_if_fail(str != NULL, NULL);
692 for (c = str, d = buf; *c != '\0'; c++)
694 if (*c == ' ')
696 *d++ = '%';
697 *d++ = '2';
698 *d++ = '0';
700 else
701 *d++ = *c;
703 *d = '\0';
705 return buf;
708 void
709 msn_import_html(const char *html, char **attributes, char **message)
711 int len, retcount = 0;
712 const char *c;
713 char *msg;
714 char *fontface = NULL;
715 char fonteffect[4];
716 char fontcolor[7];
717 char direction = '0';
719 gboolean has_bold = FALSE;
720 gboolean has_italic = FALSE;
721 gboolean has_underline = FALSE;
722 gboolean has_strikethrough = FALSE;
724 g_return_if_fail(html != NULL);
725 g_return_if_fail(attributes != NULL);
726 g_return_if_fail(message != NULL);
728 len = strlen(html);
729 msg = g_malloc0(len + 1);
731 memset(fontcolor, 0, sizeof(fontcolor));
732 strcat(fontcolor, "0");
733 memset(fonteffect, 0, sizeof(fonteffect));
735 for (c = html; *c != '\0';)
737 if (*c == '<')
739 if (!g_ascii_strncasecmp(c + 1, "br>", 3))
741 msg[retcount++] = '\r';
742 msg[retcount++] = '\n';
743 c += 4;
745 else if (!g_ascii_strncasecmp(c + 1, "i>", 2))
747 if (!has_italic)
749 strcat(fonteffect, "I");
750 has_italic = TRUE;
752 c += 3;
754 else if (!g_ascii_strncasecmp(c + 1, "b>", 2))
756 if (!has_bold)
758 strcat(fonteffect, "B");
759 has_bold = TRUE;
761 c += 3;
763 else if (!g_ascii_strncasecmp(c + 1, "u>", 2))
765 if (!has_underline)
767 strcat(fonteffect, "U");
768 has_underline = TRUE;
770 c += 3;
772 else if (!g_ascii_strncasecmp(c + 1, "s>", 2))
774 if (!has_strikethrough)
776 strcat(fonteffect, "S");
777 has_strikethrough = TRUE;
779 c += 3;
781 else if (!g_ascii_strncasecmp(c + 1, "a href=\"", 8))
783 c += 9;
785 if (!g_ascii_strncasecmp(c, "mailto:", 7))
786 c += 7;
788 while ((*c != '\0') && g_ascii_strncasecmp(c, "\">", 2))
789 msg[retcount++] = *c++;
791 if (*c != '\0')
792 c += 2;
794 /* ignore descriptive string */
795 while ((*c != '\0') && g_ascii_strncasecmp(c, "</a>", 4))
796 c++;
798 if (*c != '\0')
799 c += 4;
801 else if (!g_ascii_strncasecmp(c + 1, "span", 4))
803 /* Bi-directional text support using CSS properties in span tags */
804 c += 5;
806 while (*c != '\0' && *c != '>')
808 while (*c == ' ')
809 c++;
810 if (!g_ascii_strncasecmp(c, "dir=\"rtl\"", 9))
812 c += 9;
813 direction = '1';
815 else if (!g_ascii_strncasecmp(c, "style=\"", 7))
817 /* Parse inline CSS attributes */
818 char *attributes;
819 int attr_len = 0;
820 c += 7;
821 while (*(c + attr_len) != '\0' && *(c + attr_len) != '"')
822 attr_len++;
823 if (*(c + attr_len) == '"')
825 char *attr_dir;
826 attributes = g_strndup(c, attr_len);
827 attr_dir = purple_markup_get_css_property(attributes, "direction");
828 if (attr_dir && (!g_ascii_strncasecmp(attr_dir, "RTL", 3)))
829 direction = '1';
830 g_free(attr_dir);
831 g_free(attributes);
835 else
837 c++;
840 if (*c == '>')
841 c++;
843 else if (!g_ascii_strncasecmp(c + 1, "font", 4))
845 c += 5;
847 while ((*c != '\0') && !g_ascii_strncasecmp(c, " ", 1))
848 c++;
850 if (!g_ascii_strncasecmp(c, "color=\"#", 7))
852 c += 8;
854 fontcolor[0] = *(c + 4);
855 fontcolor[1] = *(c + 5);
856 fontcolor[2] = *(c + 2);
857 fontcolor[3] = *(c + 3);
858 fontcolor[4] = *c;
859 fontcolor[5] = *(c + 1);
861 c += 8;
863 else if (!g_ascii_strncasecmp(c, "face=\"", 6))
865 const char *end = NULL;
866 const char *comma = NULL;
867 unsigned int namelen = 0;
869 c += 6;
870 end = strchr(c, '\"');
871 comma = strchr(c, ',');
873 if (comma == NULL || comma > end)
874 namelen = (unsigned int)(end - c);
875 else
876 namelen = (unsigned int)(comma - c);
878 fontface = g_strndup(c, namelen);
879 c = end + 2;
881 else
883 /* Drop all unrecognized/misparsed font tags */
884 while ((*c != '\0') && g_ascii_strncasecmp(c, "\">", 2))
885 c++;
887 if (*c != '\0')
888 c += 2;
891 else
893 while ((*c != '\0') && (*c != '>'))
894 c++;
895 if (*c != '\0')
896 c++;
899 else if (*c == '&')
901 if (!g_ascii_strncasecmp(c, "&lt;", 4))
903 msg[retcount++] = '<';
904 c += 4;
906 else if (!g_ascii_strncasecmp(c, "&gt;", 4))
908 msg[retcount++] = '>';
909 c += 4;
911 else if (!g_ascii_strncasecmp(c, "&nbsp;", 6))
913 msg[retcount++] = ' ';
914 c += 6;
916 else if (!g_ascii_strncasecmp(c, "&quot;", 6))
918 msg[retcount++] = '"';
919 c += 6;
921 else if (!g_ascii_strncasecmp(c, "&amp;", 5))
923 msg[retcount++] = '&';
924 c += 5;
926 else if (!g_ascii_strncasecmp(c, "&apos;", 6))
928 msg[retcount++] = '\'';
929 c += 6;
931 else
932 msg[retcount++] = *c++;
934 else
935 msg[retcount++] = *c++;
938 if (fontface == NULL)
939 fontface = g_strdup("MS Sans Serif");
941 *attributes = g_strdup_printf("FN=%s; EF=%s; CO=%s; PF=0; RL=%c",
942 encode_spaces(fontface),
943 fonteffect, fontcolor, direction);
944 *message = msg;
946 g_free(fontface);
948 // End of TEMP
951 Local Variables:
952 mode: c
953 c-file-style: "bsd"
954 indent-tabs-mode: t
955 tab-width: 8
956 End: