Send BYE when response to IM message is 408/480/481
[siplcs.git] / src / core / sipmsg.c
blobb173e167aa047e6973eeb1a8afe5f165da0af1b2
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 struct sipmsg *sipmsg_parse_header(const gchar *header) {
51 struct sipmsg *msg = g_new0(struct sipmsg,1);
52 gchar **lines = g_strsplit(header,"\r\n",0);
53 gchar **parts;
54 const gchar *contentlength;
55 if(!lines[0]) {
56 g_strfreev(lines);
57 g_free(msg);
58 return NULL;
60 parts = g_strsplit(lines[0], " ", 3);
61 if(!parts[0] || !parts[1] || !parts[2]) {
62 g_strfreev(parts);
63 g_strfreev(lines);
64 g_free(msg);
65 return NULL;
67 if(strstr(parts[0],"SIP") || strstr(parts[0],"HTTP")) { /* numeric response */
68 msg->method = g_strdup(parts[2]);
69 msg->response = strtol(parts[1],NULL,10);
70 } else { /* request */
71 msg->method = g_strdup(parts[0]);
72 msg->target = g_strdup(parts[1]);
73 msg->response = 0;
75 g_strfreev(parts);
76 if (sipe_utils_parse_lines(&msg->headers,lines + 1) == FALSE) {
77 g_strfreev(lines);
78 sipmsg_free(msg);
79 return NULL;
81 g_strfreev(lines);
82 contentlength = sipmsg_find_header(msg, "Content-Length");
83 if (contentlength) {
84 msg->bodylen = strtol(contentlength,NULL,10);
85 } else {
86 purple_debug_fatal("sipe", "sipmsg_parse_header(): Content-Length header not found\n");
88 if(msg->response) {
89 const gchar *tmp;
90 g_free(msg->method);
91 tmp = sipmsg_find_header(msg, "CSeq");
92 if(!tmp) {
93 /* SHOULD NOT HAPPEN */
94 msg->method = 0;
95 } else {
96 parts = g_strsplit(tmp, " ", 2);
97 msg->method = g_strdup(parts[1]);
98 g_strfreev(parts);
101 return msg;
104 void sipmsg_print(const struct sipmsg *msg) {
105 GSList *cur;
106 struct sipnameval *elem;
107 purple_debug(PURPLE_DEBUG_MISC, "sipe", "SIP MSG\n");
108 purple_debug(PURPLE_DEBUG_MISC, "sipe", "response: %d\nmethod: %s\nbodylen: %d\n",msg->response,msg->method,msg->bodylen);
109 if(msg->target) purple_debug(PURPLE_DEBUG_MISC, "sipe", "target: %s\n",msg->target);
110 cur = msg->headers;
111 while(cur) {
112 elem = cur->data;
113 purple_debug(PURPLE_DEBUG_MISC, "sipe", "name: %s value: %s\n",elem->name, elem->value);
114 cur = g_slist_next(cur);
118 char *sipmsg_to_string(const struct sipmsg *msg) {
119 GSList *cur;
120 GString *outstr = g_string_new("");
121 struct sipnameval *elem;
123 if(msg->response)
124 g_string_append_printf(outstr, "SIP/2.0 %d Unknown\r\n",
125 msg->response);
126 else
127 g_string_append_printf(outstr, "%s %s SIP/2.0\r\n",
128 msg->method, msg->target);
130 cur = msg->headers;
131 while(cur) {
132 elem = cur->data;
133 /*Todo: remove the LFCR in a good way*/
134 /*if(sipe_strequal(elem->name,"Proxy-Authorization"))
135 g_string_append_printf(outstr, "%s: %s", elem->name,
136 elem->value);
137 else */
138 g_string_append_printf(outstr, "%s: %s\r\n", elem->name,
139 elem->value);
140 cur = g_slist_next(cur);
143 g_string_append_printf(outstr, "\r\n%s", msg->bodylen ? msg->body : "");
145 return g_string_free(outstr, FALSE);
149 * Adds header to current message headers at specified position
151 void sipmsg_add_header_now_pos(struct sipmsg *msg, const gchar *name, const gchar *value, int pos) {
152 struct sipnameval *element = g_new0(struct sipnameval,1);
154 /* SANITY CHECK: the calling code must be fixed if this happens! */
155 if (!value) {
156 purple_debug(PURPLE_DEBUG_ERROR, "sipe", "sipmsg_add_header_now_pos: NULL value for %s (%d)\n",
157 name, pos);
158 value = "";
161 element->name = g_strdup(name);
162 element->value = g_strdup(value);
163 msg->headers = g_slist_insert(msg->headers, element,pos);
167 * Adds header to current message headers
169 void sipmsg_add_header_now(struct sipmsg *msg, const gchar *name, const gchar *value) {
170 struct sipnameval *element = g_new0(struct sipnameval,1);
172 /* SANITY CHECK: the calling code must be fixed if this happens! */
173 if (!value) {
174 purple_debug(PURPLE_DEBUG_ERROR, "sipe", "sipmsg_add_header_now: NULL value for %s\n",
175 name);
176 value = "";
179 element->name = g_strdup(name);
180 element->value = g_strdup(value);
181 msg->headers = g_slist_append(msg->headers, element);
185 * Adds header to separate storage for future merge
187 void sipmsg_add_header(struct sipmsg *msg, const gchar *name, const gchar *value) {
188 struct sipnameval *element = g_new0(struct sipnameval,1);
190 /* SANITY CHECK: the calling code must be fixed if this happens! */
191 if (!value) {
192 purple_debug(PURPLE_DEBUG_ERROR, "sipe", "sipmsg_add_header: NULL value for %s\n",
193 name);
194 value = "";
197 element->name = g_strdup(name);
198 element->value = g_strdup(value);
199 msg->new_headers = g_slist_append(msg->new_headers, element);
203 * Removes header if it's not in keepers array
205 void sipmsg_strip_headers(struct sipmsg *msg, const gchar *keepers[]) {
206 GSList *entry;
207 struct sipnameval *elem;
209 entry = msg->headers;
210 while(entry) {
211 int i = 0;
212 gboolean keeper = FALSE;
214 elem = entry->data;
215 while (keepers[i]) {
216 if (!g_strcasecmp(elem->name, keepers[i])) {
217 keeper = TRUE;
218 break;
220 i++;
223 if (!keeper) {
224 GSList *to_delete = entry;
225 purple_debug_info("sipe", "sipmsg_strip_headers: removing %s\n", elem->name);
226 entry = g_slist_next(entry);
227 msg->headers = g_slist_delete_link(msg->headers, to_delete);
228 g_free(elem->name);
229 g_free(elem->value);
230 g_free(elem);
231 } else {
232 entry = g_slist_next(entry);
238 * Merges newly added headers to message
240 void sipmsg_merge_new_headers(struct sipmsg *msg) {
241 while(msg->new_headers) {
242 msg->headers = g_slist_append(msg->headers, msg->new_headers->data);
243 msg->new_headers = g_slist_remove(msg->new_headers, msg->new_headers->data);
247 void sipmsg_free(struct sipmsg *msg) {
248 sipe_utils_nameval_free(msg->headers);
249 sipe_utils_nameval_free(msg->new_headers);
250 g_free(msg->signature);
251 g_free(msg->rand);
252 g_free(msg->num);
253 g_free(msg->method);
254 g_free(msg->target);
255 g_free(msg->body);
256 g_free(msg);
259 void sipmsg_remove_header_now(struct sipmsg *msg, const gchar *name) {
260 struct sipnameval *elem;
261 GSList *tmp = msg->headers;
262 while(tmp) {
263 elem = tmp->data;
264 // OCS2005 can send the same header in either all caps or mixed case
265 if (g_ascii_strcasecmp(elem->name, name)==0) {
266 msg->headers = g_slist_remove(msg->headers, elem);
267 g_free(elem->name);
268 g_free(elem->value);
269 g_free(elem);
270 return;
272 tmp = g_slist_next(tmp);
274 return;
277 const gchar *sipmsg_find_header(const struct sipmsg *msg, const gchar *name) {
278 return sipe_utils_nameval_find_instance (msg->headers, name, 0);
281 const gchar *sipmsg_find_header_instance(const struct sipmsg *msg, const gchar *name, int which) {
282 return sipe_utils_nameval_find_instance(msg->headers, name, which);
285 gchar *sipmsg_find_part_of_header(const char *hdr, const char * before, const char * after, const char * def) {
286 const char *tmp;
287 const char *tmp2;
288 gchar *res2;
289 if (!hdr) {
290 return NULL;
293 //printf("partof %s w/ %s before and %s after\n", hdr, before, after);
295 tmp = before == NULL ? hdr : strstr(hdr, before);
296 if (!tmp) {
297 //printf ("not found, returning null\n");
298 return (gchar *)def;
301 if (before != NULL) {
302 tmp += strlen(before);
303 //printf ("tmp now %s\n", tmp);
306 if (after != NULL && (tmp2 = strstr(tmp, after))) {
307 gchar * res = g_strndup(tmp, tmp2 - tmp);
308 //printf("returning %s\n", res);
309 return res;
311 res2 = g_strdup(tmp);
312 //printf("returning %s\n", res2);
313 return res2;
317 * Parse EndPoints header from INVITE request
318 * Returns a list of end points: contact URI plus optional epid.
319 * You must free the values and the list.
321 * Example headers:
322 * EndPoints: "alice alisson" <sip:alice@atlanta.local>, <sip:bob@atlanta.local>;epid=ebca82d94d, <sip:carol@atlanta.local>
323 * EndPoints: "alice, alisson" <sip:alice@atlanta.local>, <sip:bob@atlanta.local>
324 * EndPoints: "alice alisson" <sip:alice@atlanta.local>, "Super, Man" <sip:super@atlanta.local>
326 * @param header (in) EndPoints header contents
328 * @return GSList with struct sipendpoint as elements
330 GSList *sipmsg_parse_endpoints_header(const gchar *header)
332 GSList *list = NULL;
333 gchar **parts = g_strsplit(header, ",", 0);
334 gchar *part;
335 int i;
337 for (i = 0; (part = parts[i]) != NULL; i++) {
338 /* Does the part contain a URI? */
339 gchar *contact = sipmsg_find_part_of_header(part, "<", ">", NULL);
340 if (contact) {
341 struct sipendpoint *end_point = g_new(struct sipendpoint, 1);
342 end_point->contact = contact;
343 end_point->epid = sipmsg_find_part_of_header(part, "epid=", NULL, NULL);
344 list = g_slist_append(list, end_point);
347 g_strfreev(parts);
349 return(list);
353 * sipmsg_find_auth_header will return the particular WWW-Authenticate
354 * header specified by *name.
356 * Use this function when you want to look for a specific authentication
357 * method such as NTLM or Kerberos
360 gchar *sipmsg_find_auth_header(struct sipmsg *msg, const gchar *name) {
361 GSList *tmp;
362 struct sipnameval *elem;
363 int name_len = strlen(name);
364 tmp = msg->headers;
365 while(tmp) {
366 elem = tmp->data;
367 //purple_debug(PURPLE_DEBUG_MISC, "sipmsg", "Current header: %s\r\n", elem->value);
368 if (elem && elem->name &&
369 (!g_ascii_strcasecmp(elem->name,"WWW-Authenticate")
370 || !g_ascii_strcasecmp(elem->name,"Authentication-Info")) ) {
371 if (!g_strncasecmp((gchar *)elem->value, name, name_len)) {
372 //purple_debug(PURPLE_DEBUG_MISC, "sipmsg", "elem->value: %s\r\n", elem->value);
373 return elem->value;
376 //purple_debug(PURPLE_DEBUG_MISC, "sipmsg", "moving to next header\r\n");
377 tmp = g_slist_next(tmp);
379 purple_debug(PURPLE_DEBUG_MISC, "sipmsg", "Did not found auth header %s\r\n", name);
380 return NULL;
383 gchar *sipmsg_get_x_mms_im_format(gchar *msgr) {
384 gchar *msgr2;
385 gsize msgr_dec64_len;
386 guchar *msgr_dec64;
387 gchar *msgr_utf8;
388 gchar **lines;
389 gchar **parts;
390 gchar *x_mms_im_format;
391 gchar *tmp;
393 if (!msgr) return NULL;
394 msgr2 = g_strdup(msgr);
395 while (strlen(msgr2) % 4 != 0) {
396 gchar *tmp_msgr2 = msgr2;
397 msgr2 = g_strdup_printf("%s=", msgr2);
398 g_free(tmp_msgr2);
400 msgr_dec64 = purple_base64_decode(msgr2, &msgr_dec64_len);
401 msgr_utf8 = g_convert((gchar *) msgr_dec64, msgr_dec64_len, "UTF-8", "UTF-16LE", NULL, NULL, NULL);
402 g_free(msgr_dec64);
403 g_free(msgr2);
404 lines = g_strsplit(msgr_utf8,"\r\n\r\n",0);
405 g_free(msgr_utf8);
406 //@TODO: make extraction like parsing of message headers.
407 parts = g_strsplit(lines[0],"X-MMS-IM-Format:",0);
408 x_mms_im_format = g_strdup(parts[1]);
409 g_strfreev(parts);
410 g_strfreev(lines);
411 tmp = x_mms_im_format;
412 if (x_mms_im_format) {
413 while(*x_mms_im_format==' ' || *x_mms_im_format=='\t') x_mms_im_format++;
415 x_mms_im_format = g_strdup(x_mms_im_format);
416 g_free(tmp);
417 return x_mms_im_format;
420 gchar *sipmsg_get_msgr_string(gchar *x_mms_im_format) {
421 gchar *msgr_orig;
422 gsize msgr_utf16_len;
423 gchar *msgr_utf16;
424 gchar *msgr_enc;
425 gchar *res;
426 int len;
428 if (!x_mms_im_format) return NULL;
429 msgr_orig = g_strdup_printf("X-MMS-IM-Format: %s\r\n\r\n", x_mms_im_format);
430 msgr_utf16 = g_convert(msgr_orig, -1, "UTF-16LE", "UTF-8", NULL, &msgr_utf16_len, NULL);
431 g_free(msgr_orig);
432 msgr_enc = purple_base64_encode((guchar *) msgr_utf16, msgr_utf16_len);
433 g_free(msgr_utf16);
434 len = strlen(msgr_enc);
435 while (msgr_enc[len - 1] == '=') len--;
436 res = g_strndup(msgr_enc, len);
437 g_free(msgr_enc);
438 return res;
441 gchar *sipmsg_apply_x_mms_im_format(const char *x_mms_im_format, gchar *body) {
442 char *pre, *post;
443 gchar *res;
445 if (!x_mms_im_format) {
446 return body ? g_strdup(body) : NULL;
448 msn_parse_format(x_mms_im_format, &pre, &post);
449 res = g_strdup_printf("%s%s%s", pre ? pre : "", body ? body : "", post ? post : "");
450 g_free(pre);
451 g_free(post);
452 return res;
455 /* ms-text-format: text/plain; charset=UTF-8;msgr=WAAtAE0...DIADQAKAA0ACgA;ms-body=SGk= */
456 gchar *get_html_message(const gchar *ms_text_format_in, const gchar *body_in)
458 gchar *msgr;
459 gchar *res;
460 gchar *ms_text_format = NULL;
461 gchar *body = NULL;
463 if (g_str_has_prefix(ms_text_format_in, "multipart/related") ||
464 g_str_has_prefix(ms_text_format_in, "multipart/alternative")) {
465 char *doc = g_strdup_printf("Content-Type: %s\r\n\r\n%s", ms_text_format_in, body_in);
466 PurpleMimeDocument *mime;
467 GList* parts;
469 mime = purple_mime_document_parse(doc);
470 parts = purple_mime_document_get_parts(mime);
471 while (parts) {
472 const gchar *content_type = purple_mime_part_get_field(parts->data, "Content-Type");
473 if (content_type) {
474 const gchar *content = purple_mime_part_get_data(parts->data);
475 guint length = purple_mime_part_get_length(parts->data);
477 /* if no other format has stored */
478 if (!ms_text_format && g_str_has_prefix(content_type, "text/plain")) {
479 ms_text_format = g_strdup(content_type);
480 body = g_strndup(content, length);
481 /* preferred format */
482 } else if (g_str_has_prefix(ms_text_format, "text/html")) {
483 g_free(ms_text_format);
484 g_free(body);
485 ms_text_format = g_strdup(content_type);
486 body = g_strndup(content, length);
487 break;
490 parts = parts->next;
492 g_free(doc);
493 if (mime)
494 purple_mime_document_free(mime);
495 } else {
496 ms_text_format = g_strdup(ms_text_format_in);
497 body = g_strdup(body_in);
500 if (body) {
501 res = body;
502 } else {
503 gchar *tmp = sipmsg_find_part_of_header(ms_text_format, "ms-body=", NULL, NULL);
504 if (!tmp) {
505 g_free(ms_text_format);
506 return NULL;
508 res = (gchar *) purple_base64_decode(tmp, NULL);
509 g_free(tmp);
510 if (!res) {
511 g_free(ms_text_format);
512 return NULL;
516 if (!g_str_has_prefix(ms_text_format, "text/html")) { // NOT html
517 char *tmp = res;
518 res = g_markup_escape_text(res, -1); // as this is not html
519 g_free(tmp);
522 msgr = sipmsg_find_part_of_header(ms_text_format, "msgr=", ";", NULL);
523 if (msgr) {
524 gchar *x_mms_im_format = sipmsg_get_x_mms_im_format(msgr);
525 gchar *tmp = res;
526 g_free(msgr);
527 res = sipmsg_apply_x_mms_im_format(x_mms_im_format, res);
528 g_free(tmp);
529 g_free(x_mms_im_format);
532 g_free(ms_text_format);
534 return res;
541 //------------------------------------------------------------------------------------------
542 //TEMP solution to include it here (copy from purple's msn protocol
543 //How to reuse msn's util methods from sipe?
545 // from internal.h for linux compilation
546 #ifndef _WIN32
547 #define MSG_LEN 2048
548 #define BUF_LEN MSG_LEN
549 #endif
550 void
551 msn_parse_format(const char *mime, char **pre_ret, char **post_ret)
553 char *cur;
554 GString *pre = g_string_new(NULL);
555 GString *post = g_string_new(NULL);
556 unsigned int colors[3];
558 if (pre_ret != NULL) *pre_ret = NULL;
559 if (post_ret != NULL) *post_ret = NULL;
561 cur = strstr(mime, "FN=");
563 if (cur && (*(cur = cur + 3) != ';'))
565 pre = g_string_append(pre, "<FONT FACE=\"");
567 while (*cur && *cur != ';')
569 pre = g_string_append_c(pre, *cur);
570 cur++;
573 pre = g_string_append(pre, "\">");
574 post = g_string_prepend(post, "</FONT>");
577 cur = strstr(mime, "EF=");
579 if (cur && (*(cur = cur + 3) != ';'))
581 while (*cur && *cur != ';')
583 pre = g_string_append_c(pre, '<');
584 pre = g_string_append_c(pre, *cur);
585 pre = g_string_append_c(pre, '>');
586 post = g_string_prepend_c(post, '>');
587 post = g_string_prepend_c(post, *cur);
588 post = g_string_prepend_c(post, '/');
589 post = g_string_prepend_c(post, '<');
590 cur++;
594 cur = strstr(mime, "CO=");
596 if (cur && (*(cur = cur + 3) != ';'))
598 int i;
600 i = sscanf(cur, "%02x%02x%02x;", &colors[0], &colors[1], &colors[2]);
602 if (i > 0)
604 char tag[64];
606 if (i == 1)
608 colors[1] = 0;
609 colors[2] = 0;
611 else if (i == 2)
613 unsigned int temp = colors[0];
615 colors[0] = colors[1];
616 colors[1] = temp;
617 colors[2] = 0;
619 else if (i == 3)
621 unsigned int temp = colors[2];
623 colors[2] = colors[0];
624 colors[0] = temp;
627 g_snprintf(tag, sizeof(tag),
628 "<FONT COLOR=\"#%02hhx%02hhx%02hhx\">",
629 colors[0], colors[1], colors[2]);
631 pre = g_string_append(pre, tag);
632 post = g_string_prepend(post, "</FONT>");
636 cur = strstr(mime, "RL=");
638 if (cur && (*(cur = cur + 3) != ';'))
640 if (*cur == '1')
642 /* RTL text was received */
643 pre = g_string_append(pre, "<SPAN style=\"direction:rtl;text-align:right;\">");
644 post = g_string_prepend(post, "</SPAN>");
648 cur = g_strdup(purple_url_decode(pre->str));
649 g_string_free(pre, TRUE);
651 if (pre_ret != NULL)
652 *pre_ret = cur;
653 else
654 g_free(cur);
656 cur = g_strdup(purple_url_decode(post->str));
657 g_string_free(post, TRUE);
659 if (post_ret != NULL)
660 *post_ret = cur;
661 else
662 g_free(cur);
665 static const char *
666 encode_spaces(const char *str)
668 static char buf[BUF_LEN];
669 const char *c;
670 char *d;
672 g_return_val_if_fail(str != NULL, NULL);
674 for (c = str, d = buf; *c != '\0'; c++)
676 if (*c == ' ')
678 *d++ = '%';
679 *d++ = '2';
680 *d++ = '0';
682 else
683 *d++ = *c;
685 *d = '\0';
687 return buf;
690 void
691 msn_import_html(const char *html, char **attributes, char **message)
693 int len, retcount = 0;
694 const char *c;
695 char *msg;
696 char *fontface = NULL;
697 char fonteffect[4];
698 char fontcolor[7];
699 char direction = '0';
701 gboolean has_bold = FALSE;
702 gboolean has_italic = FALSE;
703 gboolean has_underline = FALSE;
704 gboolean has_strikethrough = FALSE;
706 g_return_if_fail(html != NULL);
707 g_return_if_fail(attributes != NULL);
708 g_return_if_fail(message != NULL);
710 len = strlen(html);
711 msg = g_malloc0(len + 1);
713 memset(fontcolor, 0, sizeof(fontcolor));
714 strcat(fontcolor, "0");
715 memset(fonteffect, 0, sizeof(fonteffect));
717 for (c = html; *c != '\0';)
719 if (*c == '<')
721 if (!g_ascii_strncasecmp(c + 1, "br>", 3))
723 msg[retcount++] = '\r';
724 msg[retcount++] = '\n';
725 c += 4;
727 else if (!g_ascii_strncasecmp(c + 1, "i>", 2))
729 if (!has_italic)
731 strcat(fonteffect, "I");
732 has_italic = TRUE;
734 c += 3;
736 else if (!g_ascii_strncasecmp(c + 1, "b>", 2))
738 if (!has_bold)
740 strcat(fonteffect, "B");
741 has_bold = TRUE;
743 c += 3;
745 else if (!g_ascii_strncasecmp(c + 1, "u>", 2))
747 if (!has_underline)
749 strcat(fonteffect, "U");
750 has_underline = TRUE;
752 c += 3;
754 else if (!g_ascii_strncasecmp(c + 1, "s>", 2))
756 if (!has_strikethrough)
758 strcat(fonteffect, "S");
759 has_strikethrough = TRUE;
761 c += 3;
763 else if (!g_ascii_strncasecmp(c + 1, "a href=\"", 8))
765 c += 9;
767 if (!g_ascii_strncasecmp(c, "mailto:", 7))
768 c += 7;
770 while ((*c != '\0') && g_ascii_strncasecmp(c, "\">", 2))
771 msg[retcount++] = *c++;
773 if (*c != '\0')
774 c += 2;
776 /* ignore descriptive string */
777 while ((*c != '\0') && g_ascii_strncasecmp(c, "</a>", 4))
778 c++;
780 if (*c != '\0')
781 c += 4;
783 else if (!g_ascii_strncasecmp(c + 1, "span", 4))
785 /* Bi-directional text support using CSS properties in span tags */
786 c += 5;
788 while (*c != '\0' && *c != '>')
790 while (*c == ' ')
791 c++;
792 if (!g_ascii_strncasecmp(c, "dir=\"rtl\"", 9))
794 c += 9;
795 direction = '1';
797 else if (!g_ascii_strncasecmp(c, "style=\"", 7))
799 /* Parse inline CSS attributes */
800 int attr_len = 0;
801 c += 7;
802 while (*(c + attr_len) != '\0' && *(c + attr_len) != '"')
803 attr_len++;
804 if (*(c + attr_len) == '"')
806 char *css_attributes;
807 char *attr_dir;
808 css_attributes = g_strndup(c, attr_len);
809 attr_dir = purple_markup_get_css_property(css_attributes, "direction");
810 g_free(css_attributes);
811 if (attr_dir && (!g_ascii_strncasecmp(attr_dir, "RTL", 3)))
812 direction = '1';
813 g_free(attr_dir);
817 else
819 c++;
822 if (*c == '>')
823 c++;
825 else if (!g_ascii_strncasecmp(c + 1, "font", 4))
827 c += 5;
829 while ((*c != '\0') && !g_ascii_strncasecmp(c, " ", 1))
830 c++;
832 if (!g_ascii_strncasecmp(c, "color=\"#", 7))
834 c += 8;
836 fontcolor[0] = *(c + 4);
837 fontcolor[1] = *(c + 5);
838 fontcolor[2] = *(c + 2);
839 fontcolor[3] = *(c + 3);
840 fontcolor[4] = *c;
841 fontcolor[5] = *(c + 1);
843 c += 8;
845 else if (!g_ascii_strncasecmp(c, "face=\"", 6))
847 const char *end = NULL;
848 const char *comma = NULL;
849 unsigned int namelen = 0;
851 c += 6;
852 end = strchr(c, '\"');
853 comma = strchr(c, ',');
855 if (comma == NULL || comma > end)
856 namelen = (unsigned int)(end - c);
857 else
858 namelen = (unsigned int)(comma - c);
860 g_free(fontface);
861 fontface = g_strndup(c, namelen);
862 c = end + 2;
864 else
866 /* Drop all unrecognized/misparsed font tags */
867 while ((*c != '\0') && g_ascii_strncasecmp(c, "\">", 2))
868 c++;
870 if (*c != '\0')
871 c += 2;
874 else
876 while ((*c != '\0') && (*c != '>'))
877 c++;
878 if (*c != '\0')
879 c++;
882 else if (*c == '&')
884 if (!g_ascii_strncasecmp(c, "&lt;", 4))
886 msg[retcount++] = '<';
887 c += 4;
889 else if (!g_ascii_strncasecmp(c, "&gt;", 4))
891 msg[retcount++] = '>';
892 c += 4;
894 else if (!g_ascii_strncasecmp(c, "&nbsp;", 6))
896 msg[retcount++] = ' ';
897 c += 6;
899 else if (!g_ascii_strncasecmp(c, "&quot;", 6))
901 msg[retcount++] = '"';
902 c += 6;
904 else if (!g_ascii_strncasecmp(c, "&amp;", 5))
906 msg[retcount++] = '&';
907 c += 5;
909 else if (!g_ascii_strncasecmp(c, "&apos;", 6))
911 msg[retcount++] = '\'';
912 c += 6;
914 else
915 msg[retcount++] = *c++;
917 else
918 msg[retcount++] = *c++;
921 if (fontface == NULL)
922 fontface = g_strdup("MS Sans Serif");
924 *attributes = g_strdup_printf("FN=%s; EF=%s; CO=%s; PF=0; RL=%c",
925 encode_spaces(fontface),
926 fonteffect, fontcolor, direction);
927 *message = msg;
929 g_free(fontface);
931 // End of TEMP
934 Local Variables:
935 mode: c
936 c-file-style: "bsd"
937 indent-tabs-mode: t
938 tab-width: 8
939 End: