Fix #237: HTML escaping not removed from URL
[siplcs.git] / src / core / sipmsg.c
blob316f8021896ca424dad9ffea1c4faea339465c98
1 /**
2 * @file sipmsg.c
4 * pidgin-sipe
6 * Copyright (C) 2010-2014 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 const gchar *tmp = sipmsg_find_header(msg, "Transfer-Encoding");
90 if (tmp && sipe_strcase_equal(tmp, "chunked")) {
91 msg->bodylen = SIPMSG_BODYLEN_CHUNKED;
92 } else {
93 tmp = sipmsg_find_header(msg, "Content-Type");
94 if (tmp)
95 SIPE_DEBUG_FATAL_NOFORMAT("sipmsg_parse_header(): Content-Length header not found");
96 else
97 msg->bodylen = 0;
100 if(msg->response) {
101 const gchar *tmp;
102 tmp = sipmsg_find_header(msg, "CSeq");
103 if(!tmp) {
104 /* SHOULD NOT HAPPEN */
105 msg->method = 0;
106 } else {
107 parts = g_strsplit(tmp, " ", 2);
108 msg->method = g_strdup(parts[1]);
109 g_strfreev(parts);
112 return msg;
115 struct sipmsg *sipmsg_copy(const struct sipmsg *other) {
116 struct sipmsg *msg = g_new0(struct sipmsg, 1);
117 GSList *list;
119 msg->response = other->response;
120 msg->responsestr = g_strdup(other->responsestr);
121 msg->method = g_strdup(other->method);
122 msg->target = g_strdup(other->target);
124 list = other->headers;
125 while(list) {
126 struct sipnameval *elem = list->data;
127 sipmsg_add_header_now(msg, elem->name, elem->value);
128 list = list->next;
131 list = other->new_headers;
132 while(list) {
133 struct sipnameval *elem = list->data;
134 sipmsg_add_header(msg, elem->name, elem->value);
135 list = list->next;
138 msg->bodylen = other->bodylen;
139 msg->body = g_strdup(other->body);
140 msg->signature = g_strdup(other->signature);
141 msg->rand = g_strdup(other->rand);
142 msg->num = g_strdup(other->num);
144 return msg;
147 char *sipmsg_to_string(const struct sipmsg *msg) {
148 GSList *cur;
149 GString *outstr = g_string_new("");
150 struct sipnameval *elem;
152 if(msg->response)
153 g_string_append_printf(outstr, "SIP/2.0 %d Unknown\r\n",
154 msg->response);
155 else
156 g_string_append_printf(outstr, "%s %s SIP/2.0\r\n",
157 msg->method, msg->target);
159 cur = msg->headers;
160 while(cur) {
161 elem = cur->data;
162 /*Todo: remove the LFCR in a good way*/
163 /*if(sipe_strequal(elem->name,"Proxy-Authorization"))
164 g_string_append_printf(outstr, "%s: %s", elem->name,
165 elem->value);
166 else */
167 g_string_append_printf(outstr, "%s: %s\r\n", elem->name,
168 elem->value);
169 cur = g_slist_next(cur);
172 g_string_append_printf(outstr, "\r\n%s", msg->bodylen ? msg->body : "");
174 return g_string_free(outstr, FALSE);
178 * Adds header to current message headers
180 void sipmsg_add_header_now(struct sipmsg *msg, const gchar *name, const gchar *value) {
181 struct sipnameval *element = g_new0(struct sipnameval,1);
183 /* SANITY CHECK: the calling code must be fixed if this happens! */
184 if (!value) {
185 SIPE_DEBUG_ERROR("sipmsg_add_header_now: NULL value for %s",
186 name);
187 value = "";
190 element->name = g_strdup(name);
191 element->value = g_strdup(value);
192 msg->headers = g_slist_append(msg->headers, element);
196 * Adds header to separate storage for future merge
198 void sipmsg_add_header(struct sipmsg *msg, const gchar *name, const gchar *value) {
199 struct sipnameval *element = g_new0(struct sipnameval,1);
201 /* SANITY CHECK: the calling code must be fixed if this happens! */
202 if (!value) {
203 SIPE_DEBUG_ERROR("sipmsg_add_header: NULL value for %s", name);
204 value = "";
207 element->name = g_strdup(name);
208 element->value = g_strdup(value);
209 msg->new_headers = g_slist_append(msg->new_headers, element);
213 * Removes header if it's not in keepers array
215 void sipmsg_strip_headers(struct sipmsg *msg, const gchar *keepers[]) {
216 GSList *entry;
217 struct sipnameval *elem;
219 entry = msg->headers;
220 while(entry) {
221 int i = 0;
222 gboolean keeper = FALSE;
224 elem = entry->data;
225 while (keepers[i]) {
226 if (!g_ascii_strcasecmp(elem->name, keepers[i])) {
227 keeper = TRUE;
228 break;
230 i++;
233 if (!keeper) {
234 GSList *to_delete = entry;
235 SIPE_DEBUG_INFO("sipmsg_strip_headers: removing %s", elem->name);
236 entry = g_slist_next(entry);
237 msg->headers = g_slist_delete_link(msg->headers, to_delete);
238 g_free(elem->name);
239 g_free(elem->value);
240 g_free(elem);
241 } else {
242 entry = g_slist_next(entry);
248 * Merges newly added headers to message
250 void sipmsg_merge_new_headers(struct sipmsg *msg) {
251 while(msg->new_headers) {
252 msg->headers = g_slist_append(msg->headers, msg->new_headers->data);
253 msg->new_headers = g_slist_remove(msg->new_headers, msg->new_headers->data);
257 void sipmsg_free(struct sipmsg *msg) {
258 if (msg) {
259 sipe_utils_nameval_free(msg->headers);
260 sipe_utils_nameval_free(msg->new_headers);
261 g_free(msg->signature);
262 g_free(msg->rand);
263 g_free(msg->num);
264 g_free(msg->responsestr);
265 g_free(msg->method);
266 g_free(msg->target);
267 g_free(msg->body);
268 g_free(msg);
272 void sipmsg_remove_header_now(struct sipmsg *msg, const gchar *name) {
273 struct sipnameval *elem;
274 GSList *tmp = msg->headers;
275 while(tmp) {
276 elem = tmp->data;
277 // OCS2005 can send the same header in either all caps or mixed case
278 if (sipe_strcase_equal(elem->name, name)) {
279 msg->headers = g_slist_remove(msg->headers, elem);
280 g_free(elem->name);
281 g_free(elem->value);
282 g_free(elem);
283 return;
285 tmp = g_slist_next(tmp);
287 return;
290 const gchar *sipmsg_find_header(const struct sipmsg *msg, const gchar *name) {
291 return sipe_utils_nameval_find_instance (msg->headers, name, 0);
294 const gchar *sipmsg_find_header_instance(const struct sipmsg *msg, const gchar *name, int which) {
295 return sipe_utils_nameval_find_instance(msg->headers, name, which);
298 gchar *sipmsg_find_part_of_header(const char *hdr, const char * before, const char * after, const char * def) {
299 const char *tmp;
300 const char *tmp2;
301 gchar *res2;
302 if (!hdr) {
303 return NULL;
306 //printf("partof %s w/ %s before and %s after\n", hdr, before, after);
308 tmp = before == NULL ? hdr : strstr(hdr, before);
309 if (!tmp) {
310 //printf ("not found, returning null\n");
311 return (gchar *)def;
314 if (before != NULL) {
315 tmp += strlen(before);
316 //printf ("tmp now %s\n", tmp);
319 if (after != NULL && (tmp2 = strstr(tmp, after))) {
320 gchar * res = g_strndup(tmp, tmp2 - tmp);
321 //printf("returning %s\n", res);
322 return res;
324 res2 = g_strdup(tmp);
325 //printf("returning %s\n", res2);
326 return res2;
329 int sipmsg_parse_cseq(struct sipmsg *msg)
331 int res = -1;
332 gchar **items;
333 items = g_strsplit(sipmsg_find_header(msg, "CSeq"), " ", 1);
334 if (items[0]) {
335 res = atoi(items[0]);
337 g_strfreev(items);
338 return res;
342 * Parse EndPoints header from INVITE request
343 * Returns a list of end points: contact URI plus optional epid.
344 * You must free the values and the list.
346 * Example headers:
347 * EndPoints: "alice alisson" <sip:alice@atlanta.local>, <sip:bob@atlanta.local>;epid=ebca82d94d, <sip:carol@atlanta.local>
348 * EndPoints: "alice, alisson" <sip:alice@atlanta.local>, <sip:bob@atlanta.local>
349 * EndPoints: "alice alisson" <sip:alice@atlanta.local>, "Super, Man" <sip:super@atlanta.local>
351 * @param header (in) EndPoints header contents
353 * @return GSList with struct sipendpoint as elements
355 GSList *sipmsg_parse_endpoints_header(const gchar *header)
357 GSList *list = NULL;
358 gchar **parts = g_strsplit(header, ",", 0);
359 gchar *part;
360 int i;
362 for (i = 0; (part = parts[i]) != NULL; i++) {
363 /* Does the part contain a URI? */
364 gchar *contact = sipmsg_find_part_of_header(part, "<", ">", NULL);
365 if (contact) {
366 struct sipendpoint *end_point = g_new(struct sipendpoint, 1);
367 end_point->contact = contact;
368 end_point->epid = sipmsg_find_part_of_header(part, "epid=", NULL, NULL);
369 list = g_slist_append(list, end_point);
372 g_strfreev(parts);
374 return(list);
377 void sipmsg_parse_p_asserted_identity(const gchar *header, gchar **sip_uri,
378 gchar **tel_uri) {
379 gchar **parts, **p;
381 *sip_uri = NULL;
382 *tel_uri = NULL;
384 if (g_ascii_strncasecmp(header, "tel:", 4) == 0) {
385 *tel_uri = g_strdup(header);
386 return;
389 parts = g_strsplit(header, ",", 0);
391 for (p = parts; *p; p++) {
392 gchar *uri = sipmsg_find_part_of_header(*p, "<", ">", NULL);
393 if (!uri)
394 continue;
396 if (g_ascii_strncasecmp(uri, "sip:", 4) == 0) {
397 if (*sip_uri) {
398 SIPE_DEBUG_WARNING_NOFORMAT("More than one "
399 "sip: URI found in P-Asserted-Identity!");
400 } else {
401 *sip_uri = uri;
402 uri = NULL;
404 } else if (g_ascii_strncasecmp(uri, "tel:", 4) == 0){
405 if (*tel_uri) {
406 SIPE_DEBUG_WARNING_NOFORMAT("More than one "
407 "tel: URI found in P-Asserted-Identity!");
408 } else {
409 *tel_uri = uri;
410 uri = NULL;
414 g_free(uri);
417 g_strfreev(parts);
421 * sipmsg_find_auth_header will return the particular WWW-Authenticate
422 * header specified by *name.
424 * Use this function when you want to look for a specific authentication
425 * method such as NTLM or Kerberos
428 const gchar *sipmsg_find_auth_header(struct sipmsg *msg, const gchar *name) {
429 GSList *tmp;
430 struct sipnameval *elem;
431 int name_len = strlen(name);
432 tmp = msg->headers;
433 while(tmp) {
434 elem = tmp->data;
435 /* SIPE_DEBUG_INFO("Current header: %s", elem->value); */
436 if (elem && elem->name &&
437 (sipe_strcase_equal(elem->name,"WWW-Authenticate") ||
438 sipe_strcase_equal(elem->name,"Authentication-Info")) ) {
439 if (!g_ascii_strncasecmp((gchar *)elem->value, name, name_len)) {
440 /* SIPE_DEBUG_INFO("elem->value: %s", elem->value); */
441 return elem->value;
444 /* SIPE_DEBUG_INFO_NOFORMAT("moving to next header"); */
445 tmp = g_slist_next(tmp);
447 SIPE_DEBUG_INFO("auth header '%s' not found.", name);
448 return NULL;
452 * Parses headers-like 'msgr' attribute of INVITE's 'ms_text_format' header.
453 * Then retrieves value of 'X-MMS-IM-Format'.
455 * 'msgr' typically looks like:
456 * X-MMS-IM-Format: FN=Microsoft%20Sans%20Serif; EF=BI; CO=800000; CS=0; PF=22
458 static gchar *sipmsg_get_x_mms_im_format(gchar *msgr) {
459 gchar *msgr2;
460 gsize msgr_dec64_len;
461 guchar *msgr_dec64;
462 gchar *msgr_utf8;
463 gchar **lines;
464 gchar **parts;
465 gchar *x_mms_im_format;
466 gchar *tmp;
468 if (!msgr) return NULL;
469 msgr2 = g_strdup(msgr);
470 while (strlen(msgr2) % 4 != 0) {
471 gchar *tmp_msgr2 = msgr2;
472 msgr2 = g_strdup_printf("%s=", msgr2);
473 g_free(tmp_msgr2);
475 msgr_dec64 = g_base64_decode(msgr2, &msgr_dec64_len);
476 msgr_utf8 = g_convert((gchar *) msgr_dec64, msgr_dec64_len, "UTF-8", "UTF-16LE", NULL, NULL, NULL);
477 g_free(msgr_dec64);
478 g_free(msgr2);
479 lines = g_strsplit(msgr_utf8,"\r\n\r\n",0);
480 g_free(msgr_utf8);
481 //@TODO: make extraction like parsing of message headers.
482 parts = g_strsplit(lines[0],"X-MMS-IM-Format:",0);
483 x_mms_im_format = g_strdup(parts[1]);
484 g_strfreev(parts);
485 g_strfreev(lines);
486 tmp = x_mms_im_format;
487 if (x_mms_im_format) {
488 while(*x_mms_im_format==' ' || *x_mms_im_format=='\t') x_mms_im_format++;
490 x_mms_im_format = g_strdup(x_mms_im_format);
491 g_free(tmp);
492 return x_mms_im_format;
495 gchar *sipmsg_get_msgr_string(gchar *x_mms_im_format) {
496 gchar *msgr_orig;
497 gsize msgr_utf16_len;
498 gchar *msgr_utf16;
499 gchar *msgr_enc;
500 gchar *res;
501 int len;
503 if (!x_mms_im_format) return NULL;
504 msgr_orig = g_strdup_printf("X-MMS-IM-Format: %s\r\n\r\n", x_mms_im_format);
505 msgr_utf16 = g_convert(msgr_orig, -1, "UTF-16LE", "UTF-8", NULL, &msgr_utf16_len, NULL);
506 g_free(msgr_orig);
507 msgr_enc = g_base64_encode((guchar *) msgr_utf16, msgr_utf16_len);
508 g_free(msgr_utf16);
509 len = strlen(msgr_enc);
510 while (msgr_enc[len - 1] == '=') len--;
511 res = g_strndup(msgr_enc, len);
512 g_free(msgr_enc);
513 return res;
516 static void msn_parse_format(const char *mime, char **pre_ret, char **post_ret);
519 * Translates X-MMS-IM format to HTML presentation.
521 static gchar *sipmsg_apply_x_mms_im_format(const char *x_mms_im_format, gchar *body) {
522 char *pre, *post;
523 gchar *res;
525 if (!x_mms_im_format) {
526 return body ? g_strdup(body) : NULL;
528 msn_parse_format(x_mms_im_format, &pre, &post);
529 res = g_strdup_printf("%s%s%s", pre ? pre : "", body ? body : "", post ? post : "");
530 g_free(pre);
531 g_free(post);
532 return res;
535 struct html_message_data {
536 gchar *ms_text_format;
537 gchar *body;
538 gboolean preferred;
541 static void get_html_message_mime_cb(gpointer user_data,
542 const GSList *fields,
543 const gchar *body,
544 gsize length)
546 const gchar *type = sipe_utils_nameval_find(fields, "Content-Type");
547 struct html_message_data *data = user_data;
549 if (!data->preferred) {
550 gboolean copy = FALSE;
552 /* preferred format */
553 if (g_str_has_prefix(type, "text/html")) {
554 copy = TRUE;
555 data->preferred = TRUE;
557 /* fallback format */
558 } else if (g_str_has_prefix(type, "text/plain")) {
559 copy = TRUE;
562 if (copy) {
563 g_free(data->ms_text_format);
564 g_free(data->body);
565 data->ms_text_format = g_strdup(type);
566 data->body = g_strndup(body, length);
571 /* ms-text-format: text/plain; charset=UTF-8;msgr=WAAtAE0...DIADQAKAA0ACgA;ms-body=SGk= */
572 gchar *get_html_message(const gchar *ms_text_format_in, const gchar *body_in)
574 gchar *msgr;
575 gchar *res;
576 gchar *ms_text_format = NULL;
577 gchar *body = NULL;
579 if (g_str_has_prefix(ms_text_format_in, "multipart/related") ||
580 g_str_has_prefix(ms_text_format_in, "multipart/alternative")) {
581 struct html_message_data data = { NULL, NULL, FALSE };
583 sipe_mime_parts_foreach(ms_text_format_in, body_in,
584 get_html_message_mime_cb, &data);
586 ms_text_format = data.ms_text_format;
587 body = data.body;
589 } else {
590 ms_text_format = g_strdup(ms_text_format_in);
591 body = g_strdup(body_in);
594 if (body) {
595 res = body;
596 } else {
597 gchar *tmp = sipmsg_find_part_of_header(ms_text_format, "ms-body=", NULL, NULL);
598 gsize len;
599 if (!tmp) {
600 g_free(ms_text_format);
601 return NULL;
603 res = (gchar *) g_base64_decode(tmp, &len);
604 g_free(tmp);
605 if (!res) {
606 g_free(ms_text_format);
607 return NULL;
611 if (g_str_has_prefix(ms_text_format, "text/html")) {
613 * HTML uses tags for formatting, not line breaks. But
614 * clients still might render them, so we need to remove
615 * them to avoid incorrect text rendering.
617 gchar *d = res;
618 const gchar *s = res;
619 gchar c;
621 /* No ANSI C nor glib function seems to exist for this :-( */
622 while ((c = *s++))
623 if ((c != '\n') && (c != '\r'))
624 *d++ = c;
625 *d = c;
627 } else {
628 char *tmp = res;
629 res = g_markup_escape_text(res, -1); // as this is not html
630 g_free(tmp);
633 msgr = sipmsg_find_part_of_header(ms_text_format, "msgr=", ";", NULL);
634 if (msgr) {
635 gchar *x_mms_im_format = sipmsg_get_x_mms_im_format(msgr);
636 gchar *tmp = res;
637 g_free(msgr);
638 res = sipmsg_apply_x_mms_im_format(x_mms_im_format, res);
639 g_free(tmp);
640 g_free(x_mms_im_format);
643 g_free(ms_text_format);
645 return res;
648 static gchar *
649 get_reason(struct sipmsg *msg, const gchar *header)
651 const gchar *diagnostics = sipmsg_find_header(msg, header);
652 if (diagnostics)
653 return sipmsg_find_part_of_header(diagnostics, "reason=\"", "\"", NULL);
655 return NULL;
658 gchar *
659 sipmsg_get_ms_diagnostics_reason(struct sipmsg *msg)
661 return get_reason(msg, "ms-diagnostics");
664 gchar *
665 sipmsg_get_ms_diagnostics_public_reason(struct sipmsg *msg)
667 return get_reason(msg, "ms-diagnostics-public");
671 sipmsg_parse_warning(struct sipmsg *msg, gchar **reason)
674 * Example header:
675 * Warning: 310 lcs.microsoft.com "You are currently not using the recommended version of the client"
677 const gchar *hdr = sipmsg_find_header(msg, "Warning");
678 int code = -1;
680 if (reason)
681 *reason = NULL;
683 if (hdr) {
684 gchar **parts = g_strsplit(hdr, " ", 3);
686 if (parts[0]) {
687 code = atoi(parts[0]);
689 if (reason && parts[1] && parts[2]) {
690 size_t len = strlen(parts[2]);
691 if (len > 2 && parts[2][0] == '"' && parts[2][len - 1] == '"')
692 *reason = g_strndup(parts[2] + 1, len - 2);
696 g_strfreev(parts);
699 return code;
704 //------------------------------------------------------------------------------------------
705 //TEMP solution to include it here (copy from purple's msn protocol
706 //How to reuse msn's util methods from sipe?
708 /* from internal.h */
709 #define MSG_LEN 2048
710 #define BUF_LEN MSG_LEN
712 void
713 msn_parse_format(const char *mime, char **pre_ret, char **post_ret)
715 char *cur;
716 GString *pre = g_string_new(NULL);
717 GString *post = g_string_new(NULL);
718 unsigned int colors[3];
720 if (pre_ret != NULL) *pre_ret = NULL;
721 if (post_ret != NULL) *post_ret = NULL;
723 cur = strstr(mime, "FN=");
725 if (cur && (*(cur = cur + 3) != ';'))
727 pre = g_string_append(pre, "<FONT FACE=\"");
729 while (*cur && *cur != ';')
731 pre = g_string_append_c(pre, *cur);
732 cur++;
735 pre = g_string_append(pre, "\">");
736 post = g_string_prepend(post, "</FONT>");
739 cur = strstr(mime, "EF=");
741 if (cur && (*(cur = cur + 3) != ';'))
743 while (*cur && *cur != ';')
745 pre = g_string_append_c(pre, '<');
746 pre = g_string_append_c(pre, *cur);
747 pre = g_string_append_c(pre, '>');
748 post = g_string_prepend_c(post, '>');
749 post = g_string_prepend_c(post, *cur);
750 post = g_string_prepend_c(post, '/');
751 post = g_string_prepend_c(post, '<');
752 cur++;
756 cur = strstr(mime, "CO=");
758 if (cur && (*(cur = cur + 3) != ';'))
760 int i;
762 i = sscanf(cur, "%02x%02x%02x;", &colors[0], &colors[1], &colors[2]);
764 if (i > 0)
766 char tag[64];
768 if (i == 1)
770 colors[1] = 0;
771 colors[2] = 0;
773 else if (i == 2)
775 unsigned int temp = colors[0];
777 colors[0] = colors[1];
778 colors[1] = temp;
779 colors[2] = 0;
781 else if (i == 3)
783 unsigned int temp = colors[2];
785 colors[2] = colors[0];
786 colors[0] = temp;
789 /* hh is undefined in mingw's gcc 4.4
790 * https://sourceforge.net/tracker/index.php?func=detail&aid=2818436&group_id=2435&atid=102435
792 g_snprintf(tag, sizeof(tag),
793 "<FONT COLOR=\"#%02x%02x%02x\">",
794 (unsigned char)colors[0], (unsigned char)colors[1], (unsigned char)colors[2]);
796 pre = g_string_append(pre, tag);
797 post = g_string_prepend(post, "</FONT>");
801 cur = strstr(mime, "RL=");
803 if (cur && (*(cur = cur + 3) != ';'))
805 if (*cur == '1')
807 /* RTL text was received */
808 pre = g_string_append(pre, "<SPAN style=\"direction:rtl;text-align:right;\">");
809 post = g_string_prepend(post, "</SPAN>");
813 cur = sipe_utils_uri_unescape(pre->str);
814 g_string_free(pre, TRUE);
816 if (pre_ret != NULL)
817 *pre_ret = cur;
818 else
819 g_free(cur);
821 cur = sipe_utils_uri_unescape(post->str);
822 g_string_free(post, TRUE);
824 if (post_ret != NULL)
825 *post_ret = cur;
826 else
827 g_free(cur);
830 static const char *
831 encode_spaces(const char *str)
833 static char buf[BUF_LEN];
834 const char *c;
835 char *d;
837 g_return_val_if_fail(str != NULL, NULL);
839 for (c = str, d = buf; *c != '\0'; c++)
841 if (*c == ' ')
843 *d++ = '%';
844 *d++ = '2';
845 *d++ = '0';
847 else
848 *d++ = *c;
850 *d = '\0';
852 return buf;
855 void
856 sipe_parse_html(const char *html, char **attributes, char **message)
858 int len, retcount = 0;
859 const char *c;
860 char *msg;
861 char *fontface = NULL;
862 char fonteffect[4];
863 char fontcolor[7];
864 char direction = '0';
866 gboolean has_bold = FALSE;
867 gboolean has_italic = FALSE;
868 gboolean has_underline = FALSE;
869 gboolean has_strikethrough = FALSE;
871 g_return_if_fail(html != NULL);
872 g_return_if_fail(attributes != NULL);
873 g_return_if_fail(message != NULL);
875 #define _HTML_UNESCAPE \
876 if (!g_ascii_strncasecmp(c, "&lt;", 4)) { \
877 msg[retcount++] = '<'; \
878 c += 4; \
879 } else if (!g_ascii_strncasecmp(c, "&gt;", 4)) { \
880 msg[retcount++] = '>'; \
881 c += 4; \
882 } else if (!g_ascii_strncasecmp(c, "&nbsp;", 6)) { \
883 msg[retcount++] = ' '; \
884 c += 6; \
885 } else if (!g_ascii_strncasecmp(c, "&quot;", 6)) { \
886 msg[retcount++] = '"'; \
887 c += 6; \
888 } else if (!g_ascii_strncasecmp(c, "&amp;", 5)) { \
889 msg[retcount++] = '&'; \
890 c += 5; \
891 } else if (!g_ascii_strncasecmp(c, "&apos;", 6)) { \
892 msg[retcount++] = '\''; \
893 c += 6; \
894 } else { \
895 msg[retcount++] = *c++; \
898 len = strlen(html);
899 msg = g_malloc0(len + 1);
901 memset(fontcolor, 0, sizeof(fontcolor));
902 strcat(fontcolor, "0");
903 memset(fonteffect, 0, sizeof(fonteffect));
905 for (c = html; *c != '\0';)
907 if (*c == '<')
909 if (!g_ascii_strncasecmp(c + 1, "br>", 3))
911 msg[retcount++] = '\r';
912 msg[retcount++] = '\n';
913 c += 4;
915 else if (!g_ascii_strncasecmp(c + 1, "i>", 2))
917 if (!has_italic)
919 strcat(fonteffect, "I");
920 has_italic = TRUE;
922 c += 3;
924 else if (!g_ascii_strncasecmp(c + 1, "b>", 2))
926 if (!has_bold)
928 strcat(fonteffect, "B");
929 has_bold = TRUE;
931 c += 3;
933 else if (!g_ascii_strncasecmp(c + 1, "u>", 2))
935 if (!has_underline)
937 strcat(fonteffect, "U");
938 has_underline = TRUE;
940 c += 3;
942 else if (!g_ascii_strncasecmp(c + 1, "s>", 2))
944 if (!has_strikethrough)
946 strcat(fonteffect, "S");
947 has_strikethrough = TRUE;
949 c += 3;
951 else if (!g_ascii_strncasecmp(c + 1, "a href=\"", 8))
953 c += 9;
955 if (!g_ascii_strncasecmp(c, "mailto:", 7))
956 c += 7;
958 while ((*c != '\0') && g_ascii_strncasecmp(c, "\">", 2))
959 if (*c == '&') {
960 _HTML_UNESCAPE;
961 } else
962 msg[retcount++] = *c++;
964 if (*c != '\0')
965 c += 2;
967 /* ignore descriptive string */
968 while ((*c != '\0') && g_ascii_strncasecmp(c, "</a>", 4))
969 c++;
971 if (*c != '\0')
972 c += 4;
974 else if (!g_ascii_strncasecmp(c + 1, "span", 4))
976 /* Bi-directional text support using CSS properties in span tags */
977 c += 5;
979 while (*c != '\0' && *c != '>')
981 while (*c == ' ')
982 c++;
983 if (!g_ascii_strncasecmp(c, "dir=\"rtl\"", 9))
985 c += 9;
986 direction = '1';
988 else if (!g_ascii_strncasecmp(c, "style=\"", 7))
990 /* Parse inline CSS attributes */
991 int attr_len = 0;
992 c += 7;
993 while (*(c + attr_len) != '\0' && *(c + attr_len) != '"')
994 attr_len++;
995 if (*(c + attr_len) == '"')
997 char *css_attributes;
998 char *attr_dir;
999 css_attributes = g_strndup(c, attr_len);
1000 attr_dir = sipe_backend_markup_css_property(css_attributes, "direction");
1001 g_free(css_attributes);
1002 if (attr_dir && (!g_ascii_strncasecmp(attr_dir, "RTL", 3)))
1003 direction = '1';
1004 g_free(attr_dir);
1008 else
1010 c++;
1013 if (*c == '>')
1014 c++;
1016 else if (!g_ascii_strncasecmp(c + 1, "font", 4))
1018 c += 5;
1020 while ((*c != '\0') && !g_ascii_strncasecmp(c, " ", 1))
1021 c++;
1023 if (!g_ascii_strncasecmp(c, "color=\"#", 7))
1025 c += 8;
1027 fontcolor[0] = *(c + 4);
1028 fontcolor[1] = *(c + 5);
1029 fontcolor[2] = *(c + 2);
1030 fontcolor[3] = *(c + 3);
1031 fontcolor[4] = *c;
1032 fontcolor[5] = *(c + 1);
1034 c += 8;
1036 else if (!g_ascii_strncasecmp(c, "face=\"", 6))
1038 const char *end = NULL;
1039 const char *comma = NULL;
1040 unsigned int namelen = 0;
1042 c += 6;
1043 end = strchr(c, '\"');
1044 comma = strchr(c, ',');
1046 if (comma == NULL || comma > end)
1047 namelen = (unsigned int)(end - c);
1048 else
1049 namelen = (unsigned int)(comma - c);
1051 g_free(fontface);
1052 fontface = g_strndup(c, namelen);
1053 c = end + 2;
1055 else
1057 /* Drop all unrecognized/misparsed font tags */
1058 while ((*c != '\0') && g_ascii_strncasecmp(c, "\">", 2))
1059 c++;
1061 if (*c != '\0')
1062 c += 2;
1065 else
1067 while ((*c != '\0') && (*c != '>'))
1068 c++;
1069 if (*c != '\0')
1070 c++;
1073 else if (*c == '&')
1075 _HTML_UNESCAPE;
1077 else
1078 msg[retcount++] = *c++;
1081 if (fontface == NULL)
1082 fontface = g_strdup("MS Sans Serif");
1084 *attributes = g_strdup_printf("FN=%s; EF=%s; CO=%s; PF=0; RL=%c",
1085 encode_spaces(fontface),
1086 fonteffect, fontcolor, direction);
1087 *message = msg;
1089 g_free(fontface);
1091 #undef _HTML_UNESCAPE
1093 // End of TEMP
1096 Local Variables:
1097 mode: c
1098 c-file-style: "bsd"
1099 indent-tabs-mode: t
1100 tab-width: 8
1101 End: