Fix #3198585: Extra line breaks
[siplcs.git] / src / core / sipmsg.c
blob5c386405f6cc7785d26ce14c3b6a36aae5eae627
1 /**
2 * @file sipmsg.c
4 * pidgin-sipe
6 * Copyright (C) 2010-11 SIPE Project <http://sipe.sourceforge.net/>
7 * Copyright (C) 2008 Novell, Inc.
8 * Copyright (C) 2005 Thomas Butter <butter@uni-mannheim.de>
10 * This program is free software; you can redistribute it and/or modify
11 * it under the terms of the GNU General Public License as published by
12 * the Free Software Foundation; either version 2 of the License, or
13 * (at your option) any later version.
15 * This program is distributed in the hope that it will be useful,
16 * but WITHOUT ANY WARRANTY; without even the implied warranty of
17 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
18 * GNU General Public License for more details.
20 * You should have received a copy of the GNU General Public License
21 * along with this program; if not, write to the Free Software
22 * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
25 #include <stdlib.h>
26 #include <stdio.h>
27 #include <string.h>
28 #include <time.h>
30 #include <glib.h>
32 #include "sipmsg.h"
33 #include "sipe-backend.h"
34 #include "sipe-mime.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->responsestr = 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 SIPE_DEBUG_FATAL_NOFORMAT("sipmsg_parse_header(): Content-Length header not found");
91 if(msg->response) {
92 const gchar *tmp;
93 tmp = sipmsg_find_header(msg, "CSeq");
94 if(!tmp) {
95 /* SHOULD NOT HAPPEN */
96 msg->method = 0;
97 } else {
98 parts = g_strsplit(tmp, " ", 2);
99 msg->method = g_strdup(parts[1]);
100 g_strfreev(parts);
103 return msg;
106 struct sipmsg *sipmsg_copy(const struct sipmsg *other) {
107 struct sipmsg *msg = g_new0(struct sipmsg, 1);
108 GSList *list;
110 msg->response = other->response;
111 msg->responsestr = g_strdup(other->responsestr);
112 msg->method = g_strdup(other->method);
113 msg->target = g_strdup(other->target);
115 list = other->headers;
116 while(list) {
117 struct sipnameval *elem = list->data;
118 sipmsg_add_header_now(msg, elem->name, elem->value);
119 list = list->next;
122 list = other->new_headers;
123 while(list) {
124 struct sipnameval *elem = list->data;
125 sipmsg_add_header(msg, elem->name, elem->value);
126 list = list->next;
129 msg->bodylen = other->bodylen;
130 msg->body = g_strdup(other->body);
131 msg->signature = g_strdup(other->signature);
132 msg->rand = g_strdup(other->rand);
133 msg->num = g_strdup(other->num);
135 return msg;
138 char *sipmsg_to_string(const struct sipmsg *msg) {
139 GSList *cur;
140 GString *outstr = g_string_new("");
141 struct sipnameval *elem;
143 if(msg->response)
144 g_string_append_printf(outstr, "SIP/2.0 %d Unknown\r\n",
145 msg->response);
146 else
147 g_string_append_printf(outstr, "%s %s SIP/2.0\r\n",
148 msg->method, msg->target);
150 cur = msg->headers;
151 while(cur) {
152 elem = cur->data;
153 /*Todo: remove the LFCR in a good way*/
154 /*if(sipe_strequal(elem->name,"Proxy-Authorization"))
155 g_string_append_printf(outstr, "%s: %s", elem->name,
156 elem->value);
157 else */
158 g_string_append_printf(outstr, "%s: %s\r\n", elem->name,
159 elem->value);
160 cur = g_slist_next(cur);
163 g_string_append_printf(outstr, "\r\n%s", msg->bodylen ? msg->body : "");
165 return g_string_free(outstr, FALSE);
169 * Adds header to current message headers at specified position
171 void sipmsg_add_header_now_pos(struct sipmsg *msg, const gchar *name, const gchar *value, int pos) {
172 struct sipnameval *element = g_new0(struct sipnameval,1);
174 /* SANITY CHECK: the calling code must be fixed if this happens! */
175 if (!value) {
176 SIPE_DEBUG_ERROR("sipmsg_add_header_now_pos: NULL value for %s (%d)",
177 name, pos);
178 value = "";
181 element->name = g_strdup(name);
182 element->value = g_strdup(value);
183 msg->headers = g_slist_insert(msg->headers, element,pos);
187 * Adds header to current message headers
189 void sipmsg_add_header_now(struct sipmsg *msg, const gchar *name, const gchar *value) {
190 struct sipnameval *element = g_new0(struct sipnameval,1);
192 /* SANITY CHECK: the calling code must be fixed if this happens! */
193 if (!value) {
194 SIPE_DEBUG_ERROR("sipmsg_add_header_now: NULL value for %s",
195 name);
196 value = "";
199 element->name = g_strdup(name);
200 element->value = g_strdup(value);
201 msg->headers = g_slist_append(msg->headers, element);
205 * Adds header to separate storage for future merge
207 void sipmsg_add_header(struct sipmsg *msg, const gchar *name, const gchar *value) {
208 struct sipnameval *element = g_new0(struct sipnameval,1);
210 /* SANITY CHECK: the calling code must be fixed if this happens! */
211 if (!value) {
212 SIPE_DEBUG_ERROR("sipmsg_add_header: NULL value for %s", name);
213 value = "";
216 element->name = g_strdup(name);
217 element->value = g_strdup(value);
218 msg->new_headers = g_slist_append(msg->new_headers, element);
222 * Removes header if it's not in keepers array
224 void sipmsg_strip_headers(struct sipmsg *msg, const gchar *keepers[]) {
225 GSList *entry;
226 struct sipnameval *elem;
228 entry = msg->headers;
229 while(entry) {
230 int i = 0;
231 gboolean keeper = FALSE;
233 elem = entry->data;
234 while (keepers[i]) {
235 if (!g_strcasecmp(elem->name, keepers[i])) {
236 keeper = TRUE;
237 break;
239 i++;
242 if (!keeper) {
243 GSList *to_delete = entry;
244 SIPE_DEBUG_INFO("sipmsg_strip_headers: removing %s", elem->name);
245 entry = g_slist_next(entry);
246 msg->headers = g_slist_delete_link(msg->headers, to_delete);
247 g_free(elem->name);
248 g_free(elem->value);
249 g_free(elem);
250 } else {
251 entry = g_slist_next(entry);
257 * Merges newly added headers to message
259 void sipmsg_merge_new_headers(struct sipmsg *msg) {
260 while(msg->new_headers) {
261 msg->headers = g_slist_append(msg->headers, msg->new_headers->data);
262 msg->new_headers = g_slist_remove(msg->new_headers, msg->new_headers->data);
266 void sipmsg_free(struct sipmsg *msg) {
267 sipe_utils_nameval_free(msg->headers);
268 sipe_utils_nameval_free(msg->new_headers);
269 g_free(msg->signature);
270 g_free(msg->rand);
271 g_free(msg->num);
272 g_free(msg->responsestr);
273 g_free(msg->method);
274 g_free(msg->target);
275 g_free(msg->body);
276 g_free(msg);
279 void sipmsg_remove_header_now(struct sipmsg *msg, const gchar *name) {
280 struct sipnameval *elem;
281 GSList *tmp = msg->headers;
282 while(tmp) {
283 elem = tmp->data;
284 // OCS2005 can send the same header in either all caps or mixed case
285 if (sipe_strcase_equal(elem->name, name)) {
286 msg->headers = g_slist_remove(msg->headers, elem);
287 g_free(elem->name);
288 g_free(elem->value);
289 g_free(elem);
290 return;
292 tmp = g_slist_next(tmp);
294 return;
297 const gchar *sipmsg_find_header(const struct sipmsg *msg, const gchar *name) {
298 return sipe_utils_nameval_find_instance (msg->headers, name, 0);
301 const gchar *sipmsg_find_header_instance(const struct sipmsg *msg, const gchar *name, int which) {
302 return sipe_utils_nameval_find_instance(msg->headers, name, which);
305 gchar *sipmsg_find_part_of_header(const char *hdr, const char * before, const char * after, const char * def) {
306 const char *tmp;
307 const char *tmp2;
308 gchar *res2;
309 if (!hdr) {
310 return NULL;
313 //printf("partof %s w/ %s before and %s after\n", hdr, before, after);
315 tmp = before == NULL ? hdr : strstr(hdr, before);
316 if (!tmp) {
317 //printf ("not found, returning null\n");
318 return (gchar *)def;
321 if (before != NULL) {
322 tmp += strlen(before);
323 //printf ("tmp now %s\n", tmp);
326 if (after != NULL && (tmp2 = strstr(tmp, after))) {
327 gchar * res = g_strndup(tmp, tmp2 - tmp);
328 //printf("returning %s\n", res);
329 return res;
331 res2 = g_strdup(tmp);
332 //printf("returning %s\n", res2);
333 return res2;
336 int sipmsg_parse_cseq(struct sipmsg *msg)
338 int res = -1;
339 gchar **items;
340 items = g_strsplit(sipmsg_find_header(msg, "CSeq"), " ", 1);
341 if (items[0]) {
342 res = atoi(items[0]);
344 g_strfreev(items);
345 return res;
349 * Parse EndPoints header from INVITE request
350 * Returns a list of end points: contact URI plus optional epid.
351 * You must free the values and the list.
353 * Example headers:
354 * EndPoints: "alice alisson" <sip:alice@atlanta.local>, <sip:bob@atlanta.local>;epid=ebca82d94d, <sip:carol@atlanta.local>
355 * EndPoints: "alice, alisson" <sip:alice@atlanta.local>, <sip:bob@atlanta.local>
356 * EndPoints: "alice alisson" <sip:alice@atlanta.local>, "Super, Man" <sip:super@atlanta.local>
358 * @param header (in) EndPoints header contents
360 * @return GSList with struct sipendpoint as elements
362 GSList *sipmsg_parse_endpoints_header(const gchar *header)
364 GSList *list = NULL;
365 gchar **parts = g_strsplit(header, ",", 0);
366 gchar *part;
367 int i;
369 for (i = 0; (part = parts[i]) != NULL; i++) {
370 /* Does the part contain a URI? */
371 gchar *contact = sipmsg_find_part_of_header(part, "<", ">", NULL);
372 if (contact) {
373 struct sipendpoint *end_point = g_new(struct sipendpoint, 1);
374 end_point->contact = contact;
375 end_point->epid = sipmsg_find_part_of_header(part, "epid=", NULL, NULL);
376 list = g_slist_append(list, end_point);
379 g_strfreev(parts);
381 return(list);
384 void sipmsg_parse_p_asserted_identity(const gchar *header, gchar **sip_uri,
385 gchar **tel_uri) {
386 gchar **parts, **p;
388 *sip_uri = NULL;
389 *tel_uri = NULL;
391 if (g_ascii_strncasecmp(header, "tel:", 4) == 0) {
392 *tel_uri = g_strdup(header);
393 return;
396 parts = g_strsplit(header, ",", 0);
398 for (p = parts; *p; p++) {
399 gchar *uri = sipmsg_find_part_of_header(*p, "<", ">", NULL);
400 if (!uri)
401 continue;
403 if (g_ascii_strncasecmp(uri, "sip:", 4) == 0) {
404 if (*sip_uri) {
405 SIPE_DEBUG_WARNING_NOFORMAT("More than one "
406 "sip: URI found in P-Asserted-Identity!");
407 } else {
408 *sip_uri = uri;
409 uri = NULL;
411 } else if (g_ascii_strncasecmp(uri, "tel:", 4) == 0){
412 if (*tel_uri) {
413 SIPE_DEBUG_WARNING_NOFORMAT("More than one "
414 "tel: URI found in P-Asserted-Identity!");
415 } else {
416 *tel_uri = uri;
417 uri = NULL;
421 g_free(uri);
424 g_strfreev(parts);
428 * sipmsg_find_auth_header will return the particular WWW-Authenticate
429 * header specified by *name.
431 * Use this function when you want to look for a specific authentication
432 * method such as NTLM or Kerberos
435 gchar *sipmsg_find_auth_header(struct sipmsg *msg, const gchar *name) {
436 GSList *tmp;
437 struct sipnameval *elem;
438 int name_len = strlen(name);
439 tmp = msg->headers;
440 while(tmp) {
441 elem = tmp->data;
442 /* SIPE_DEBUG_INFO("Current header: %s", elem->value); */
443 if (elem && elem->name &&
444 (sipe_strcase_equal(elem->name,"WWW-Authenticate") ||
445 sipe_strcase_equal(elem->name,"Authentication-Info")) ) {
446 if (!g_strncasecmp((gchar *)elem->value, name, name_len)) {
447 /* SIPE_DEBUG_INFO("elem->value: %s", elem->value); */
448 return elem->value;
451 /* SIPE_DEBUG_INFO_NOFORMAT("moving to next header"); */
452 tmp = g_slist_next(tmp);
454 SIPE_DEBUG_INFO("auth header '%s' not found.", name);
455 return NULL;
459 * Parses headers-like 'msgr' attribute of INVITE's 'ms_text_format' header.
460 * Then retrieves value of 'X-MMS-IM-Format'.
462 * 'msgr' typically looks like:
463 * X-MMS-IM-Format: FN=Microsoft%20Sans%20Serif; EF=BI; CO=800000; CS=0; PF=22
465 static gchar *sipmsg_get_x_mms_im_format(gchar *msgr) {
466 gchar *msgr2;
467 gsize msgr_dec64_len;
468 guchar *msgr_dec64;
469 gchar *msgr_utf8;
470 gchar **lines;
471 gchar **parts;
472 gchar *x_mms_im_format;
473 gchar *tmp;
475 if (!msgr) return NULL;
476 msgr2 = g_strdup(msgr);
477 while (strlen(msgr2) % 4 != 0) {
478 gchar *tmp_msgr2 = msgr2;
479 msgr2 = g_strdup_printf("%s=", msgr2);
480 g_free(tmp_msgr2);
482 msgr_dec64 = g_base64_decode(msgr2, &msgr_dec64_len);
483 msgr_utf8 = g_convert((gchar *) msgr_dec64, msgr_dec64_len, "UTF-8", "UTF-16LE", NULL, NULL, NULL);
484 g_free(msgr_dec64);
485 g_free(msgr2);
486 lines = g_strsplit(msgr_utf8,"\r\n\r\n",0);
487 g_free(msgr_utf8);
488 //@TODO: make extraction like parsing of message headers.
489 parts = g_strsplit(lines[0],"X-MMS-IM-Format:",0);
490 x_mms_im_format = g_strdup(parts[1]);
491 g_strfreev(parts);
492 g_strfreev(lines);
493 tmp = x_mms_im_format;
494 if (x_mms_im_format) {
495 while(*x_mms_im_format==' ' || *x_mms_im_format=='\t') x_mms_im_format++;
497 x_mms_im_format = g_strdup(x_mms_im_format);
498 g_free(tmp);
499 return x_mms_im_format;
502 gchar *sipmsg_get_msgr_string(gchar *x_mms_im_format) {
503 gchar *msgr_orig;
504 gsize msgr_utf16_len;
505 gchar *msgr_utf16;
506 gchar *msgr_enc;
507 gchar *res;
508 int len;
510 if (!x_mms_im_format) return NULL;
511 msgr_orig = g_strdup_printf("X-MMS-IM-Format: %s\r\n\r\n", x_mms_im_format);
512 msgr_utf16 = g_convert(msgr_orig, -1, "UTF-16LE", "UTF-8", NULL, &msgr_utf16_len, NULL);
513 g_free(msgr_orig);
514 msgr_enc = g_base64_encode((guchar *) msgr_utf16, msgr_utf16_len);
515 g_free(msgr_utf16);
516 len = strlen(msgr_enc);
517 while (msgr_enc[len - 1] == '=') len--;
518 res = g_strndup(msgr_enc, len);
519 g_free(msgr_enc);
520 return res;
523 static void msn_parse_format(const char *mime, char **pre_ret, char **post_ret);
526 * Translates X-MMS-IM format to HTML presentation.
528 static gchar *sipmsg_apply_x_mms_im_format(const char *x_mms_im_format, gchar *body) {
529 char *pre, *post;
530 gchar *res;
532 if (!x_mms_im_format) {
533 return body ? g_strdup(body) : NULL;
535 msn_parse_format(x_mms_im_format, &pre, &post);
536 res = g_strdup_printf("%s%s%s", pre ? pre : "", body ? body : "", post ? post : "");
537 g_free(pre);
538 g_free(post);
539 return res;
542 struct html_message_data {
543 gchar *ms_text_format;
544 gchar *body;
545 gboolean preferred;
548 static void get_html_message_mime_cb(gpointer user_data,
549 const GSList *fields,
550 const gchar *body,
551 gsize length)
553 const gchar *type = sipe_utils_nameval_find(fields, "Content-Type");
554 struct html_message_data *data = user_data;
556 if (!data->preferred) {
557 gboolean copy = FALSE;
559 /* preferred format */
560 if (g_str_has_prefix(type, "text/html")) {
561 copy = TRUE;
562 data->preferred = TRUE;
564 /* fallback format */
565 } else if (g_str_has_prefix(type, "text/plain")) {
566 copy = TRUE;
569 if (copy) {
570 g_free(data->ms_text_format);
571 g_free(data->body);
572 data->ms_text_format = g_strdup(type);
573 data->body = g_strndup(body, length);
578 /* ms-text-format: text/plain; charset=UTF-8;msgr=WAAtAE0...DIADQAKAA0ACgA;ms-body=SGk= */
579 gchar *get_html_message(const gchar *ms_text_format_in, const gchar *body_in)
581 gchar *msgr;
582 gchar *res;
583 gchar *ms_text_format = NULL;
584 gchar *body = NULL;
586 if (g_str_has_prefix(ms_text_format_in, "multipart/related") ||
587 g_str_has_prefix(ms_text_format_in, "multipart/alternative")) {
588 struct html_message_data data = { NULL, NULL, FALSE };
590 sipe_mime_parts_foreach(ms_text_format_in, body_in,
591 get_html_message_mime_cb, &data);
593 ms_text_format = data.ms_text_format;
594 body = data.body;
596 } else {
597 ms_text_format = g_strdup(ms_text_format_in);
598 body = g_strdup(body_in);
601 if (body) {
602 res = body;
603 } else {
604 gchar *tmp = sipmsg_find_part_of_header(ms_text_format, "ms-body=", NULL, NULL);
605 gsize len;
606 if (!tmp) {
607 g_free(ms_text_format);
608 return NULL;
610 res = (gchar *) g_base64_decode(tmp, &len);
611 g_free(tmp);
612 if (!res) {
613 g_free(ms_text_format);
614 return NULL;
618 if (g_str_has_prefix(ms_text_format, "text/html")) {
620 * HTML uses tags for formatting, not line breaks. But
621 * clients still might render them, so we need to remove
622 * them to avoid incorrect text rendering.
624 gchar *d = res;
625 const gchar *s = res;
626 gchar c;
628 /* No ANSI C nor glib function seems to exist for this :-( */
629 while ((c = *s++))
630 if ((c != '\n') && (c != '\r'))
631 *d++ = c;
632 *d = c;
634 } else {
635 char *tmp = res;
636 res = g_markup_escape_text(res, -1); // as this is not html
637 g_free(tmp);
640 msgr = sipmsg_find_part_of_header(ms_text_format, "msgr=", ";", NULL);
641 if (msgr) {
642 gchar *x_mms_im_format = sipmsg_get_x_mms_im_format(msgr);
643 gchar *tmp = res;
644 g_free(msgr);
645 res = sipmsg_apply_x_mms_im_format(x_mms_im_format, res);
646 g_free(tmp);
647 g_free(x_mms_im_format);
650 g_free(ms_text_format);
652 return res;
659 //------------------------------------------------------------------------------------------
660 //TEMP solution to include it here (copy from purple's msn protocol
661 //How to reuse msn's util methods from sipe?
663 /* from internal.h */
664 #define MSG_LEN 2048
665 #define BUF_LEN MSG_LEN
667 static
668 gchar *sipmsg_uri_unescape(const gchar *string)
670 gchar *unescaped, *tmp;
672 if (!string) return(NULL);
674 #if GLIB_CHECK_VERSION(2,16,0)
675 unescaped = g_uri_unescape_string(string, NULL);
676 #else
677 /* loosely based on libpurple/util.c:purple_url_decode() */
679 gsize i = 0;
680 gsize len = strlen(string);
682 unescaped = g_malloc(len + 1);
683 while (len-- > 0) {
684 gchar c = *string++;
685 if ((len >= 2) && (c == '%')) {
686 char hex[3];
687 strncpy(hex, string, 2);
688 hex[2] = '\0';
689 c = strtol(hex, NULL, 16);
690 string += 2;
691 len -= 2;
693 unescaped[i++] = c;
695 unescaped[i] = '\0';
697 #endif
699 if (!g_utf8_validate(unescaped, -1, (const gchar **)&tmp))
700 *tmp = '\0';
702 return(unescaped);
705 void
706 msn_parse_format(const char *mime, char **pre_ret, char **post_ret)
708 char *cur;
709 GString *pre = g_string_new(NULL);
710 GString *post = g_string_new(NULL);
711 unsigned int colors[3];
713 if (pre_ret != NULL) *pre_ret = NULL;
714 if (post_ret != NULL) *post_ret = NULL;
716 cur = strstr(mime, "FN=");
718 if (cur && (*(cur = cur + 3) != ';'))
720 pre = g_string_append(pre, "<FONT FACE=\"");
722 while (*cur && *cur != ';')
724 pre = g_string_append_c(pre, *cur);
725 cur++;
728 pre = g_string_append(pre, "\">");
729 post = g_string_prepend(post, "</FONT>");
732 cur = strstr(mime, "EF=");
734 if (cur && (*(cur = cur + 3) != ';'))
736 while (*cur && *cur != ';')
738 pre = g_string_append_c(pre, '<');
739 pre = g_string_append_c(pre, *cur);
740 pre = g_string_append_c(pre, '>');
741 post = g_string_prepend_c(post, '>');
742 post = g_string_prepend_c(post, *cur);
743 post = g_string_prepend_c(post, '/');
744 post = g_string_prepend_c(post, '<');
745 cur++;
749 cur = strstr(mime, "CO=");
751 if (cur && (*(cur = cur + 3) != ';'))
753 int i;
755 i = sscanf(cur, "%02x%02x%02x;", &colors[0], &colors[1], &colors[2]);
757 if (i > 0)
759 char tag[64];
761 if (i == 1)
763 colors[1] = 0;
764 colors[2] = 0;
766 else if (i == 2)
768 unsigned int temp = colors[0];
770 colors[0] = colors[1];
771 colors[1] = temp;
772 colors[2] = 0;
774 else if (i == 3)
776 unsigned int temp = colors[2];
778 colors[2] = colors[0];
779 colors[0] = temp;
782 /* hh is undefined in mingw's gcc 4.4
783 * https://sourceforge.net/tracker/index.php?func=detail&aid=2818436&group_id=2435&atid=102435
785 g_snprintf(tag, sizeof(tag),
786 "<FONT COLOR=\"#%02x%02x%02x\">",
787 (unsigned char)colors[0], (unsigned char)colors[1], (unsigned char)colors[2]);
789 pre = g_string_append(pre, tag);
790 post = g_string_prepend(post, "</FONT>");
794 cur = strstr(mime, "RL=");
796 if (cur && (*(cur = cur + 3) != ';'))
798 if (*cur == '1')
800 /* RTL text was received */
801 pre = g_string_append(pre, "<SPAN style=\"direction:rtl;text-align:right;\">");
802 post = g_string_prepend(post, "</SPAN>");
806 cur = sipmsg_uri_unescape(pre->str);
807 g_string_free(pre, TRUE);
809 if (pre_ret != NULL)
810 *pre_ret = cur;
811 else
812 g_free(cur);
814 cur = sipmsg_uri_unescape(post->str);
815 g_string_free(post, TRUE);
817 if (post_ret != NULL)
818 *post_ret = cur;
819 else
820 g_free(cur);
823 static const char *
824 encode_spaces(const char *str)
826 static char buf[BUF_LEN];
827 const char *c;
828 char *d;
830 g_return_val_if_fail(str != NULL, NULL);
832 for (c = str, d = buf; *c != '\0'; c++)
834 if (*c == ' ')
836 *d++ = '%';
837 *d++ = '2';
838 *d++ = '0';
840 else
841 *d++ = *c;
843 *d = '\0';
845 return buf;
848 void
849 sipe_parse_html(const char *html, char **attributes, char **message)
851 int len, retcount = 0;
852 const char *c;
853 char *msg;
854 char *fontface = NULL;
855 char fonteffect[4];
856 char fontcolor[7];
857 char direction = '0';
859 gboolean has_bold = FALSE;
860 gboolean has_italic = FALSE;
861 gboolean has_underline = FALSE;
862 gboolean has_strikethrough = FALSE;
864 g_return_if_fail(html != NULL);
865 g_return_if_fail(attributes != NULL);
866 g_return_if_fail(message != NULL);
868 len = strlen(html);
869 msg = g_malloc0(len + 1);
871 memset(fontcolor, 0, sizeof(fontcolor));
872 strcat(fontcolor, "0");
873 memset(fonteffect, 0, sizeof(fonteffect));
875 for (c = html; *c != '\0';)
877 if (*c == '<')
879 if (!g_ascii_strncasecmp(c + 1, "br>", 3))
881 msg[retcount++] = '\r';
882 msg[retcount++] = '\n';
883 c += 4;
885 else if (!g_ascii_strncasecmp(c + 1, "i>", 2))
887 if (!has_italic)
889 strcat(fonteffect, "I");
890 has_italic = TRUE;
892 c += 3;
894 else if (!g_ascii_strncasecmp(c + 1, "b>", 2))
896 if (!has_bold)
898 strcat(fonteffect, "B");
899 has_bold = TRUE;
901 c += 3;
903 else if (!g_ascii_strncasecmp(c + 1, "u>", 2))
905 if (!has_underline)
907 strcat(fonteffect, "U");
908 has_underline = TRUE;
910 c += 3;
912 else if (!g_ascii_strncasecmp(c + 1, "s>", 2))
914 if (!has_strikethrough)
916 strcat(fonteffect, "S");
917 has_strikethrough = TRUE;
919 c += 3;
921 else if (!g_ascii_strncasecmp(c + 1, "a href=\"", 8))
923 c += 9;
925 if (!g_ascii_strncasecmp(c, "mailto:", 7))
926 c += 7;
928 while ((*c != '\0') && g_ascii_strncasecmp(c, "\">", 2))
929 msg[retcount++] = *c++;
931 if (*c != '\0')
932 c += 2;
934 /* ignore descriptive string */
935 while ((*c != '\0') && g_ascii_strncasecmp(c, "</a>", 4))
936 c++;
938 if (*c != '\0')
939 c += 4;
941 else if (!g_ascii_strncasecmp(c + 1, "span", 4))
943 /* Bi-directional text support using CSS properties in span tags */
944 c += 5;
946 while (*c != '\0' && *c != '>')
948 while (*c == ' ')
949 c++;
950 if (!g_ascii_strncasecmp(c, "dir=\"rtl\"", 9))
952 c += 9;
953 direction = '1';
955 else if (!g_ascii_strncasecmp(c, "style=\"", 7))
957 /* Parse inline CSS attributes */
958 int attr_len = 0;
959 c += 7;
960 while (*(c + attr_len) != '\0' && *(c + attr_len) != '"')
961 attr_len++;
962 if (*(c + attr_len) == '"')
964 char *css_attributes;
965 char *attr_dir;
966 css_attributes = g_strndup(c, attr_len);
967 attr_dir = sipe_backend_markup_css_property(css_attributes, "direction");
968 g_free(css_attributes);
969 if (attr_dir && (!g_ascii_strncasecmp(attr_dir, "RTL", 3)))
970 direction = '1';
971 g_free(attr_dir);
975 else
977 c++;
980 if (*c == '>')
981 c++;
983 else if (!g_ascii_strncasecmp(c + 1, "font", 4))
985 c += 5;
987 while ((*c != '\0') && !g_ascii_strncasecmp(c, " ", 1))
988 c++;
990 if (!g_ascii_strncasecmp(c, "color=\"#", 7))
992 c += 8;
994 fontcolor[0] = *(c + 4);
995 fontcolor[1] = *(c + 5);
996 fontcolor[2] = *(c + 2);
997 fontcolor[3] = *(c + 3);
998 fontcolor[4] = *c;
999 fontcolor[5] = *(c + 1);
1001 c += 8;
1003 else if (!g_ascii_strncasecmp(c, "face=\"", 6))
1005 const char *end = NULL;
1006 const char *comma = NULL;
1007 unsigned int namelen = 0;
1009 c += 6;
1010 end = strchr(c, '\"');
1011 comma = strchr(c, ',');
1013 if (comma == NULL || comma > end)
1014 namelen = (unsigned int)(end - c);
1015 else
1016 namelen = (unsigned int)(comma - c);
1018 g_free(fontface);
1019 fontface = g_strndup(c, namelen);
1020 c = end + 2;
1022 else
1024 /* Drop all unrecognized/misparsed font tags */
1025 while ((*c != '\0') && g_ascii_strncasecmp(c, "\">", 2))
1026 c++;
1028 if (*c != '\0')
1029 c += 2;
1032 else
1034 while ((*c != '\0') && (*c != '>'))
1035 c++;
1036 if (*c != '\0')
1037 c++;
1040 else if (*c == '&')
1042 if (!g_ascii_strncasecmp(c, "&lt;", 4))
1044 msg[retcount++] = '<';
1045 c += 4;
1047 else if (!g_ascii_strncasecmp(c, "&gt;", 4))
1049 msg[retcount++] = '>';
1050 c += 4;
1052 else if (!g_ascii_strncasecmp(c, "&nbsp;", 6))
1054 msg[retcount++] = ' ';
1055 c += 6;
1057 else if (!g_ascii_strncasecmp(c, "&quot;", 6))
1059 msg[retcount++] = '"';
1060 c += 6;
1062 else if (!g_ascii_strncasecmp(c, "&amp;", 5))
1064 msg[retcount++] = '&';
1065 c += 5;
1067 else if (!g_ascii_strncasecmp(c, "&apos;", 6))
1069 msg[retcount++] = '\'';
1070 c += 6;
1072 else
1073 msg[retcount++] = *c++;
1075 else
1076 msg[retcount++] = *c++;
1079 if (fontface == NULL)
1080 fontface = g_strdup("MS Sans Serif");
1082 *attributes = g_strdup_printf("FN=%s; EF=%s; CO=%s; PF=0; RL=%c",
1083 encode_spaces(fontface),
1084 fonteffect, fontcolor, direction);
1085 *message = msg;
1087 g_free(fontface);
1089 // End of TEMP
1092 Local Variables:
1093 mode: c
1094 c-file-style: "bsd"
1095 indent-tabs-mode: t
1096 tab-width: 8
1097 End: