sipmsg: handle Pidgin 3.x paragraph separators
[siplcs.git] / src / core / sipmsg.c
blob4bbfb3e29af15d880acc77cd6b85e9f25df52a06
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) {
96 * This is a fatal error situation: the message
97 * is corrupted and we can't proceed. Set the
98 * response code to a special value so that the
99 * caller can abort correctly.
101 SIPE_DEBUG_ERROR_NOFORMAT("sipmsg_parse_header: Content-Length header not found. Aborting!");
102 msg->response = SIPMSG_RESPONSE_FATAL_ERROR;
103 return(msg);
104 } else {
105 msg->bodylen = 0;
109 if(msg->response) {
110 const gchar *tmp;
111 tmp = sipmsg_find_header(msg, "CSeq");
112 if(!tmp) {
113 /* SHOULD NOT HAPPEN */
114 msg->method = 0;
115 } else {
116 parts = g_strsplit(tmp, " ", 2);
117 msg->method = g_strdup(parts[1]);
118 g_strfreev(parts);
121 return msg;
124 struct sipmsg *sipmsg_copy(const struct sipmsg *other) {
125 struct sipmsg *msg = g_new0(struct sipmsg, 1);
126 GSList *list;
128 msg->response = other->response;
129 msg->responsestr = g_strdup(other->responsestr);
130 msg->method = g_strdup(other->method);
131 msg->target = g_strdup(other->target);
133 list = other->headers;
134 while(list) {
135 struct sipnameval *elem = list->data;
136 sipmsg_add_header_now(msg, elem->name, elem->value);
137 list = list->next;
140 list = other->new_headers;
141 while(list) {
142 struct sipnameval *elem = list->data;
143 sipmsg_add_header(msg, elem->name, elem->value);
144 list = list->next;
147 msg->bodylen = other->bodylen;
148 msg->body = g_strdup(other->body);
149 msg->signature = g_strdup(other->signature);
150 msg->rand = g_strdup(other->rand);
151 msg->num = g_strdup(other->num);
153 return msg;
156 char *sipmsg_to_string(const struct sipmsg *msg) {
157 GSList *cur;
158 GString *outstr = g_string_new("");
159 struct sipnameval *elem;
161 if(msg->response)
162 g_string_append_printf(outstr, "SIP/2.0 %d Unknown\r\n",
163 msg->response);
164 else
165 g_string_append_printf(outstr, "%s %s SIP/2.0\r\n",
166 msg->method, msg->target);
168 cur = msg->headers;
169 while(cur) {
170 elem = cur->data;
171 /*Todo: remove the LFCR in a good way*/
172 /*if(sipe_strequal(elem->name,"Proxy-Authorization"))
173 g_string_append_printf(outstr, "%s: %s", elem->name,
174 elem->value);
175 else */
176 g_string_append_printf(outstr, "%s: %s\r\n", elem->name,
177 elem->value);
178 cur = g_slist_next(cur);
181 g_string_append_printf(outstr, "\r\n%s", msg->bodylen ? msg->body : "");
183 return g_string_free(outstr, FALSE);
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_ascii_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 if (msg) {
268 sipe_utils_nameval_free(msg->headers);
269 sipe_utils_nameval_free(msg->new_headers);
270 g_free(msg->signature);
271 g_free(msg->rand);
272 g_free(msg->num);
273 g_free(msg->responsestr);
274 g_free(msg->method);
275 g_free(msg->target);
276 g_free(msg->body);
277 g_free(msg);
281 void sipmsg_remove_header_now(struct sipmsg *msg, const gchar *name) {
282 struct sipnameval *elem;
283 GSList *tmp = msg->headers;
284 while(tmp) {
285 elem = tmp->data;
286 // OCS2005 can send the same header in either all caps or mixed case
287 if (sipe_strcase_equal(elem->name, name)) {
288 msg->headers = g_slist_remove(msg->headers, elem);
289 g_free(elem->name);
290 g_free(elem->value);
291 g_free(elem);
292 return;
294 tmp = g_slist_next(tmp);
296 return;
299 const gchar *sipmsg_find_header(const struct sipmsg *msg, const gchar *name) {
300 return sipe_utils_nameval_find_instance (msg->headers, name, 0);
303 const gchar *sipmsg_find_header_instance(const struct sipmsg *msg, const gchar *name, int which) {
304 return sipe_utils_nameval_find_instance(msg->headers, name, which);
307 gchar *sipmsg_find_part_of_header(const char *hdr, const char * before, const char * after, const char * def) {
308 const char *tmp;
309 const char *tmp2;
310 gchar *res2;
311 if (!hdr) {
312 return NULL;
315 //printf("partof %s w/ %s before and %s after\n", hdr, before, after);
317 tmp = before == NULL ? hdr : strstr(hdr, before);
318 if (!tmp) {
319 //printf ("not found, returning null\n");
320 return (gchar *)def;
323 if (before != NULL) {
324 tmp += strlen(before);
325 //printf ("tmp now %s\n", tmp);
328 if (after != NULL && (tmp2 = strstr(tmp, after))) {
329 gchar * res = g_strndup(tmp, tmp2 - tmp);
330 //printf("returning %s\n", res);
331 return res;
333 res2 = g_strdup(tmp);
334 //printf("returning %s\n", res2);
335 return res2;
338 int sipmsg_parse_cseq(struct sipmsg *msg)
340 int res = -1;
341 gchar **items;
342 items = g_strsplit(sipmsg_find_header(msg, "CSeq"), " ", 1);
343 if (items[0]) {
344 res = atoi(items[0]);
346 g_strfreev(items);
347 return res;
351 * Parse EndPoints header from INVITE request
352 * Returns a list of end points: contact URI plus optional epid.
353 * You must free the values and the list.
355 * Example headers:
356 * EndPoints: "alice alisson" <sip:alice@atlanta.local>, <sip:bob@atlanta.local>;epid=ebca82d94d, <sip:carol@atlanta.local>
357 * EndPoints: "alice, alisson" <sip:alice@atlanta.local>, <sip:bob@atlanta.local>
358 * EndPoints: "alice alisson" <sip:alice@atlanta.local>, "Super, Man" <sip:super@atlanta.local>
360 * @param header (in) EndPoints header contents
362 * @return GSList with struct sipendpoint as elements
364 GSList *sipmsg_parse_endpoints_header(const gchar *header)
366 GSList *list = NULL;
367 gchar **parts = g_strsplit(header, ",", 0);
368 gchar *part;
369 int i;
371 for (i = 0; (part = parts[i]) != NULL; i++) {
372 /* Does the part contain a URI? */
373 gchar *contact = sipmsg_find_part_of_header(part, "<", ">", NULL);
374 if (contact) {
375 struct sipendpoint *end_point = g_new(struct sipendpoint, 1);
376 end_point->contact = contact;
377 end_point->epid = sipmsg_find_part_of_header(part, "epid=", NULL, NULL);
378 list = g_slist_append(list, end_point);
381 g_strfreev(parts);
383 return(list);
386 void sipmsg_parse_p_asserted_identity(const gchar *header, gchar **sip_uri,
387 gchar **tel_uri) {
388 gchar **parts, **p;
390 *sip_uri = NULL;
391 *tel_uri = NULL;
393 if (g_ascii_strncasecmp(header, "tel:", 4) == 0) {
394 *tel_uri = g_strdup(header);
395 return;
398 parts = g_strsplit(header, ",", 0);
400 for (p = parts; *p; p++) {
401 gchar *uri = sipmsg_find_part_of_header(*p, "<", ">", NULL);
402 if (!uri)
403 continue;
405 if (g_ascii_strncasecmp(uri, "sip:", 4) == 0) {
406 if (*sip_uri) {
407 SIPE_DEBUG_WARNING_NOFORMAT("More than one "
408 "sip: URI found in P-Asserted-Identity!");
409 } else {
410 *sip_uri = uri;
411 uri = NULL;
413 } else if (g_ascii_strncasecmp(uri, "tel:", 4) == 0){
414 if (*tel_uri) {
415 SIPE_DEBUG_WARNING_NOFORMAT("More than one "
416 "tel: URI found in P-Asserted-Identity!");
417 } else {
418 *tel_uri = uri;
419 uri = NULL;
423 g_free(uri);
426 g_strfreev(parts);
430 * sipmsg_find_auth_header will return the particular WWW-Authenticate
431 * header specified by *name.
433 * Use this function when you want to look for a specific authentication
434 * method such as NTLM or Kerberos
437 const gchar *sipmsg_find_auth_header(struct sipmsg *msg, const gchar *name) {
438 GSList *tmp;
439 struct sipnameval *elem;
440 int name_len;
442 if (!name) {
443 SIPE_DEBUG_INFO_NOFORMAT("sipmsg_find_auth_header: no authentication scheme specified");
444 return NULL;
447 name_len = strlen(name);
448 tmp = msg->headers;
449 while(tmp) {
450 elem = tmp->data;
451 /* SIPE_DEBUG_INFO("Current header: %s", elem->value); */
452 if (elem && elem->name &&
453 (sipe_strcase_equal(elem->name,"WWW-Authenticate") ||
454 sipe_strcase_equal(elem->name,"Authentication-Info")) ) {
455 if (!g_ascii_strncasecmp((gchar *)elem->value, name, name_len)) {
456 /* SIPE_DEBUG_INFO("elem->value: %s", elem->value); */
457 return elem->value;
460 /* SIPE_DEBUG_INFO_NOFORMAT("moving to next header"); */
461 tmp = g_slist_next(tmp);
463 SIPE_DEBUG_INFO("sipmsg_find_auth_header: '%s' not found", name);
464 return NULL;
468 * Parses headers-like 'msgr' attribute of INVITE's 'ms_text_format' header.
469 * Then retrieves value of 'X-MMS-IM-Format'.
471 * 'msgr' typically looks like:
472 * X-MMS-IM-Format: FN=Microsoft%20Sans%20Serif; EF=BI; CO=800000; CS=0; PF=22
474 static gchar *sipmsg_get_x_mms_im_format(gchar *msgr) {
475 gchar *msgr2;
476 gsize msgr_dec64_len;
477 guchar *msgr_dec64;
478 gchar *msgr_utf8;
479 gchar **lines;
480 gchar **parts;
481 gchar *x_mms_im_format;
482 gchar *tmp;
484 if (!msgr) return NULL;
485 msgr2 = g_strdup(msgr);
486 while (strlen(msgr2) % 4 != 0) {
487 gchar *tmp_msgr2 = msgr2;
488 msgr2 = g_strdup_printf("%s=", msgr2);
489 g_free(tmp_msgr2);
491 msgr_dec64 = g_base64_decode(msgr2, &msgr_dec64_len);
492 msgr_utf8 = g_convert((gchar *) msgr_dec64, msgr_dec64_len, "UTF-8", "UTF-16LE", NULL, NULL, NULL);
493 g_free(msgr_dec64);
494 g_free(msgr2);
495 lines = g_strsplit(msgr_utf8,"\r\n\r\n",0);
496 g_free(msgr_utf8);
497 //@TODO: make extraction like parsing of message headers.
498 parts = g_strsplit(lines[0],"X-MMS-IM-Format:",0);
499 x_mms_im_format = g_strdup(parts[1]);
500 g_strfreev(parts);
501 g_strfreev(lines);
502 tmp = x_mms_im_format;
503 if (x_mms_im_format) {
504 while(*x_mms_im_format==' ' || *x_mms_im_format=='\t') x_mms_im_format++;
506 x_mms_im_format = g_strdup(x_mms_im_format);
507 g_free(tmp);
508 return x_mms_im_format;
511 gchar *sipmsg_get_msgr_string(gchar *x_mms_im_format) {
512 gchar *msgr_orig;
513 gsize msgr_utf16_len;
514 gchar *msgr_utf16;
515 gchar *msgr_enc;
516 gchar *res;
517 int len;
519 if (!x_mms_im_format) return NULL;
520 msgr_orig = g_strdup_printf("X-MMS-IM-Format: %s\r\n\r\n", x_mms_im_format);
521 msgr_utf16 = g_convert(msgr_orig, -1, "UTF-16LE", "UTF-8", NULL, &msgr_utf16_len, NULL);
522 g_free(msgr_orig);
523 msgr_enc = g_base64_encode((guchar *) msgr_utf16, msgr_utf16_len);
524 g_free(msgr_utf16);
525 len = strlen(msgr_enc);
526 while (msgr_enc[len - 1] == '=') len--;
527 res = g_strndup(msgr_enc, len);
528 g_free(msgr_enc);
529 return res;
532 static void msn_parse_format(const char *mime, char **pre_ret, char **post_ret);
535 * Translates X-MMS-IM format to HTML presentation.
537 static gchar *sipmsg_apply_x_mms_im_format(const char *x_mms_im_format, gchar *body) {
538 char *pre, *post;
539 gchar *res;
541 if (!x_mms_im_format) {
542 return body ? g_strdup(body) : NULL;
544 msn_parse_format(x_mms_im_format, &pre, &post);
545 res = g_strdup_printf("%s%s%s", pre ? pre : "", body ? body : "", post ? post : "");
546 g_free(pre);
547 g_free(post);
548 return res;
551 struct html_message_data {
552 gchar *ms_text_format;
553 gchar *body;
554 gboolean preferred;
557 static void get_html_message_mime_cb(gpointer user_data,
558 const GSList *fields,
559 const gchar *body,
560 gsize length)
562 const gchar *type = sipe_utils_nameval_find(fields, "Content-Type");
563 struct html_message_data *data = user_data;
565 if (!data->preferred) {
566 gboolean copy = FALSE;
568 /* preferred format */
569 if (g_str_has_prefix(type, "text/html")) {
570 copy = TRUE;
571 data->preferred = TRUE;
573 /* fallback format */
574 } else if (g_str_has_prefix(type, "text/plain")) {
575 copy = TRUE;
578 if (copy) {
579 g_free(data->ms_text_format);
580 g_free(data->body);
581 data->ms_text_format = g_strdup(type);
582 data->body = g_strndup(body, length);
587 /* ms-text-format: text/plain; charset=UTF-8;msgr=WAAtAE0...DIADQAKAA0ACgA;ms-body=SGk= */
588 gchar *get_html_message(const gchar *ms_text_format_in, const gchar *body_in)
590 gchar *msgr;
591 gchar *res;
592 gchar *ms_text_format = NULL;
593 gchar *body = NULL;
595 if (g_str_has_prefix(ms_text_format_in, "multipart/related") ||
596 g_str_has_prefix(ms_text_format_in, "multipart/alternative")) {
597 struct html_message_data data = { NULL, NULL, FALSE };
599 sipe_mime_parts_foreach(ms_text_format_in, body_in,
600 get_html_message_mime_cb, &data);
602 ms_text_format = data.ms_text_format;
603 body = data.body;
605 } else {
606 ms_text_format = g_strdup(ms_text_format_in);
607 body = g_strdup(body_in);
610 if (body) {
611 res = body;
612 } else {
613 gchar *tmp = sipmsg_find_part_of_header(ms_text_format, "ms-body=", NULL, NULL);
614 gsize len;
615 if (!tmp) {
616 g_free(ms_text_format);
617 return NULL;
619 res = (gchar *) g_base64_decode(tmp, &len);
620 g_free(tmp);
621 if (!res) {
622 g_free(ms_text_format);
623 return NULL;
627 if (g_str_has_prefix(ms_text_format, "text/html")) {
629 * HTML uses tags for formatting, not line breaks. But
630 * clients still might render them, so we need to remove
631 * them to avoid incorrect text rendering.
633 gchar *d = res;
634 const gchar *s = res;
635 gchar c;
637 /* No ANSI C nor glib function seems to exist for this :-( */
638 while ((c = *s++))
639 if ((c != '\n') && (c != '\r'))
640 *d++ = c;
641 *d = c;
643 } else {
644 char *tmp = res;
645 res = g_markup_escape_text(res, -1); // as this is not html
646 g_free(tmp);
649 msgr = sipmsg_find_part_of_header(ms_text_format, "msgr=", ";", NULL);
650 if (msgr) {
651 gchar *x_mms_im_format = sipmsg_get_x_mms_im_format(msgr);
652 gchar *tmp = res;
653 g_free(msgr);
654 res = sipmsg_apply_x_mms_im_format(x_mms_im_format, res);
655 g_free(tmp);
656 g_free(x_mms_im_format);
659 g_free(ms_text_format);
661 return res;
664 static gchar *
665 get_reason(struct sipmsg *msg, const gchar *header)
667 const gchar *diagnostics = sipmsg_find_header(msg, header);
668 if (diagnostics)
669 return sipmsg_find_part_of_header(diagnostics, "reason=\"", "\"", NULL);
671 return NULL;
674 gchar *
675 sipmsg_get_ms_diagnostics_reason(struct sipmsg *msg)
677 return get_reason(msg, "ms-diagnostics");
680 gchar *
681 sipmsg_get_ms_diagnostics_public_reason(struct sipmsg *msg)
683 return get_reason(msg, "ms-diagnostics-public");
687 sipmsg_parse_warning(struct sipmsg *msg, gchar **reason)
690 * Example header:
691 * Warning: 310 lcs.microsoft.com "You are currently not using the recommended version of the client"
693 const gchar *hdr = sipmsg_find_header(msg, "Warning");
694 int code = -1;
696 if (reason)
697 *reason = NULL;
699 if (hdr) {
700 gchar **parts = g_strsplit(hdr, " ", 3);
702 if (parts[0]) {
703 code = atoi(parts[0]);
705 if (reason && parts[1] && parts[2]) {
706 size_t len = strlen(parts[2]);
707 if (len > 2 && parts[2][0] == '"' && parts[2][len - 1] == '"')
708 *reason = g_strndup(parts[2] + 1, len - 2);
712 g_strfreev(parts);
715 return code;
720 //------------------------------------------------------------------------------------------
721 //TEMP solution to include it here (copy from purple's msn protocol
722 //How to reuse msn's util methods from sipe?
724 /* from internal.h */
725 #define MSG_LEN 2048
726 #define BUF_LEN MSG_LEN
728 void
729 msn_parse_format(const char *mime, char **pre_ret, char **post_ret)
731 char *cur;
732 GString *pre = g_string_new(NULL);
733 GString *post = g_string_new(NULL);
734 unsigned int colors[3];
736 if (pre_ret != NULL) *pre_ret = NULL;
737 if (post_ret != NULL) *post_ret = NULL;
739 cur = strstr(mime, "FN=");
741 if (cur && (*(cur = cur + 3) != ';'))
743 pre = g_string_append(pre, "<FONT FACE=\"");
745 while (*cur && *cur != ';')
747 pre = g_string_append_c(pre, *cur);
748 cur++;
751 pre = g_string_append(pre, "\">");
752 post = g_string_prepend(post, "</FONT>");
755 cur = strstr(mime, "EF=");
757 if (cur && (*(cur = cur + 3) != ';'))
759 while (*cur && *cur != ';')
761 pre = g_string_append_c(pre, '<');
762 pre = g_string_append_c(pre, *cur);
763 pre = g_string_append_c(pre, '>');
764 post = g_string_prepend_c(post, '>');
765 post = g_string_prepend_c(post, *cur);
766 post = g_string_prepend_c(post, '/');
767 post = g_string_prepend_c(post, '<');
768 cur++;
772 cur = strstr(mime, "CO=");
774 if (cur && (*(cur = cur + 3) != ';'))
776 int i;
778 i = sscanf(cur, "%02x%02x%02x;", &colors[0], &colors[1], &colors[2]);
780 if (i > 0)
782 char tag[64];
784 if (i == 1)
786 colors[1] = 0;
787 colors[2] = 0;
789 else if (i == 2)
791 unsigned int temp = colors[0];
793 colors[0] = colors[1];
794 colors[1] = temp;
795 colors[2] = 0;
797 else if (i == 3)
799 unsigned int temp = colors[2];
801 colors[2] = colors[0];
802 colors[0] = temp;
805 /* hh is undefined in mingw's gcc 4.4
806 * https://sourceforge.net/tracker/index.php?func=detail&aid=2818436&group_id=2435&atid=102435
808 g_snprintf(tag, sizeof(tag),
809 "<FONT COLOR=\"#%02x%02x%02x\">",
810 (unsigned char)colors[0], (unsigned char)colors[1], (unsigned char)colors[2]);
812 pre = g_string_append(pre, tag);
813 post = g_string_prepend(post, "</FONT>");
817 cur = strstr(mime, "RL=");
819 if (cur && (*(cur = cur + 3) != ';'))
821 if (*cur == '1')
823 /* RTL text was received */
824 pre = g_string_append(pre, "<SPAN style=\"direction:rtl;text-align:right;\">");
825 post = g_string_prepend(post, "</SPAN>");
829 cur = sipe_utils_uri_unescape(pre->str);
830 g_string_free(pre, TRUE);
832 if (pre_ret != NULL)
833 *pre_ret = cur;
834 else
835 g_free(cur);
837 cur = sipe_utils_uri_unescape(post->str);
838 g_string_free(post, TRUE);
840 if (post_ret != NULL)
841 *post_ret = cur;
842 else
843 g_free(cur);
846 static const char *
847 encode_spaces(const char *str)
849 static char buf[BUF_LEN];
850 const char *c;
851 char *d;
853 g_return_val_if_fail(str != NULL, NULL);
855 for (c = str, d = buf; *c != '\0'; c++)
857 if (*c == ' ')
859 *d++ = '%';
860 *d++ = '2';
861 *d++ = '0';
863 else
864 *d++ = *c;
866 *d = '\0';
868 return buf;
871 void
872 sipe_parse_html(const char *html, char **attributes, char **message)
874 int len, retcount = 0;
875 const char *c;
876 char *msg;
877 char *fontface = NULL;
878 char fonteffect[4];
879 char fontcolor[7];
880 char direction = '0';
882 gboolean has_bold = FALSE;
883 gboolean has_italic = FALSE;
884 gboolean has_underline = FALSE;
885 gboolean has_strikethrough = FALSE;
887 g_return_if_fail(html != NULL);
888 g_return_if_fail(attributes != NULL);
889 g_return_if_fail(message != NULL);
891 #define _HTML_UNESCAPE \
892 if (!g_ascii_strncasecmp(c, "&lt;", 4)) { \
893 msg[retcount++] = '<'; \
894 c += 4; \
895 } else if (!g_ascii_strncasecmp(c, "&gt;", 4)) { \
896 msg[retcount++] = '>'; \
897 c += 4; \
898 } else if (!g_ascii_strncasecmp(c, "&nbsp;", 6)) { \
899 msg[retcount++] = ' '; \
900 c += 6; \
901 } else if (!g_ascii_strncasecmp(c, "&quot;", 6)) { \
902 msg[retcount++] = '"'; \
903 c += 6; \
904 } else if (!g_ascii_strncasecmp(c, "&amp;", 5)) { \
905 msg[retcount++] = '&'; \
906 c += 5; \
907 } else if (!g_ascii_strncasecmp(c, "&apos;", 6)) { \
908 msg[retcount++] = '\''; \
909 c += 6; \
910 } else { \
911 msg[retcount++] = *c++; \
914 len = strlen(html);
915 msg = g_malloc0(len + 1);
917 memset(fontcolor, 0, sizeof(fontcolor));
918 strcat(fontcolor, "0");
919 memset(fonteffect, 0, sizeof(fonteffect));
921 for (c = html; *c != '\0';)
923 if (*c == '<')
925 if (!g_ascii_strncasecmp(c + 1, "br>", 3))
927 msg[retcount++] = '\r';
928 msg[retcount++] = '\n';
929 c += 4;
931 else if (!g_ascii_strncasecmp(c + 1, "div>", 4))
933 msg[retcount++] = '\r';
934 msg[retcount++] = '\n';
935 c += 5;
936 if (!g_ascii_strncasecmp(c, "<br></div>", 10)) {
937 /* This is an empty paragraph; replace it with
938 * one line break. */
939 c += 10;
942 else if (!g_ascii_strncasecmp(c + 1, "i>", 2))
944 if (!has_italic)
946 strcat(fonteffect, "I");
947 has_italic = TRUE;
949 c += 3;
951 else if (!g_ascii_strncasecmp(c + 1, "b>", 2))
953 if (!has_bold)
955 strcat(fonteffect, "B");
956 has_bold = TRUE;
958 c += 3;
960 else if (!g_ascii_strncasecmp(c + 1, "u>", 2))
962 if (!has_underline)
964 strcat(fonteffect, "U");
965 has_underline = TRUE;
967 c += 3;
969 else if (!g_ascii_strncasecmp(c + 1, "s>", 2))
971 if (!has_strikethrough)
973 strcat(fonteffect, "S");
974 has_strikethrough = TRUE;
976 c += 3;
978 else if (!g_ascii_strncasecmp(c + 1, "a href=\"", 8))
980 c += 9;
982 if (!g_ascii_strncasecmp(c, "mailto:", 7))
983 c += 7;
985 while ((*c != '\0') && g_ascii_strncasecmp(c, "\">", 2))
986 if (*c == '&') {
987 _HTML_UNESCAPE;
988 } else
989 msg[retcount++] = *c++;
991 if (*c != '\0')
992 c += 2;
994 /* ignore descriptive string */
995 while ((*c != '\0') && g_ascii_strncasecmp(c, "</a>", 4))
996 c++;
998 if (*c != '\0')
999 c += 4;
1001 else if (!g_ascii_strncasecmp(c + 1, "span", 4))
1003 /* Bi-directional text support using CSS properties in span tags */
1004 c += 5;
1006 while (*c != '\0' && *c != '>')
1008 while (*c == ' ')
1009 c++;
1010 if (!g_ascii_strncasecmp(c, "dir=\"rtl\"", 9))
1012 c += 9;
1013 direction = '1';
1015 else if (!g_ascii_strncasecmp(c, "style=\"", 7))
1017 /* Parse inline CSS attributes */
1018 int attr_len = 0;
1019 c += 7;
1020 while (*(c + attr_len) != '\0' && *(c + attr_len) != '"')
1021 attr_len++;
1022 if (*(c + attr_len) == '"')
1024 char *css_attributes;
1025 char *attr_dir;
1026 css_attributes = g_strndup(c, attr_len);
1027 attr_dir = sipe_backend_markup_css_property(css_attributes, "direction");
1028 g_free(css_attributes);
1029 if (attr_dir && (!g_ascii_strncasecmp(attr_dir, "RTL", 3)))
1030 direction = '1';
1031 g_free(attr_dir);
1035 else
1037 c++;
1040 if (*c == '>')
1041 c++;
1043 else if (!g_ascii_strncasecmp(c + 1, "font", 4))
1045 c += 5;
1047 while ((*c != '\0') && !g_ascii_strncasecmp(c, " ", 1))
1048 c++;
1050 if (!g_ascii_strncasecmp(c, "color=\"#", 7))
1052 c += 8;
1054 fontcolor[0] = *(c + 4);
1055 fontcolor[1] = *(c + 5);
1056 fontcolor[2] = *(c + 2);
1057 fontcolor[3] = *(c + 3);
1058 fontcolor[4] = *c;
1059 fontcolor[5] = *(c + 1);
1061 c += 8;
1063 else if (!g_ascii_strncasecmp(c, "face=\"", 6))
1065 const char *end = NULL;
1066 const char *comma = NULL;
1067 unsigned int namelen = 0;
1069 c += 6;
1070 end = strchr(c, '\"');
1071 comma = strchr(c, ',');
1073 if (comma == NULL || comma > end)
1074 namelen = (unsigned int)(end - c);
1075 else
1076 namelen = (unsigned int)(comma - c);
1078 g_free(fontface);
1079 fontface = g_strndup(c, namelen);
1080 c = end + 2;
1082 else
1084 /* Drop all unrecognized/misparsed font tags */
1085 while ((*c != '\0') && g_ascii_strncasecmp(c, "\">", 2))
1086 c++;
1088 if (*c != '\0')
1089 c += 2;
1092 else
1094 while ((*c != '\0') && (*c != '>'))
1095 c++;
1096 if (*c != '\0')
1097 c++;
1100 else if (*c == '&')
1102 _HTML_UNESCAPE;
1104 else
1105 msg[retcount++] = *c++;
1108 if (fontface == NULL)
1109 fontface = g_strdup("MS Sans Serif");
1111 *attributes = g_strdup_printf("FN=%s; EF=%s; CO=%s; PF=0; RL=%c",
1112 encode_spaces(fontface),
1113 fonteffect, fontcolor, direction);
1114 *message = msg;
1116 g_free(fontface);
1118 #undef _HTML_UNESCAPE
1120 // End of TEMP
1123 Local Variables:
1124 mode: c
1125 c-file-style: "bsd"
1126 indent-tabs-mode: t
1127 tab-width: 8
1128 End: