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
33 #include "sipe-backend.h"
34 #include "sipe-mime.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");
45 line
= g_strndup(msg
, tmp
- msg
);
47 smsg
= sipmsg_parse_header(line
);
48 smsg
->body
= g_strdup(tmp
+ 4);
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);
58 const gchar
*contentlength
;
64 parts
= g_strsplit(lines
[0], " ", 3);
65 if(!parts
[0] || !parts
[1] || !parts
[2]) {
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]);
80 if (sipe_utils_parse_lines(&msg
->headers
, lines
+ 1, ":") == FALSE
) {
86 contentlength
= sipmsg_find_header(msg
, "Content-Length");
88 msg
->bodylen
= strtol(contentlength
,NULL
,10);
90 const gchar
*tmp
= sipmsg_find_header(msg
, "Transfer-Encoding");
91 if (tmp
&& sipe_strcase_equal(tmp
, "chunked")) {
92 msg
->bodylen
= SIPMSG_BODYLEN_CHUNKED
;
94 tmp
= sipmsg_find_content_type_header(msg
);
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
;
112 tmp
= sipmsg_find_cseq_header(msg
);
114 /* SHOULD NOT HAPPEN */
117 parts
= g_strsplit(tmp
, " ", 2);
118 msg
->method
= g_strdup(parts
[1]);
125 struct sipmsg
*sipmsg_copy(const struct sipmsg
*other
) {
126 struct sipmsg
*msg
= g_new0(struct sipmsg
, 1);
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
;
136 struct sipnameval
*elem
= list
->data
;
137 sipmsg_add_header_now(msg
, elem
->name
, elem
->value
);
141 list
= other
->new_headers
;
143 struct sipnameval
*elem
= list
->data
;
144 sipmsg_add_header(msg
, elem
->name
, elem
->value
);
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
);
157 char *sipmsg_to_string(const struct sipmsg
*msg
) {
159 GString
*outstr
= g_string_new("");
160 struct sipnameval
*elem
;
163 g_string_append_printf(outstr
, "SIP/2.0 %d Unknown\r\n",
166 g_string_append_printf(outstr
, "%s %s SIP/2.0\r\n",
167 msg
->method
, msg
->target
);
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,
177 g_string_append_printf(outstr
, "%s: %s\r\n", elem
->name
,
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! */
195 SIPE_DEBUG_ERROR("sipmsg_add_header_now: NULL value for %s",
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! */
213 SIPE_DEBUG_ERROR("sipmsg_add_header: NULL value for %s", name
);
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
[]) {
227 struct sipnameval
*elem
;
229 entry
= msg
->headers
;
232 gboolean keeper
= FALSE
;
236 if (!g_ascii_strcasecmp(elem
->name
, keepers
[i
])) {
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
);
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
) {
269 sipe_utils_nameval_free(msg
->headers
);
270 sipe_utils_nameval_free(msg
->new_headers
);
271 g_free(msg
->signature
);
274 g_free(msg
->responsestr
);
282 void sipmsg_remove_header_now(struct sipmsg
*msg
, const gchar
*name
) {
283 struct sipnameval
*elem
;
284 GSList
*tmp
= msg
->headers
;
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
);
295 tmp
= g_slist_next(tmp
);
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
) {
316 //printf("partof %s w/ %s before and %s after\n", hdr, before, after);
318 tmp
= before
== NULL
? hdr
: strstr(hdr
, before
);
320 //printf ("not found, returning null\n");
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);
334 res2
= g_strdup(tmp
);
335 //printf("returning %s\n", res2);
339 int sipmsg_parse_cseq(struct sipmsg
*msg
)
343 items
= g_strsplit(sipmsg_find_cseq_header(msg
), " ", 1);
345 res
= atoi(items
[0]);
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.
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
)
368 gchar
**parts
= g_strsplit(header
, ",", 0);
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
);
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
);
387 void sipmsg_parse_p_asserted_identity(const gchar
*header
, gchar
**sip_uri
,
394 if (g_ascii_strncasecmp(header
, "tel:", 4) == 0) {
395 *tel_uri
= g_strdup(header
);
399 parts
= g_strsplit(header
, ",", 0);
401 for (p
= parts
; *p
; p
++) {
402 gchar
*uri
= sipmsg_find_part_of_header(*p
, "<", ">", NULL
);
406 if (g_ascii_strncasecmp(uri
, "sip:", 4) == 0) {
408 SIPE_DEBUG_WARNING_NOFORMAT("More than one "
409 "sip: URI found in P-Asserted-Identity!");
414 } else if (g_ascii_strncasecmp(uri
, "tel:", 4) == 0){
416 SIPE_DEBUG_WARNING_NOFORMAT("More than one "
417 "tel: URI found in P-Asserted-Identity!");
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
) {
440 struct sipnameval
*elem
;
444 SIPE_DEBUG_INFO_NOFORMAT("sipmsg_find_auth_header: no authentication scheme specified");
448 name_len
= strlen(name
);
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); */
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
);
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
) {
477 gsize msgr_dec64_len
;
482 gchar
*x_mms_im_format
;
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
);
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
);
496 lines
= g_strsplit(msgr_utf8
,"\r\n\r\n",0);
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]);
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
);
509 return x_mms_im_format
;
512 gchar
*sipmsg_get_msgr_string(gchar
*x_mms_im_format
) {
514 gsize msgr_utf16_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
);
524 msgr_enc
= g_base64_encode((guchar
*) msgr_utf16
, msgr_utf16_len
);
526 len
= strlen(msgr_enc
);
527 while (msgr_enc
[len
- 1] == '=') len
--;
528 res
= g_strndup(msgr_enc
, len
);
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
) {
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
: "");
552 struct html_message_data
{
553 gchar
*ms_text_format
;
558 static void get_html_message_mime_cb(gpointer user_data
,
559 const GSList
*fields
,
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")) {
573 data
->preferred
= TRUE
;
575 /* fallback format */
576 } else if (g_str_has_prefix(type
, "text/plain")) {
581 g_free(data
->ms_text_format
);
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
)
594 gchar
*ms_text_format
= 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
;
608 ms_text_format
= g_strdup(ms_text_format_in
);
609 body
= g_strdup(body_in
);
615 gchar
*tmp
= sipmsg_find_part_of_header(ms_text_format
, "ms-body=", NULL
, NULL
);
618 g_free(ms_text_format
);
621 res
= (gchar
*) g_base64_decode(tmp
, &len
);
624 g_free(ms_text_format
);
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.
636 const gchar
*s
= res
;
639 /* No ANSI C nor glib function seems to exist for this :-( */
641 if ((c
!= '\n') && (c
!= '\r'))
645 } else if (g_str_has_prefix(ms_text_format
, "text/rtf")) {
647 res
= sipe_rtf_to_html(res
);
651 res
= g_markup_escape_text(res
, -1); // as this is not html
655 msgr
= sipmsg_find_part_of_header(ms_text_format
, "msgr=", ";", NULL
);
657 gchar
*x_mms_im_format
= sipmsg_get_x_mms_im_format(msgr
);
660 res
= sipmsg_apply_x_mms_im_format(x_mms_im_format
, res
);
662 g_free(x_mms_im_format
);
665 g_free(ms_text_format
);
671 get_reason(struct sipmsg
*msg
, const gchar
*header
)
673 const gchar
*diagnostics
= sipmsg_find_header(msg
, header
);
675 return sipmsg_find_part_of_header(diagnostics
, "reason=\"", "\"", NULL
);
681 sipmsg_get_ms_diagnostics_reason(struct sipmsg
*msg
)
683 return get_reason(msg
, "ms-diagnostics");
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
)
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");
706 gchar
**parts
= g_strsplit(hdr
, " ", 3);
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);
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
,
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
);
774 sipmsg_remove_header_now(msg
, "To");
775 sipmsg_add_header_now(msg
, "To", 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 */
786 #define BUF_LEN MSG_LEN
789 msn_parse_format(const char *mime
, char **pre_ret
, char **post_ret
)
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
);
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
, '<');
832 cur
= strstr(mime
, "CO=");
834 if (cur
&& (*(cur
= cur
+ 3) != ';'))
838 i
= sscanf(cur
, "%02x%02x%02x;", &colors
[0], &colors
[1], &colors
[2]);
851 unsigned int temp
= colors
[0];
853 colors
[0] = colors
[1];
859 unsigned int temp
= colors
[2];
861 colors
[2] = colors
[0];
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) != ';'))
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
);
897 cur
= sipe_utils_uri_unescape(post
->str
);
898 g_string_free(post
, TRUE
);
900 if (post_ret
!= NULL
)
907 encode_spaces(const char *str
)
909 static char buf
[BUF_LEN
];
913 g_return_val_if_fail(str
!= NULL
, NULL
);
915 for (c
= str
, d
= buf
; *c
!= '\0'; c
++)
932 sipe_parse_html(const char *html
, char **attributes
, char **message
)
934 int len
, retcount
= 0;
937 char *fontface
= NULL
;
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, "<", 4)) { \
953 msg[retcount++] = '<'; \
955 } else if (!g_ascii_strncasecmp(c, ">", 4)) { \
956 msg[retcount++] = '>'; \
958 } else if (!g_ascii_strncasecmp(c, " ", 6)) { \
959 msg[retcount++] = ' '; \
961 } else if (!g_ascii_strncasecmp(c, """, 6)) { \
962 msg[retcount++] = '"'; \
964 } else if (!g_ascii_strncasecmp(c, "&", 5)) { \
965 msg[retcount++] = '&'; \
967 } else if (!g_ascii_strncasecmp(c, "'", 6)) { \
968 msg[retcount++] = '\''; \
971 msg[retcount++] = *c++; \
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';)
985 if (!g_ascii_strncasecmp(c
+ 1, "br>", 3))
987 msg
[retcount
++] = '\r';
988 msg
[retcount
++] = '\n';
991 else if (!g_ascii_strncasecmp(c
+ 1, "div>", 4))
993 msg
[retcount
++] = '\r';
994 msg
[retcount
++] = '\n';
996 if (!g_ascii_strncasecmp(c
, "<br></div>", 10)) {
997 /* This is an empty paragraph; replace it with
1002 else if (!g_ascii_strncasecmp(c
+ 1, "i>", 2))
1006 strcat(fonteffect
, "I");
1011 else if (!g_ascii_strncasecmp(c
+ 1, "b>", 2))
1015 strcat(fonteffect
, "B");
1020 else if (!g_ascii_strncasecmp(c
+ 1, "u>", 2))
1024 strcat(fonteffect
, "U");
1025 has_underline
= TRUE
;
1029 else if (!g_ascii_strncasecmp(c
+ 1, "s>", 2))
1031 if (!has_strikethrough
)
1033 strcat(fonteffect
, "S");
1034 has_strikethrough
= TRUE
;
1038 else if (!g_ascii_strncasecmp(c
+ 1, "a href=\"", 8))
1042 if (!g_ascii_strncasecmp(c
, "mailto:", 7))
1045 while ((*c
!= '\0') && g_ascii_strncasecmp(c
, "\">", 2))
1049 msg
[retcount
++] = *c
++;
1054 /* ignore descriptive string */
1055 while ((*c
!= '\0') && g_ascii_strncasecmp(c
, "</a>", 4))
1061 else if (!g_ascii_strncasecmp(c
+ 1, "span", 4))
1063 /* Bi-directional text support using CSS properties in span tags */
1066 while (*c
!= '\0' && *c
!= '>')
1070 if (!g_ascii_strncasecmp(c
, "dir=\"rtl\"", 9))
1075 else if (!g_ascii_strncasecmp(c
, "style=\"", 7))
1077 /* Parse inline CSS attributes */
1080 while (*(c
+ attr_len
) != '\0' && *(c
+ attr_len
) != '"')
1082 if (*(c
+ attr_len
) == '"')
1084 char *css_attributes
;
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)))
1103 else if (!g_ascii_strncasecmp(c
+ 1, "font", 4))
1107 while ((*c
!= '\0') && !g_ascii_strncasecmp(c
, " ", 1))
1110 if (!g_ascii_strncasecmp(c
, "color=\"#", 7))
1114 fontcolor
[0] = *(c
+ 4);
1115 fontcolor
[1] = *(c
+ 5);
1116 fontcolor
[2] = *(c
+ 2);
1117 fontcolor
[3] = *(c
+ 3);
1119 fontcolor
[5] = *(c
+ 1);
1123 else if (!g_ascii_strncasecmp(c
, "face=\"", 6))
1125 const char *end
= NULL
;
1126 const char *comma
= NULL
;
1127 unsigned int namelen
= 0;
1130 end
= strchr(c
, '\"');
1131 comma
= strchr(c
, ',');
1133 if (comma
== NULL
|| comma
> end
)
1134 namelen
= (unsigned int)(end
- c
);
1136 namelen
= (unsigned int)(comma
- c
);
1139 fontface
= g_strndup(c
, namelen
);
1144 /* Drop all unrecognized/misparsed font tags */
1145 while ((*c
!= '\0') && g_ascii_strncasecmp(c
, "\">", 2))
1154 while ((*c
!= '\0') && (*c
!= '>'))
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
);
1178 #undef _HTML_UNESCAPE