media: mux RTP and RTCP when using TCP connection
[siplcs.git] / src / core / sipe-utils.c
blobf915ce573ebedbf38a4e0ec6da6b71dc7e16d3ee
1 /**
2 * @file sipe-utils.c
4 * pidgin-sipe
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
23 #include <stdlib.h>
24 #include <stdio.h>
25 #include <string.h>
26 #include <ctype.h>
27 #include <time.h>
29 #include <glib.h>
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"
35 #include "uuid.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);
48 gchar *gentag(void)
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)
68 gchar *from;
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 <...> */
77 tmp2 = tmp + 1;
78 tmp = strchr(tmp2, '>');
79 if (tmp) {
80 from = g_strndup(tmp2, tmp - tmp2);
81 } else {
82 SIPE_DEBUG_INFO_NOFORMAT("found < without > in From");
83 return NULL;
85 } else {
86 tmp = strchr(tmp2, ';');
87 if (tmp) {
88 from = g_strndup(tmp2, tmp - tmp2);
89 } else {
90 from = g_strdup(tmp2);
93 SIPE_DEBUG_INFO("got %s", from);
94 return 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;
111 if (len) {
112 gchar *s;
114 /* reserve space for worst case, i.e. every character needs escaping */
115 escaped = s = g_malloc(3 * len + 1);
116 while (len--) {
117 gchar c = *in++;
119 /* only allow ASCII characters */
120 if (!isascii(c)) {
121 g_free(escaped);
122 return(NULL);
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"
137 if (isalnum(c) ||
138 (c == '.') ||
139 (c == '-') ||
140 (c == '_') ||
141 (c == '~')) {
142 *s++ = c;
143 } else {
144 sprintf(s, "%%%1X%1X", c / 16, c % 16);
145 s += 3;
148 *s = '\0';
151 return(escaped);
154 gchar *sip_uri_if_valid(const gchar *string)
156 /* strip possible sip: prefix */
157 const gchar *uri = sipe_get_no_sip_uri(string);
158 const gchar *at;
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);
165 if (userinfo) {
166 gchar *host = escape_uri_part(at + 1, strlen(at + 1));
168 if (host) {
169 /* name is valid for URI, convert it */
170 result = g_strdup_printf("sip:%s@%s",
171 userinfo,
172 host);
173 g_free(host);
175 g_free(userinfo);
179 return(result);
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));
190 } else {
191 return sip_uri;
195 gchar *
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,
201 g_get_host_name(),
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);
212 g_free(epid);
213 return(uuid);
217 guint
218 sipe_get_pub_instance(struct sipe_core_private *sipe_private,
219 int publication_key)
221 unsigned res = 0;
222 gchar *epid = get_epid(sipe_private);
224 sscanf(epid, "%08x", &res);
225 g_free(epid);
227 if (publication_key == SIPE_PUB_DEVICE) {
228 /* as is */
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);
244 g_free(mail_hash);
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;
250 return res;
253 gboolean
254 sipe_is_bad_alias(const char *uri,
255 const char *alias)
257 char *uri_alias;
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)) {
268 result = TRUE;
270 g_free(uri_alias);
272 return result;
275 gboolean
276 is_empty(const char *st)
278 if (!st || strlen(st) == 0)
280 return TRUE;
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) {
289 g_free(dup);
290 return TRUE;
292 g_free(dup);
294 return FALSE;
297 void sipe_utils_message_debug(const gchar *type,
298 const gchar *header,
299 const gchar *body,
300 gboolean sending)
302 if (sipe_backend_debug_enabled()) {
303 GString *str = g_string_new("");
304 GTimeVal currtime;
305 gchar *time_str;
306 const char *marker = sending ?
307 ">>>>>>>>>>" :
308 "<<<<<<<<<<";
309 gchar *tmp;
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"));
315 g_free(tmp);
316 g_string_append(str, "\n");
317 if (body) {
318 g_string_append(str, tmp = sipe_utils_str_replace(body, "\r\n", "\n"));
319 g_free(tmp);
320 g_string_append(str, "\n");
322 g_string_append_printf(str, "MESSAGE END %s %s - %s", marker, type, time_str);
323 g_free(time_str);
324 SIPE_DEBUG_INFO_NOFORMAT(str->str);
325 g_string_free(str, TRUE);
329 gboolean
330 sipe_strequal(const gchar *left, const gchar *right)
332 #if GLIB_CHECK_VERSION(2,16,0)
333 return (g_strcmp0(left, right) == 0);
334 #else
335 return ((left == NULL && right == NULL) ||
336 (left != NULL && right != NULL && strcmp(left, right) == 0));
337 #endif
340 gboolean
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));
351 #else
352 if (!a)
353 return -(a != b);
354 if (!b)
355 return a != b;
356 return strcmp(a, b);
357 #endif
360 time_t
361 sipe_utils_str_to_time(const gchar *timestamp)
363 GTimeVal time;
364 gboolean success = FALSE;
366 /* g_time_val_from_iso8601() warns about NULL pointer */
367 if (timestamp) {
368 guint len;
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);
377 g_free(tmp);
378 } else {
379 success = g_time_val_from_iso8601(timestamp, &time);
383 if (!success) {
384 SIPE_DEBUG_ERROR("sipe_utils_str_to_time: failed to parse ISO8601 string '%s'",
385 timestamp ? timestamp : "");
386 time.tv_sec = 0;
389 return time.tv_sec;
392 gchar *
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);
402 size_t length;
404 if (!buffer)
405 return("");
407 /* asctime() appends "\n" to the resulting string -> strip it */
408 length = strlen(buffer);
409 if (length)
410 buffer[length - 1] = '\0';
412 return(buffer);
415 size_t
416 hex_str_to_buff(const char *hex_str, guint8 **buff)
418 char two_digits[3];
419 size_t length;
420 size_t i;
422 if (!buff) return 0;
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);
434 return length;
437 char *
438 buff_to_hex_str(const guint8 *buff, const size_t buff_len)
440 char *res;
441 size_t i, j;
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]);
449 res[j] = '\0';
450 return res;
453 gboolean
454 sipe_utils_parse_lines(GSList **list, gchar **lines, gchar *delimiter)
456 int i;
457 gchar **parts;
458 gchar *dummy;
459 gchar *dummy2;
460 gchar *tmp;
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]) {
465 g_strfreev(parts);
466 return FALSE;
468 dummy = parts[1];
469 dummy2 = 0;
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')) {
473 i++;
474 dummy = lines[i];
475 while(*dummy==' ' || *dummy=='\t') dummy++;
476 tmp = g_strdup_printf("%s %s",dummy2, dummy);
477 g_free(dummy2);
478 dummy2 = tmp;
480 *list = sipe_utils_nameval_add(*list, parts[0], dummy2);
481 g_free(dummy2);
482 g_strfreev(parts);
485 return TRUE;
488 GSList*
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! */
494 if (!value) {
495 SIPE_DEBUG_ERROR("sipe_utils_nameval_add: NULL value for %s",
496 name);
497 value = "";
500 element->name = g_strdup(name);
501 element->value = g_strdup(value);
502 return g_slist_append(list, element);
505 void
506 sipe_utils_nameval_free(GSList *list) {
507 struct sipnameval *elem;
508 while(list) {
509 elem = list->data;
510 list = g_slist_remove(list,elem);
511 g_free(elem->name);
512 g_free(elem->value);
513 g_free(elem);
517 const gchar *
518 sipe_utils_nameval_find(const GSList *list, const gchar *name)
520 return sipe_utils_nameval_find_instance (list, name, 0);
523 const gchar *
524 sipe_utils_nameval_find_instance(const GSList *list, const gchar *name, int which)
526 const GSList *tmp;
527 struct sipnameval *elem;
528 int i = 0;
529 tmp = list;
530 while(tmp) {
531 elem = tmp->data;
532 // OCS2005 can send the same header in either all caps or mixed case
533 if (sipe_strcase_equal(elem->name, name)) {
534 if (i == which) {
535 return elem->value;
537 i++;
539 tmp = g_slist_next(tmp);
541 return NULL;
544 gchar *sipe_utils_str_replace(const gchar *string,
545 const gchar *delimiter,
546 const gchar *replacement)
548 gchar **split;
549 gchar *result;
551 if (!string || !delimiter || !replacement) return NULL;
553 split = g_strsplit(string, delimiter, 0);
554 result = g_strjoinv(replacement, split);
555 g_strfreev(split);
557 return result;
560 void sipe_utils_shrink_buffer(struct sipe_transport_connection *conn,
561 const gchar *unread)
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);
580 gchar *
581 sipe_utils_uri_unescape(const gchar *string)
583 gchar *unescaped;
584 gchar *tmp;
586 if (!string)
587 return NULL;
589 #if GLIB_CHECK_VERSION(2,16,0)
590 unescaped = g_uri_unescape_string(string, NULL);
591 #else
592 // based on libpurple/util.c:purple_url_decode()
594 GString *buf = g_string_new(NULL);
595 size_t len = strlen(string);
596 char hex[3];
598 hex[2] = '\0';
600 while (len--) {
601 gchar c = *string++;
603 if ((len >= 2) && (c == '%')) {
604 strncpy(hex, string, 2);
605 c = strtol(hex, NULL, 16);
607 string += 2;
608 len -= 2;
611 g_string_append_c(buf, c);
614 unescaped = g_string_free(buf, FALSE);
616 #endif
617 if (unescaped && !g_utf8_validate(unescaped, -1, (const gchar **)&tmp))
618 *tmp = '\0';
620 return unescaped;
623 GSList *sipe_utils_slist_insert_unique_sorted(GSList *list,
624 gpointer data,
625 GCompareFunc func,
626 GDestroyNotify destroy)
628 if (g_slist_find_custom(list, data, func)) {
629 /* duplicate */
630 if (destroy)
631 (*destroy)(data);
632 return(list);
633 } else {
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,
640 GDestroyNotify free)
642 #if GLIB_CHECK_VERSION(2,28,0)
643 g_slist_free_full(list, free);
644 #else
645 GSList *entry = list;
646 while (entry) {
647 (*free)(entry->data);
648 entry = entry->next;
650 g_slist_free(list);
651 #endif
655 Local Variables:
656 mode: c
657 c-file-style: "bsd"
658 indent-tabs-mode: t
659 tab-width: 8
660 End: