core: take sipmsg_find_to_header() into use
[siplcs.git] / src / core / sipmsg.c
blob3e277f20f7f68715692c051ab1f6a5b847729fc6
1 /**
2 * @file sipmsg.c
4 * pidgin-sipe
6 * Copyright (C) 2010-2019 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-rtf.h"
36 #include "sipe-utils.h"
38 struct sipmsg *sipmsg_parse_msg(const gchar *msg) {
39 const char *tmp = strstr(msg, "\r\n\r\n");
40 char *line;
41 struct sipmsg *smsg;
43 if(!tmp) return NULL;
45 line = g_strndup(msg, tmp - msg);
47 smsg = sipmsg_parse_header(line);
48 smsg->body = g_strdup(tmp + 4);
50 g_free(line);
51 return smsg;
54 struct sipmsg *sipmsg_parse_header(const gchar *header) {
55 struct sipmsg *msg = g_new0(struct sipmsg,1);
56 gchar **lines = g_strsplit(header,"\r\n",0);
57 gchar **parts;
58 const gchar *contentlength;
59 if(!lines[0]) {
60 g_strfreev(lines);
61 g_free(msg);
62 return NULL;
64 parts = g_strsplit(lines[0], " ", 3);
65 if(!parts[0] || !parts[1] || !parts[2]) {
66 g_strfreev(parts);
67 g_strfreev(lines);
68 g_free(msg);
69 return NULL;
71 if(strstr(parts[0],"SIP") || strstr(parts[0],"HTTP")) { /* numeric response */
72 msg->responsestr = g_strdup(parts[2]);
73 msg->response = strtol(parts[1],NULL,10);
74 } else { /* request */
75 msg->method = g_strdup(parts[0]);
76 msg->target = g_strdup(parts[1]);
77 msg->response = 0;
79 g_strfreev(parts);
80 if (sipe_utils_parse_lines(&msg->headers, lines + 1, ":") == FALSE) {
81 g_strfreev(lines);
82 sipmsg_free(msg);
83 return NULL;
85 g_strfreev(lines);
86 contentlength = sipmsg_find_header(msg, "Content-Length");
87 if (contentlength) {
88 msg->bodylen = strtol(contentlength,NULL,10);
89 } else {
90 const gchar *tmp = sipmsg_find_header(msg, "Transfer-Encoding");
91 if (tmp && sipe_strcase_equal(tmp, "chunked")) {
92 msg->bodylen = SIPMSG_BODYLEN_CHUNKED;
93 } else {
94 tmp = sipmsg_find_content_type_header(msg);
95 if (tmp) {
97 * This is a fatal error situation: the message
98 * is corrupted and we can't proceed. Set the
99 * response code to a special value so that the
100 * caller can abort correctly.
102 SIPE_DEBUG_ERROR_NOFORMAT("sipmsg_parse_header: Content-Length header not found. Aborting!");
103 msg->response = SIPMSG_RESPONSE_FATAL_ERROR;
104 return(msg);
105 } else {
106 msg->bodylen = 0;
110 if(msg->response) {
111 const gchar *tmp;
112 tmp = sipmsg_find_cseq_header(msg);
113 if(!tmp) {
114 /* SHOULD NOT HAPPEN */
115 msg->method = 0;
116 } else {
117 parts = g_strsplit(tmp, " ", 2);
118 msg->method = g_strdup(parts[1]);
119 g_strfreev(parts);
122 return msg;
125 struct sipmsg *sipmsg_copy(const struct sipmsg *other) {
126 struct sipmsg *msg = g_new0(struct sipmsg, 1);
127 GSList *list;
129 msg->response = other->response;
130 msg->responsestr = g_strdup(other->responsestr);
131 msg->method = g_strdup(other->method);
132 msg->target = g_strdup(other->target);
134 list = other->headers;
135 while(list) {
136 struct sipnameval *elem = list->data;
137 sipmsg_add_header_now(msg, elem->name, elem->value);
138 list = list->next;
141 list = other->new_headers;
142 while(list) {
143 struct sipnameval *elem = list->data;
144 sipmsg_add_header(msg, elem->name, elem->value);
145 list = list->next;
148 msg->bodylen = other->bodylen;
149 msg->body = g_strdup(other->body);
150 msg->signature = g_strdup(other->signature);
151 msg->rand = g_strdup(other->rand);
152 msg->num = g_strdup(other->num);
154 return msg;
157 char *sipmsg_to_string(const struct sipmsg *msg) {
158 GSList *cur;
159 GString *outstr = g_string_new("");
160 struct sipnameval *elem;
162 if(msg->response)
163 g_string_append_printf(outstr, "SIP/2.0 %d Unknown\r\n",
164 msg->response);
165 else
166 g_string_append_printf(outstr, "%s %s SIP/2.0\r\n",
167 msg->method, msg->target);
169 cur = msg->headers;
170 while(cur) {
171 elem = cur->data;
172 /*Todo: remove the LFCR in a good way*/
173 /*if(sipe_strequal(elem->name,"Proxy-Authorization"))
174 g_string_append_printf(outstr, "%s: %s", elem->name,
175 elem->value);
176 else */
177 g_string_append_printf(outstr, "%s: %s\r\n", elem->name,
178 elem->value);
179 cur = g_slist_next(cur);
182 g_string_append_printf(outstr, "\r\n%s", msg->bodylen ? msg->body : "");
184 return g_string_free(outstr, FALSE);
188 * Adds header to current message headers
190 void sipmsg_add_header_now(struct sipmsg *msg, const gchar *name, const gchar *value) {
191 struct sipnameval *element = g_new0(struct sipnameval,1);
193 /* SANITY CHECK: the calling code must be fixed if this happens! */
194 if (!value) {
195 SIPE_DEBUG_ERROR("sipmsg_add_header_now: NULL value for %s",
196 name);
197 value = "";
200 element->name = g_strdup(name);
201 element->value = g_strdup(value);
202 msg->headers = g_slist_append(msg->headers, element);
206 * Adds header to separate storage for future merge
208 void sipmsg_add_header(struct sipmsg *msg, const gchar *name, const gchar *value) {
209 struct sipnameval *element = g_new0(struct sipnameval,1);
211 /* SANITY CHECK: the calling code must be fixed if this happens! */
212 if (!value) {
213 SIPE_DEBUG_ERROR("sipmsg_add_header: NULL value for %s", name);
214 value = "";
217 element->name = g_strdup(name);
218 element->value = g_strdup(value);
219 msg->new_headers = g_slist_append(msg->new_headers, element);
223 * Removes header if it's not in keepers array
225 void sipmsg_strip_headers(struct sipmsg *msg, const gchar *keepers[]) {
226 GSList *entry;
227 struct sipnameval *elem;
229 entry = msg->headers;
230 while(entry) {
231 int i = 0;
232 gboolean keeper = FALSE;
234 elem = entry->data;
235 while (keepers[i]) {
236 if (!g_ascii_strcasecmp(elem->name, keepers[i])) {
237 keeper = TRUE;
238 break;
240 i++;
243 if (!keeper) {
244 GSList *to_delete = entry;
245 SIPE_DEBUG_INFO("sipmsg_strip_headers: removing %s", elem->name);
246 entry = g_slist_next(entry);
247 msg->headers = g_slist_delete_link(msg->headers, to_delete);
248 g_free(elem->name);
249 g_free(elem->value);
250 g_free(elem);
251 } else {
252 entry = g_slist_next(entry);
258 * Merges newly added headers to message
260 void sipmsg_merge_new_headers(struct sipmsg *msg) {
261 while(msg->new_headers) {
262 msg->headers = g_slist_append(msg->headers, msg->new_headers->data);
263 msg->new_headers = g_slist_remove(msg->new_headers, msg->new_headers->data);
267 void sipmsg_free(struct sipmsg *msg) {
268 if (msg) {
269 sipe_utils_nameval_free(msg->headers);
270 sipe_utils_nameval_free(msg->new_headers);
271 g_free(msg->signature);
272 g_free(msg->rand);
273 g_free(msg->num);
274 g_free(msg->responsestr);
275 g_free(msg->method);
276 g_free(msg->target);
277 g_free(msg->body);
278 g_free(msg);
282 void sipmsg_remove_header_now(struct sipmsg *msg, const gchar *name) {
283 struct sipnameval *elem;
284 GSList *tmp = msg->headers;
285 while(tmp) {
286 elem = tmp->data;
287 // OCS2005 can send the same header in either all caps or mixed case
288 if (sipe_strcase_equal(elem->name, name)) {
289 msg->headers = g_slist_remove(msg->headers, elem);
290 g_free(elem->name);
291 g_free(elem->value);
292 g_free(elem);
293 return;
295 tmp = g_slist_next(tmp);
297 return;
300 const gchar *sipmsg_find_header(const struct sipmsg *msg, const gchar *name) {
301 return sipe_utils_nameval_find_instance (msg->headers, name, 0);
304 const gchar *sipmsg_find_header_instance(const struct sipmsg *msg, const gchar *name, int which) {
305 return sipe_utils_nameval_find_instance(msg->headers, name, which);
308 gchar *sipmsg_find_part_of_header(const char *hdr, const char * before, const char * after, const char * def) {
309 const char *tmp;
310 const char *tmp2;
311 gchar *res2;
312 if (!hdr) {
313 return NULL;
316 //printf("partof %s w/ %s before and %s after\n", hdr, before, after);
318 tmp = before == NULL ? hdr : strstr(hdr, before);
319 if (!tmp) {
320 //printf ("not found, returning null\n");
321 return (gchar *)def;
324 if (before != NULL) {
325 tmp += strlen(before);
326 //printf ("tmp now %s\n", tmp);
329 if (after != NULL && (tmp2 = strstr(tmp, after))) {
330 gchar * res = g_strndup(tmp, tmp2 - tmp);
331 //printf("returning %s\n", res);
332 return res;
334 res2 = g_strdup(tmp);
335 //printf("returning %s\n", res2);
336 return res2;
339 int sipmsg_parse_cseq(struct sipmsg *msg)
341 int res = -1;
342 gchar **items;
343 items = g_strsplit(sipmsg_find_cseq_header(msg), " ", 1);
344 if (items[0]) {
345 res = atoi(items[0]);
347 g_strfreev(items);
348 return res;
352 * Parse EndPoints header from INVITE request
353 * Returns a list of end points: contact URI plus optional epid.
354 * You must free the values and the list.
356 * Example headers:
357 * EndPoints: "alice alisson" <sip:alice@atlanta.local>, <sip:bob@atlanta.local>;epid=ebca82d94d, <sip:carol@atlanta.local>
358 * EndPoints: "alice, alisson" <sip:alice@atlanta.local>, <sip:bob@atlanta.local>
359 * EndPoints: "alice alisson" <sip:alice@atlanta.local>, "Super, Man" <sip:super@atlanta.local>
361 * @param header (in) EndPoints header contents
363 * @return GSList with struct sipendpoint as elements
365 GSList *sipmsg_parse_endpoints_header(const gchar *header)
367 GSList *list = NULL;
368 gchar **parts = g_strsplit(header, ",", 0);
369 gchar *part;
370 int i;
372 for (i = 0; (part = parts[i]) != NULL; i++) {
373 /* Does the part contain a URI? */
374 gchar *contact = sipmsg_find_part_of_header(part, "<", ">", NULL);
375 if (contact) {
376 struct sipendpoint *end_point = g_new(struct sipendpoint, 1);
377 end_point->contact = contact;
378 end_point->epid = sipmsg_find_part_of_header(part, "epid=", NULL, NULL);
379 list = g_slist_append(list, end_point);
382 g_strfreev(parts);
384 return(list);
387 void sipmsg_parse_p_asserted_identity(const gchar *header, gchar **sip_uri,
388 gchar **tel_uri) {
389 gchar **parts, **p;
391 *sip_uri = NULL;
392 *tel_uri = NULL;
394 if (g_ascii_strncasecmp(header, "tel:", 4) == 0) {
395 *tel_uri = g_strdup(header);
396 return;
399 parts = g_strsplit(header, ",", 0);
401 for (p = parts; *p; p++) {
402 gchar *uri = sipmsg_find_part_of_header(*p, "<", ">", NULL);
403 if (!uri)
404 continue;
406 if (g_ascii_strncasecmp(uri, "sip:", 4) == 0) {
407 if (*sip_uri) {
408 SIPE_DEBUG_WARNING_NOFORMAT("More than one "
409 "sip: URI found in P-Asserted-Identity!");
410 } else {
411 *sip_uri = uri;
412 uri = NULL;
414 } else if (g_ascii_strncasecmp(uri, "tel:", 4) == 0){
415 if (*tel_uri) {
416 SIPE_DEBUG_WARNING_NOFORMAT("More than one "
417 "tel: URI found in P-Asserted-Identity!");
418 } else {
419 *tel_uri = uri;
420 uri = NULL;
424 g_free(uri);
427 g_strfreev(parts);
431 * sipmsg_find_auth_header will return the particular WWW-Authenticate
432 * header specified by *name.
434 * Use this function when you want to look for a specific authentication
435 * method such as NTLM or Kerberos
438 const gchar *sipmsg_find_auth_header(struct sipmsg *msg, const gchar *name) {
439 GSList *tmp;
440 struct sipnameval *elem;
441 int name_len;
443 if (!name) {
444 SIPE_DEBUG_INFO_NOFORMAT("sipmsg_find_auth_header: no authentication scheme specified");
445 return NULL;
448 name_len = strlen(name);
449 tmp = msg->headers;
450 while(tmp) {
451 elem = tmp->data;
452 /* SIPE_DEBUG_INFO("Current header: %s", elem->value); */
453 if (elem && elem->name &&
454 (sipe_strcase_equal(elem->name,"WWW-Authenticate") ||
455 sipe_strcase_equal(elem->name,"Authentication-Info")) ) {
456 if (!g_ascii_strncasecmp((gchar *)elem->value, name, name_len)) {
457 /* SIPE_DEBUG_INFO("elem->value: %s", elem->value); */
458 return elem->value;
461 /* SIPE_DEBUG_INFO_NOFORMAT("moving to next header"); */
462 tmp = g_slist_next(tmp);
464 SIPE_DEBUG_INFO("sipmsg_find_auth_header: '%s' not found", name);
465 return NULL;
469 * Parses headers-like 'msgr' attribute of INVITE's 'ms_text_format' header.
470 * Then retrieves value of 'X-MMS-IM-Format'.
472 * 'msgr' typically looks like:
473 * X-MMS-IM-Format: FN=Microsoft%20Sans%20Serif; EF=BI; CO=800000; CS=0; PF=22
475 static gchar *sipmsg_get_x_mms_im_format(gchar *msgr) {
476 gchar *msgr2;
477 gsize msgr_dec64_len;
478 guchar *msgr_dec64;
479 gchar *msgr_utf8;
480 gchar **lines;
481 gchar **parts;
482 gchar *x_mms_im_format;
483 gchar *tmp;
485 if (!msgr) return NULL;
486 msgr2 = g_strdup(msgr);
487 while (strlen(msgr2) % 4 != 0) {
488 gchar *tmp_msgr2 = msgr2;
489 msgr2 = g_strdup_printf("%s=", msgr2);
490 g_free(tmp_msgr2);
492 msgr_dec64 = g_base64_decode(msgr2, &msgr_dec64_len);
493 msgr_utf8 = g_convert((gchar *) msgr_dec64, msgr_dec64_len, "UTF-8", "UTF-16LE", NULL, NULL, NULL);
494 g_free(msgr_dec64);
495 g_free(msgr2);
496 lines = g_strsplit(msgr_utf8,"\r\n\r\n",0);
497 g_free(msgr_utf8);
498 //@TODO: make extraction like parsing of message headers.
499 parts = g_strsplit(lines[0],"X-MMS-IM-Format:",0);
500 x_mms_im_format = g_strdup(parts[1]);
501 g_strfreev(parts);
502 g_strfreev(lines);
503 tmp = x_mms_im_format;
504 if (x_mms_im_format) {
505 while(*x_mms_im_format==' ' || *x_mms_im_format=='\t') x_mms_im_format++;
507 x_mms_im_format = g_strdup(x_mms_im_format);
508 g_free(tmp);
509 return x_mms_im_format;
512 gchar *sipmsg_get_msgr_string(gchar *x_mms_im_format) {
513 gchar *msgr_orig;
514 gsize msgr_utf16_len;
515 gchar *msgr_utf16;
516 gchar *msgr_enc;
517 gchar *res;
518 int len;
520 if (!x_mms_im_format) return NULL;
521 msgr_orig = g_strdup_printf("X-MMS-IM-Format: %s\r\n\r\n", x_mms_im_format);
522 msgr_utf16 = g_convert(msgr_orig, -1, "UTF-16LE", "UTF-8", NULL, &msgr_utf16_len, NULL);
523 g_free(msgr_orig);
524 msgr_enc = g_base64_encode((guchar *) msgr_utf16, msgr_utf16_len);
525 g_free(msgr_utf16);
526 len = strlen(msgr_enc);
527 while (msgr_enc[len - 1] == '=') len--;
528 res = g_strndup(msgr_enc, len);
529 g_free(msgr_enc);
530 return res;
533 static void msn_parse_format(const char *mime, char **pre_ret, char **post_ret);
536 * Translates X-MMS-IM format to HTML presentation.
538 static gchar *sipmsg_apply_x_mms_im_format(const char *x_mms_im_format, gchar *body) {
539 char *pre, *post;
540 gchar *res;
542 if (!x_mms_im_format) {
543 return body ? g_strdup(body) : NULL;
545 msn_parse_format(x_mms_im_format, &pre, &post);
546 res = g_strdup_printf("%s%s%s", pre ? pre : "", body ? body : "", post ? post : "");
547 g_free(pre);
548 g_free(post);
549 return res;
552 struct html_message_data {
553 gchar *ms_text_format;
554 gchar *body;
555 gboolean preferred;
558 static void get_html_message_mime_cb(gpointer user_data,
559 const GSList *fields,
560 const gchar *body,
561 gsize length)
563 const gchar *type = sipe_utils_nameval_find(fields, "Content-Type");
564 struct html_message_data *data = user_data;
566 if (!data->preferred) {
567 gboolean copy = FALSE;
569 /* preferred formats */
570 if (g_str_has_prefix(type, "text/html") ||
571 g_str_has_prefix(type, "text/rtf")) {
572 copy = TRUE;
573 data->preferred = TRUE;
575 /* fallback format */
576 } else if (g_str_has_prefix(type, "text/plain")) {
577 copy = TRUE;
580 if (copy) {
581 g_free(data->ms_text_format);
582 g_free(data->body);
583 data->ms_text_format = g_strdup(type);
584 data->body = g_strndup(body, length);
589 /* ms-text-format: text/plain; charset=UTF-8;msgr=WAAtAE0...DIADQAKAA0ACgA;ms-body=SGk= */
590 gchar *get_html_message(const gchar *ms_text_format_in, const gchar *body_in)
592 gchar *msgr;
593 gchar *res;
594 gchar *ms_text_format = NULL;
595 gchar *body = NULL;
597 if (g_str_has_prefix(ms_text_format_in, "multipart/related") ||
598 g_str_has_prefix(ms_text_format_in, "multipart/alternative")) {
599 struct html_message_data data = { NULL, NULL, FALSE };
601 sipe_mime_parts_foreach(ms_text_format_in, body_in,
602 get_html_message_mime_cb, &data);
604 ms_text_format = data.ms_text_format;
605 body = data.body;
607 } else {
608 ms_text_format = g_strdup(ms_text_format_in);
609 body = g_strdup(body_in);
612 if (body) {
613 res = body;
614 } else {
615 gchar *tmp = sipmsg_find_part_of_header(ms_text_format, "ms-body=", NULL, NULL);
616 gsize len;
617 if (!tmp) {
618 g_free(ms_text_format);
619 return NULL;
621 res = (gchar *) g_base64_decode(tmp, &len);
622 g_free(tmp);
623 if (!res) {
624 g_free(ms_text_format);
625 return NULL;
629 if (g_str_has_prefix(ms_text_format, "text/html")) {
631 * HTML uses tags for formatting, not line breaks. But
632 * clients still might render them, so we need to remove
633 * them to avoid incorrect text rendering.
635 gchar *d = res;
636 const gchar *s = res;
637 gchar c;
639 /* No ANSI C nor glib function seems to exist for this :-( */
640 while ((c = *s++))
641 if ((c != '\n') && (c != '\r'))
642 *d++ = c;
643 *d = c;
645 } else if (g_str_has_prefix(ms_text_format, "text/rtf")) {
646 char *tmp = res;
647 res = sipe_rtf_to_html(res);
648 g_free(tmp);
649 } else {
650 char *tmp = res;
651 res = g_markup_escape_text(res, -1); // as this is not html
652 g_free(tmp);
655 msgr = sipmsg_find_part_of_header(ms_text_format, "msgr=", ";", NULL);
656 if (msgr) {
657 gchar *x_mms_im_format = sipmsg_get_x_mms_im_format(msgr);
658 gchar *tmp = res;
659 g_free(msgr);
660 res = sipmsg_apply_x_mms_im_format(x_mms_im_format, res);
661 g_free(tmp);
662 g_free(x_mms_im_format);
665 g_free(ms_text_format);
667 return res;
670 static gchar *
671 get_reason(struct sipmsg *msg, const gchar *header)
673 const gchar *diagnostics = sipmsg_find_header(msg, header);
674 if (diagnostics)
675 return sipmsg_find_part_of_header(diagnostics, "reason=\"", "\"", NULL);
677 return NULL;
680 gchar *
681 sipmsg_get_ms_diagnostics_reason(struct sipmsg *msg)
683 return get_reason(msg, "ms-diagnostics");
686 gchar *
687 sipmsg_get_ms_diagnostics_public_reason(struct sipmsg *msg)
689 return get_reason(msg, "ms-diagnostics-public");
693 sipmsg_parse_warning(struct sipmsg *msg, gchar **reason)
696 * Example header:
697 * Warning: 310 lcs.microsoft.com "You are currently not using the recommended version of the client"
699 const gchar *hdr = sipmsg_find_header(msg, "Warning");
700 int code = -1;
702 if (reason)
703 *reason = NULL;
705 if (hdr) {
706 gchar **parts = g_strsplit(hdr, " ", 3);
708 if (parts[0]) {
709 code = atoi(parts[0]);
711 if (reason && parts[1] && parts[2]) {
712 size_t len = strlen(parts[2]);
713 if (len > 2 && parts[2][0] == '"' && parts[2][len - 1] == '"')
714 *reason = g_strndup(parts[2] + 1, len - 2);
718 g_strfreev(parts);
721 return code;
724 const gchar *sipmsg_find_call_id_header(const struct sipmsg *msg) {
725 return(sipmsg_find_header(msg, "Call-ID"));
728 const gchar *sipmsg_find_content_type_header(const struct sipmsg *msg) {
729 return(sipmsg_find_header(msg, "Content-Type"));
732 const gchar *sipmsg_find_cseq_header(const struct sipmsg *msg) {
733 return(sipmsg_find_header(msg, "CSeq"));
736 const gchar *sipmsg_find_event_header(const struct sipmsg *msg) {
737 return(sipmsg_find_header(msg, "Event"));
740 const gchar *sipmsg_find_expires_header(const struct sipmsg *msg) {
741 return(sipmsg_find_header(msg, "Expires"));
744 const gchar *sipmsg_find_from_header(const struct sipmsg *msg) {
745 return(sipmsg_find_header(msg, "From"));
748 const gchar *sipmsg_find_to_header(const struct sipmsg *msg) {
749 return(sipmsg_find_header(msg, "To"));
752 gchar *sipmsg_parse_address_from_header(const struct sipmsg *msg,
753 const gchar *name) {
754 return(parse_from(sipmsg_find_header(msg, name)));
757 gchar *sipmsg_parse_contact_address(const struct sipmsg *msg) {
758 return(sipmsg_parse_address_from_header(msg, "Contact"));
761 gchar *sipmsg_parse_from_address(const struct sipmsg *msg) {
762 return(sipmsg_parse_address_from_header(msg, "From"));
765 gchar *sipmsg_parse_to_address(const struct sipmsg *msg) {
766 return(sipmsg_parse_address_from_header(msg, "To"));
769 void sipmsg_update_to_header_tag(struct sipmsg *msg) {
770 const gchar *old = sipmsg_find_to_header(msg);
771 gchar *tag = gentag();
772 gchar *new = g_strdup_printf("%s;tag=%s", old, tag);
773 g_free(tag);
774 sipmsg_remove_header_now(msg, "To");
775 sipmsg_add_header_now(msg, "To", new);
776 g_free(new);
780 //------------------------------------------------------------------------------------------
781 //TEMP solution to include it here (copy from purple's msn protocol
782 //How to reuse msn's util methods from sipe?
784 /* from internal.h */
785 #define MSG_LEN 2048
786 #define BUF_LEN MSG_LEN
788 void
789 msn_parse_format(const char *mime, char **pre_ret, char **post_ret)
791 char *cur;
792 GString *pre = g_string_new(NULL);
793 GString *post = g_string_new(NULL);
794 unsigned int colors[3];
796 if (pre_ret != NULL) *pre_ret = NULL;
797 if (post_ret != NULL) *post_ret = NULL;
799 cur = strstr(mime, "FN=");
801 if (cur && (*(cur = cur + 3) != ';'))
803 pre = g_string_append(pre, "<FONT FACE=\"");
805 while (*cur && *cur != ';')
807 pre = g_string_append_c(pre, *cur);
808 cur++;
811 pre = g_string_append(pre, "\">");
812 post = g_string_prepend(post, "</FONT>");
815 cur = strstr(mime, "EF=");
817 if (cur && (*(cur = cur + 3) != ';'))
819 while (*cur && *cur != ';')
821 pre = g_string_append_c(pre, '<');
822 pre = g_string_append_c(pre, *cur);
823 pre = g_string_append_c(pre, '>');
824 post = g_string_prepend_c(post, '>');
825 post = g_string_prepend_c(post, *cur);
826 post = g_string_prepend_c(post, '/');
827 post = g_string_prepend_c(post, '<');
828 cur++;
832 cur = strstr(mime, "CO=");
834 if (cur && (*(cur = cur + 3) != ';'))
836 int i;
838 i = sscanf(cur, "%02x%02x%02x;", &colors[0], &colors[1], &colors[2]);
840 if (i > 0)
842 char tag[64];
844 if (i == 1)
846 colors[1] = 0;
847 colors[2] = 0;
849 else if (i == 2)
851 unsigned int temp = colors[0];
853 colors[0] = colors[1];
854 colors[1] = temp;
855 colors[2] = 0;
857 else if (i == 3)
859 unsigned int temp = colors[2];
861 colors[2] = colors[0];
862 colors[0] = temp;
865 /* hh is undefined in mingw's gcc 4.4
866 * https://sourceforge.net/tracker/index.php?func=detail&aid=2818436&group_id=2435&atid=102435
868 g_snprintf(tag, sizeof(tag),
869 "<FONT COLOR=\"#%02x%02x%02x\">",
870 (unsigned char)colors[0], (unsigned char)colors[1], (unsigned char)colors[2]);
872 pre = g_string_append(pre, tag);
873 post = g_string_prepend(post, "</FONT>");
877 cur = strstr(mime, "RL=");
879 if (cur && (*(cur = cur + 3) != ';'))
881 if (*cur == '1')
883 /* RTL text was received */
884 pre = g_string_append(pre, "<SPAN style=\"direction:rtl;text-align:right;\">");
885 post = g_string_prepend(post, "</SPAN>");
889 cur = sipe_utils_uri_unescape(pre->str);
890 g_string_free(pre, TRUE);
892 if (pre_ret != NULL)
893 *pre_ret = cur;
894 else
895 g_free(cur);
897 cur = sipe_utils_uri_unescape(post->str);
898 g_string_free(post, TRUE);
900 if (post_ret != NULL)
901 *post_ret = cur;
902 else
903 g_free(cur);
906 static const char *
907 encode_spaces(const char *str)
909 static char buf[BUF_LEN];
910 const char *c;
911 char *d;
913 g_return_val_if_fail(str != NULL, NULL);
915 for (c = str, d = buf; *c != '\0'; c++)
917 if (*c == ' ')
919 *d++ = '%';
920 *d++ = '2';
921 *d++ = '0';
923 else
924 *d++ = *c;
926 *d = '\0';
928 return buf;
931 void
932 sipe_parse_html(const char *html, char **attributes, char **message)
934 int len, retcount = 0;
935 const char *c;
936 char *msg;
937 char *fontface = NULL;
938 char fonteffect[4];
939 char fontcolor[7];
940 char direction = '0';
942 gboolean has_bold = FALSE;
943 gboolean has_italic = FALSE;
944 gboolean has_underline = FALSE;
945 gboolean has_strikethrough = FALSE;
947 g_return_if_fail(html != NULL);
948 g_return_if_fail(attributes != NULL);
949 g_return_if_fail(message != NULL);
951 #define _HTML_UNESCAPE \
952 if (!g_ascii_strncasecmp(c, "&lt;", 4)) { \
953 msg[retcount++] = '<'; \
954 c += 4; \
955 } else if (!g_ascii_strncasecmp(c, "&gt;", 4)) { \
956 msg[retcount++] = '>'; \
957 c += 4; \
958 } else if (!g_ascii_strncasecmp(c, "&nbsp;", 6)) { \
959 msg[retcount++] = ' '; \
960 c += 6; \
961 } else if (!g_ascii_strncasecmp(c, "&quot;", 6)) { \
962 msg[retcount++] = '"'; \
963 c += 6; \
964 } else if (!g_ascii_strncasecmp(c, "&amp;", 5)) { \
965 msg[retcount++] = '&'; \
966 c += 5; \
967 } else if (!g_ascii_strncasecmp(c, "&apos;", 6)) { \
968 msg[retcount++] = '\''; \
969 c += 6; \
970 } else { \
971 msg[retcount++] = *c++; \
974 len = strlen(html);
975 msg = g_malloc0(len + 1);
977 memset(fontcolor, 0, sizeof(fontcolor));
978 strcat(fontcolor, "0");
979 memset(fonteffect, 0, sizeof(fonteffect));
981 for (c = html; *c != '\0';)
983 if (*c == '<')
985 if (!g_ascii_strncasecmp(c + 1, "br>", 3))
987 msg[retcount++] = '\r';
988 msg[retcount++] = '\n';
989 c += 4;
991 else if (!g_ascii_strncasecmp(c + 1, "div>", 4))
993 msg[retcount++] = '\r';
994 msg[retcount++] = '\n';
995 c += 5;
996 if (!g_ascii_strncasecmp(c, "<br></div>", 10)) {
997 /* This is an empty paragraph; replace it with
998 * one line break. */
999 c += 10;
1002 else if (!g_ascii_strncasecmp(c + 1, "i>", 2))
1004 if (!has_italic)
1006 strcat(fonteffect, "I");
1007 has_italic = TRUE;
1009 c += 3;
1011 else if (!g_ascii_strncasecmp(c + 1, "b>", 2))
1013 if (!has_bold)
1015 strcat(fonteffect, "B");
1016 has_bold = TRUE;
1018 c += 3;
1020 else if (!g_ascii_strncasecmp(c + 1, "u>", 2))
1022 if (!has_underline)
1024 strcat(fonteffect, "U");
1025 has_underline = TRUE;
1027 c += 3;
1029 else if (!g_ascii_strncasecmp(c + 1, "s>", 2))
1031 if (!has_strikethrough)
1033 strcat(fonteffect, "S");
1034 has_strikethrough = TRUE;
1036 c += 3;
1038 else if (!g_ascii_strncasecmp(c + 1, "a href=\"", 8))
1040 c += 9;
1042 if (!g_ascii_strncasecmp(c, "mailto:", 7))
1043 c += 7;
1045 while ((*c != '\0') && g_ascii_strncasecmp(c, "\">", 2))
1046 if (*c == '&') {
1047 _HTML_UNESCAPE;
1048 } else
1049 msg[retcount++] = *c++;
1051 if (*c != '\0')
1052 c += 2;
1054 /* ignore descriptive string */
1055 while ((*c != '\0') && g_ascii_strncasecmp(c, "</a>", 4))
1056 c++;
1058 if (*c != '\0')
1059 c += 4;
1061 else if (!g_ascii_strncasecmp(c + 1, "span", 4))
1063 /* Bi-directional text support using CSS properties in span tags */
1064 c += 5;
1066 while (*c != '\0' && *c != '>')
1068 while (*c == ' ')
1069 c++;
1070 if (!g_ascii_strncasecmp(c, "dir=\"rtl\"", 9))
1072 c += 9;
1073 direction = '1';
1075 else if (!g_ascii_strncasecmp(c, "style=\"", 7))
1077 /* Parse inline CSS attributes */
1078 int attr_len = 0;
1079 c += 7;
1080 while (*(c + attr_len) != '\0' && *(c + attr_len) != '"')
1081 attr_len++;
1082 if (*(c + attr_len) == '"')
1084 char *css_attributes;
1085 char *attr_dir;
1086 css_attributes = g_strndup(c, attr_len);
1087 attr_dir = sipe_backend_markup_css_property(css_attributes, "direction");
1088 g_free(css_attributes);
1089 if (attr_dir && (!g_ascii_strncasecmp(attr_dir, "RTL", 3)))
1090 direction = '1';
1091 g_free(attr_dir);
1095 else
1097 c++;
1100 if (*c == '>')
1101 c++;
1103 else if (!g_ascii_strncasecmp(c + 1, "font", 4))
1105 c += 5;
1107 while ((*c != '\0') && !g_ascii_strncasecmp(c, " ", 1))
1108 c++;
1110 if (!g_ascii_strncasecmp(c, "color=\"#", 7))
1112 c += 8;
1114 fontcolor[0] = *(c + 4);
1115 fontcolor[1] = *(c + 5);
1116 fontcolor[2] = *(c + 2);
1117 fontcolor[3] = *(c + 3);
1118 fontcolor[4] = *c;
1119 fontcolor[5] = *(c + 1);
1121 c += 8;
1123 else if (!g_ascii_strncasecmp(c, "face=\"", 6))
1125 const char *end = NULL;
1126 const char *comma = NULL;
1127 unsigned int namelen = 0;
1129 c += 6;
1130 end = strchr(c, '\"');
1131 comma = strchr(c, ',');
1133 if (comma == NULL || comma > end)
1134 namelen = (unsigned int)(end - c);
1135 else
1136 namelen = (unsigned int)(comma - c);
1138 g_free(fontface);
1139 fontface = g_strndup(c, namelen);
1140 c = end + 2;
1142 else
1144 /* Drop all unrecognized/misparsed font tags */
1145 while ((*c != '\0') && g_ascii_strncasecmp(c, "\">", 2))
1146 c++;
1148 if (*c != '\0')
1149 c += 2;
1152 else
1154 while ((*c != '\0') && (*c != '>'))
1155 c++;
1156 if (*c != '\0')
1157 c++;
1160 else if (*c == '&')
1162 _HTML_UNESCAPE;
1164 else
1165 msg[retcount++] = *c++;
1168 if (fontface == NULL)
1169 fontface = g_strdup("MS Sans Serif");
1171 *attributes = g_strdup_printf("FN=%s; EF=%s; CO=%s; PF=0; RL=%c",
1172 encode_spaces(fontface),
1173 fonteffect, fontcolor, direction);
1174 *message = msg;
1176 g_free(fontface);
1178 #undef _HTML_UNESCAPE
1180 // End of TEMP
1183 Local Variables:
1184 mode: c
1185 c-file-style: "bsd"
1186 indent-tabs-mode: t
1187 tab-width: 8
1188 End: