6 * Copyright (C) 2009-2015 SIPE Project <http://sipe.sourceforge.net/>
8 * This program is free software; you can redistribute it and/or modify
9 * it under the terms of the GNU General Public License as published by
10 * the Free Software Foundation; either version 2 of the License, or
11 * (at your option) any later version.
13 * This program is distributed in the hope that it will be useful,
14 * but WITHOUT ANY WARRANTY; without even the implied warranty of
15 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
16 * GNU General Public License for more details.
18 * You should have received a copy of the GNU General Public License
19 * along with this program; if not, write to the Free Software
20 * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
31 #include "sipe-backend.h"
32 #include "sipe-core.h" /* to ensure same API for backends */
33 #include "sipe-core-private.h"
34 #include "sipe-utils.h"
37 /* Generate 16 random bits */
38 #define RANDOM16BITS (rand() & 0xFFFF)
40 gchar
*gencallid(void)
42 return g_strdup_printf("%04Xg%04Xa%04Xi%04Xm%04Xt%04Xb%04Xx%04Xx",
43 RANDOM16BITS
, RANDOM16BITS
, RANDOM16BITS
,
44 RANDOM16BITS
, RANDOM16BITS
, RANDOM16BITS
,
45 RANDOM16BITS
, RANDOM16BITS
);
50 return g_strdup_printf("%04d%04d", RANDOM16BITS
, RANDOM16BITS
);
53 gchar
*genconfid(void)
55 return g_strdup_printf("%04X%04X%04X%04X%04X%04X%04X%04X",
56 RANDOM16BITS
, RANDOM16BITS
, RANDOM16BITS
,
57 RANDOM16BITS
, RANDOM16BITS
, RANDOM16BITS
,
58 RANDOM16BITS
, RANDOM16BITS
);
61 gchar
*get_contact(const struct sipe_core_private
*sipe_private
)
63 return g_strdup(sipe_private
->contact
);
66 gchar
*parse_from(const gchar
*hdr
)
69 const gchar
*tmp
, *tmp2
= hdr
;
71 if (!hdr
) return NULL
;
72 SIPE_DEBUG_INFO("parsing address out of %s", hdr
);
73 tmp
= strchr(hdr
, '<');
75 /* i hate the different SIP UA behaviours... */
76 if (tmp
) { /* sip address in <...> */
78 tmp
= strchr(tmp2
, '>');
80 from
= g_strndup(tmp2
, tmp
- tmp2
);
82 SIPE_DEBUG_INFO_NOFORMAT("found < without > in From");
86 tmp
= strchr(tmp2
, ';');
88 from
= g_strndup(tmp2
, tmp
- tmp2
);
90 from
= g_strdup(tmp2
);
93 SIPE_DEBUG_INFO("got %s", from
);
97 gchar
*sip_uri_from_name(const gchar
*name
)
99 return(g_strdup_printf("sip:%s", name
));
102 gchar
*sip_uri(const gchar
*string
)
104 return(strstr(string
, "sip:") ? g_strdup(string
) : sip_uri_from_name(string
));
107 static gchar
*escape_uri_part(const gchar
*in
, guint len
)
109 gchar
*escaped
= NULL
;
114 /* reserve space for worst case, i.e. every character needs escaping */
115 escaped
= s
= g_malloc(3 * len
+ 1);
119 /* only allow ASCII characters */
126 * RFC 3986 Appendix A
128 * authority = [ userinfo "@" ] host [ ":" port ]
129 * userinfo = *( unreserved / pct-encoded / sub-delims / ":" )
130 * host = IP-literal / IPv4address / reg-name
131 * reg-name = *( unreserved / pct-encoded / sub-delims )
132 * pct-encoded = "%" HEXDIG HEXDIG
133 * unreserved = ALPHA / DIGIT / "-" / "." / "_" / "~"
135 * Escape everything that isn't in "unreserved"
144 sprintf(s
, "%%%1X%1X", c
/ 16, c
% 16);
154 gchar
*sip_uri_if_valid(const gchar
*string
)
156 /* strip possible sip: prefix */
157 const gchar
*uri
= sipe_get_no_sip_uri(string
);
159 gchar
*result
= NULL
;
161 /* only XXX@YYY is valid */
162 if (uri
&& ((at
= strchr(uri
, '@')) != NULL
)) {
163 gchar
*userinfo
= escape_uri_part(uri
, at
- uri
);
166 gchar
*host
= escape_uri_part(at
+ 1, strlen(at
+ 1));
169 /* name is valid for URI, convert it */
170 result
= g_strdup_printf("sip:%s@%s",
182 const gchar
*sipe_get_no_sip_uri(const gchar
*sip_uri
)
184 #define SIP_PREFIX "sip:"
186 if (!sip_uri
) return NULL
;
188 if (g_str_has_prefix(sip_uri
, SIP_PREFIX
)) {
189 return(sip_uri
+ strlen(SIP_PREFIX
));
196 get_epid(struct sipe_core_private
*sipe_private
)
198 if (!sipe_private
->epid
) {
199 gchar
*self_sip_uri
= sip_uri_self(sipe_private
);
200 sipe_private
->epid
= sipe_get_epid(self_sip_uri
,
202 sipe_backend_network_ip_address(SIPE_CORE_PUBLIC
));
203 g_free(self_sip_uri
);
205 return g_strdup(sipe_private
->epid
);
208 gchar
*get_uuid(struct sipe_core_private
*sipe_private
)
210 gchar
*epid
= get_epid(sipe_private
);
211 gchar
*uuid
= generateUUIDfromEPID(epid
);
218 sipe_get_pub_instance(struct sipe_core_private
*sipe_private
,
222 gchar
*epid
= get_epid(sipe_private
);
224 sscanf(epid
, "%08x", &res
);
227 if (publication_key
== SIPE_PUB_DEVICE
) {
229 } else if (publication_key
== SIPE_PUB_STATE_MACHINE
) { /* First hexadecimal digit is 0x3 */
230 res
= (res
>> 4) | 0x30000000;
231 } else if (publication_key
== SIPE_PUB_STATE_USER
) {
232 res
= 0x20000000; /* fixed */
233 } else if (publication_key
== SIPE_PUB_STATE_CALENDAR
) { /* First hexadecimal digit is 0x4 */
234 res
= (res
>> 4) | 0x40000000;
235 } else if (publication_key
== SIPE_PUB_STATE_CALENDAR_OOF
) { /* First hexadecimal digit is 0x5 */
236 res
= (res
>> 4) | 0x50000000;
237 } else if (publication_key
== SIPE_PUB_CALENDAR_DATA
||
238 publication_key
== SIPE_PUB_NOTE_OOF
)
239 { /* First hexadecimal digit is 0x4 */
240 unsigned calendar_id
= 0;
241 char *mail_hash
= sipe_get_epid(sipe_private
->email
, "", "");
243 sscanf(mail_hash
, "%08x", &calendar_id
);
245 res
= (calendar_id
>> 4) | 0x40000000;
246 } else if (publication_key
== SIPE_PUB_STATE_PHONE_VOIP
) { /* First hexadecimal digit is 0x8 */
247 res
= (res
>> 4) | 0x80000000;
254 sipe_is_bad_alias(const char *uri
,
258 gboolean result
= FALSE
;
260 if (!uri
) return FALSE
;
261 if (!alias
) return TRUE
;
263 if (g_str_has_prefix(alias
, "sip:") || g_str_has_prefix(alias
, "sips:")) return TRUE
;
265 /* check if alias is just SIP URI but without 'sip:' prefix */
266 uri_alias
= sip_uri_from_name(alias
);
267 if (sipe_strcase_equal(uri
, uri_alias
)) {
276 is_empty(const char *st
)
278 if (!st
|| strlen(st
) == 0)
282 /* suspicious leading or trailing spaces */
283 else if (isspace((unsigned char) *st
) ||
284 isspace((unsigned char) *(st
+ strlen(st
) - 1)))
286 /* to not modify original string */
287 char *dup
= g_strdup(st
);
288 if (strlen(g_strstrip(dup
)) == 0) {
297 void sipe_utils_message_debug(const gchar
*type
,
302 if (sipe_backend_debug_enabled()) {
303 GString
*str
= g_string_new("");
306 const char *marker
= sending
?
311 g_get_current_time(&currtime
);
312 time_str
= g_time_val_to_iso8601(&currtime
);
313 g_string_append_printf(str
, "\nMESSAGE START %s %s - %s\n", marker
, type
, time_str
);
314 g_string_append(str
, tmp
= sipe_utils_str_replace(header
, "\r\n", "\n"));
316 g_string_append(str
, "\n");
318 g_string_append(str
, tmp
= sipe_utils_str_replace(body
, "\r\n", "\n"));
320 g_string_append(str
, "\n");
322 g_string_append_printf(str
, "MESSAGE END %s %s - %s", marker
, type
, time_str
);
324 SIPE_DEBUG_INFO_NOFORMAT(str
->str
);
325 g_string_free(str
, TRUE
);
330 sipe_strequal(const gchar
*left
, const gchar
*right
)
332 #if GLIB_CHECK_VERSION(2,16,0)
333 return (g_strcmp0(left
, right
) == 0);
335 return ((left
== NULL
&& right
== NULL
) ||
336 (left
!= NULL
&& right
!= NULL
&& strcmp(left
, right
) == 0));
341 sipe_strcase_equal(const gchar
*left
, const gchar
*right
)
343 return ((left
== NULL
&& right
== NULL
) ||
344 (left
!= NULL
&& right
!= NULL
&& g_ascii_strcasecmp(left
, right
) == 0));
347 gint
sipe_strcompare(gconstpointer a
, gconstpointer b
)
349 #if GLIB_CHECK_VERSION(2,16,0)
350 return (g_strcmp0(a
, b
));
361 sipe_utils_str_to_time(const gchar
*timestamp
)
364 gboolean success
= FALSE
;
366 /* g_time_val_from_iso8601() warns about NULL pointer */
370 /* We have to make sure that the ISO8601 contains a time zone offset,
371 otherwise the time is interpreted as local time, not UTC!
372 @TODO: is there a better way to check this? */
373 if (((len
= strlen(timestamp
)) > 0) &&
374 isdigit(timestamp
[len
-1])) {
375 gchar
*tmp
= g_strdup_printf("%sZ", timestamp
);
376 success
= g_time_val_from_iso8601(tmp
, &time
);
379 success
= g_time_val_from_iso8601(timestamp
, &time
);
384 SIPE_DEBUG_ERROR("sipe_utils_str_to_time: failed to parse ISO8601 string '%s'",
385 timestamp
? timestamp
: "");
393 sipe_utils_time_to_str(time_t timestamp
)
395 GTimeVal time
= { timestamp
, 0 };
396 return g_time_val_to_iso8601(&time
);
399 const gchar
*sipe_utils_time_to_debug_str(const struct tm
*tm
)
401 gchar
*buffer
= asctime(tm
);
407 /* asctime() appends "\n" to the resulting string -> strip it */
408 length
= strlen(buffer
);
410 buffer
[length
- 1] = '\0';
416 hex_str_to_buff(const char *hex_str
, guint8
**buff
)
423 if (!hex_str
) return 0;
425 length
= strlen(hex_str
)/2;
426 *buff
= (unsigned char *)g_malloc(length
);
427 for (i
= 0; i
< length
; i
++) {
428 two_digits
[0] = hex_str
[i
* 2];
429 two_digits
[1] = hex_str
[i
* 2 + 1];
430 two_digits
[2] = '\0';
431 (*buff
)[i
] = (unsigned char)strtoul(two_digits
, NULL
, 16);
438 buff_to_hex_str(const guint8
*buff
, const size_t buff_len
)
443 if (!buff
) return NULL
;
445 res
= g_malloc(buff_len
* 2 + 1);
446 for (i
= 0, j
= 0; i
< buff_len
; i
++, j
+=2) {
447 sprintf(&res
[j
], "%02X", buff
[i
]);
454 sipe_utils_parse_lines(GSList
**list
, gchar
**lines
, gchar
*delimiter
)
462 for(i
= 0; lines
[i
] && strlen(lines
[i
]) > 2; i
++) {
463 parts
= g_strsplit(lines
[i
], delimiter
, 2);
464 if(!parts
[0] || !parts
[1]) {
470 while(*dummy
==' ' || *dummy
=='\t') dummy
++;
471 dummy2
= g_strdup(dummy
);
472 while(lines
[i
+1] && (lines
[i
+1][0]==' ' || lines
[i
+1][0]=='\t')) {
475 while(*dummy
==' ' || *dummy
=='\t') dummy
++;
476 tmp
= g_strdup_printf("%s %s",dummy2
, dummy
);
480 *list
= sipe_utils_nameval_add(*list
, parts
[0], dummy2
);
489 sipe_utils_nameval_add(GSList
* list
, const gchar
*name
, const gchar
*value
)
491 struct sipnameval
*element
= g_new0(struct sipnameval
,1);
493 /* SANITY CHECK: the calling code must be fixed if this happens! */
495 SIPE_DEBUG_ERROR("sipe_utils_nameval_add: NULL value for %s",
500 element
->name
= g_strdup(name
);
501 element
->value
= g_strdup(value
);
502 return g_slist_append(list
, element
);
506 sipe_utils_nameval_free(GSList
*list
) {
507 struct sipnameval
*elem
;
510 list
= g_slist_remove(list
,elem
);
518 sipe_utils_nameval_find(const GSList
*list
, const gchar
*name
)
520 return sipe_utils_nameval_find_instance (list
, name
, 0);
524 sipe_utils_nameval_find_instance(const GSList
*list
, const gchar
*name
, int which
)
527 struct sipnameval
*elem
;
532 // OCS2005 can send the same header in either all caps or mixed case
533 if (sipe_strcase_equal(elem
->name
, name
)) {
539 tmp
= g_slist_next(tmp
);
544 gchar
*sipe_utils_str_replace(const gchar
*string
,
545 const gchar
*delimiter
,
546 const gchar
*replacement
)
551 if (!string
|| !delimiter
|| !replacement
) return NULL
;
553 split
= g_strsplit(string
, delimiter
, 0);
554 result
= g_strjoinv(replacement
, split
);
560 void sipe_utils_shrink_buffer(struct sipe_transport_connection
*conn
,
563 conn
->buffer_used
-= unread
- conn
->buffer
;
564 /* string terminator is not included in buffer_used */
565 memmove(conn
->buffer
, unread
, conn
->buffer_used
+ 1);
568 gboolean
sipe_utils_ip_is_private(const char *ip
)
570 return g_str_has_prefix(ip
, "10.") ||
571 g_str_has_prefix(ip
, "172.16.") ||
572 g_str_has_prefix(ip
, "192.168.");
575 gchar
*sipe_utils_presence_key(const gchar
*uri
)
577 return g_strdup_printf("<presence><%s>", uri
);
581 sipe_utils_uri_unescape(const gchar
*string
)
589 #if GLIB_CHECK_VERSION(2,16,0)
590 unescaped
= g_uri_unescape_string(string
, NULL
);
592 // based on libpurple/util.c:purple_url_decode()
594 GString
*buf
= g_string_new(NULL
);
595 size_t len
= strlen(string
);
603 if ((len
>= 2) && (c
== '%')) {
604 strncpy(hex
, string
, 2);
605 c
= strtol(hex
, NULL
, 16);
611 g_string_append_c(buf
, c
);
614 unescaped
= g_string_free(buf
, FALSE
);
617 if (unescaped
&& !g_utf8_validate(unescaped
, -1, (const gchar
**)&tmp
))
623 GSList
*sipe_utils_slist_insert_unique_sorted(GSList
*list
,
626 GDestroyNotify destroy
)
628 if (g_slist_find_custom(list
, data
, func
)) {
634 /* unique: list takes ownership of "data" */
635 return(g_slist_insert_sorted(list
, data
, func
));
639 void sipe_utils_slist_free_full(GSList
*list
,
642 #if GLIB_CHECK_VERSION(2,28,0)
643 g_slist_free_full(list
, free
);
645 GSList
*entry
= list
;
647 (*free
)(entry
->data
);