group: disable sipe_core_group_set_alias for UCS
[siplcs.git] / src / core / sipe-utils.c
blobb183f27e55da6076d17c200d71a193cf17113886
1 /**
2 * @file sipe-utils.c
4 * pidgin-sipe
6 * Copyright (C) 2009-2013 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>
28 #include <glib.h>
30 #include "sipe-backend.h"
31 #include "sipe-core.h" /* to ensure same API for backends */
32 #include "sipe-core-private.h"
33 #include "sipe-utils.h"
34 #include "uuid.h"
36 /* Generate 16 random bits */
37 #define RANDOM16BITS (rand() & 0xFFFF)
39 gchar *gencallid(void)
41 return g_strdup_printf("%04Xg%04Xa%04Xi%04Xm%04Xt%04Xb%04Xx%04Xx",
42 RANDOM16BITS, RANDOM16BITS, RANDOM16BITS,
43 RANDOM16BITS, RANDOM16BITS, RANDOM16BITS,
44 RANDOM16BITS, RANDOM16BITS);
47 gchar *gentag(void)
49 return g_strdup_printf("%04d%04d", RANDOM16BITS, RANDOM16BITS);
52 gchar *genconfid(void)
54 return g_strdup_printf("%04X%04X%04X%04X%04X%04X%04X%04X",
55 RANDOM16BITS, RANDOM16BITS, RANDOM16BITS,
56 RANDOM16BITS, RANDOM16BITS, RANDOM16BITS,
57 RANDOM16BITS, RANDOM16BITS);
60 gchar *get_contact(const struct sipe_core_private *sipe_private)
62 return g_strdup(sipe_private->contact);
65 gchar *parse_from(const gchar *hdr)
67 gchar *from;
68 const gchar *tmp, *tmp2 = hdr;
70 if (!hdr) return NULL;
71 SIPE_DEBUG_INFO("parsing address out of %s", hdr);
72 tmp = strchr(hdr, '<');
74 /* i hate the different SIP UA behaviours... */
75 if (tmp) { /* sip address in <...> */
76 tmp2 = tmp + 1;
77 tmp = strchr(tmp2, '>');
78 if (tmp) {
79 from = g_strndup(tmp2, tmp - tmp2);
80 } else {
81 SIPE_DEBUG_INFO_NOFORMAT("found < without > in From");
82 return NULL;
84 } else {
85 tmp = strchr(tmp2, ';');
86 if (tmp) {
87 from = g_strndup(tmp2, tmp - tmp2);
88 } else {
89 from = g_strdup(tmp2);
92 SIPE_DEBUG_INFO("got %s", from);
93 return from;
96 gchar *sip_uri_from_name(const gchar *name)
98 return(g_strdup_printf("sip:%s", name));
101 gchar *sip_uri(const gchar *string)
103 return(strstr(string, "sip:") ? g_strdup(string) : sip_uri_from_name(string));
106 static gchar *escape_uri_part(const gchar *in, guint len)
108 gchar *escaped = NULL;
110 if (len) {
111 gchar *s;
113 /* reserve space for worst case, i.e. every character needs escaping */
114 escaped = s = g_malloc(3 * len + 1);
115 while (len--) {
116 gchar c = *in++;
118 /* only allow ASCII characters */
119 if (!isascii(c)) {
120 g_free(escaped);
121 return(NULL);
125 * RFC 3986 Appendix A
127 * authority = [ userinfo "@" ] host [ ":" port ]
128 * userinfo = *( unreserved / pct-encoded / sub-delims / ":" )
129 * host = IP-literal / IPv4address / reg-name
130 * reg-name = *( unreserved / pct-encoded / sub-delims )
131 * pct-encoded = "%" HEXDIG HEXDIG
132 * unreserved = ALPHA / DIGIT / "-" / "." / "_" / "~"
134 * Escape everything that isn't in "unreserved"
136 if (isalnum(c) ||
137 (c == '.') ||
138 (c == '-') ||
139 (c == '_') ||
140 (c == '~')) {
141 *s++ = c;
142 } else {
143 sprintf(s, "%%%1X%1X", c / 16, c % 16);
144 s += 3;
147 *s = '\0';
150 return(escaped);
153 gchar *sip_uri_if_valid(const gchar *string)
155 /* strip possible sip: prefix */
156 const gchar *uri = sipe_get_no_sip_uri(string);
157 const gchar *at;
158 gchar *result = NULL;
160 /* only XXX@YYY is valid */
161 if (uri && ((at = strchr(uri, '@')) != NULL)) {
162 gchar *userinfo = escape_uri_part(uri, at - uri);
164 if (userinfo) {
165 gchar *host = escape_uri_part(at + 1, strlen(at + 1));
167 if (host) {
168 /* name is valid for URI, convert it */
169 result = g_strdup_printf("sip:%s@%s",
170 userinfo,
171 host);
172 g_free(host);
174 g_free(userinfo);
178 return(result);
181 const gchar *sipe_get_no_sip_uri(const gchar *sip_uri)
183 #define SIP_PREFIX "sip:"
185 if (!sip_uri) return NULL;
187 if (g_str_has_prefix(sip_uri, SIP_PREFIX)) {
188 return(sip_uri + strlen(SIP_PREFIX));
189 } else {
190 return sip_uri;
194 gchar *
195 get_epid(struct sipe_core_private *sipe_private)
197 if (!sipe_private->epid) {
198 gchar *self_sip_uri = sip_uri_self(sipe_private);
199 sipe_private->epid = sipe_get_epid(self_sip_uri,
200 g_get_host_name(),
201 sipe_backend_network_ip_address(SIPE_CORE_PUBLIC));
202 g_free(self_sip_uri);
204 return g_strdup(sipe_private->epid);
207 gchar *get_uuid(struct sipe_core_private *sipe_private)
209 gchar *epid = get_epid(sipe_private);
210 gchar *uuid = generateUUIDfromEPID(epid);
211 g_free(epid);
212 return(uuid);
216 guint
217 sipe_get_pub_instance(struct sipe_core_private *sipe_private,
218 int publication_key)
220 unsigned res = 0;
221 gchar *epid = get_epid(sipe_private);
223 sscanf(epid, "%08x", &res);
224 g_free(epid);
226 if (publication_key == SIPE_PUB_DEVICE) {
227 /* as is */
228 } else if (publication_key == SIPE_PUB_STATE_MACHINE) { /* First hexadecimal digit is 0x3 */
229 res = (res >> 4) | 0x30000000;
230 } else if (publication_key == SIPE_PUB_STATE_USER) {
231 res = 0x20000000; /* fixed */
232 } else if (publication_key == SIPE_PUB_STATE_CALENDAR) { /* First hexadecimal digit is 0x4 */
233 res = (res >> 4) | 0x40000000;
234 } else if (publication_key == SIPE_PUB_STATE_CALENDAR_OOF) { /* First hexadecimal digit is 0x5 */
235 res = (res >> 4) | 0x50000000;
236 } else if (publication_key == SIPE_PUB_CALENDAR_DATA ||
237 publication_key == SIPE_PUB_NOTE_OOF)
238 { /* First hexadecimal digit is 0x4 */
239 unsigned calendar_id = 0;
240 char *mail_hash = sipe_get_epid(sipe_private->email, "", "");
242 sscanf(mail_hash, "%08x", &calendar_id);
243 g_free(mail_hash);
244 res = (calendar_id >> 4) | 0x40000000;
245 } else if (publication_key == SIPE_PUB_STATE_PHONE_VOIP) { /* First hexadecimal digit is 0x8 */
246 res = (res >> 4) | 0x80000000;
249 return res;
252 gboolean
253 sipe_is_bad_alias(const char *uri,
254 const char *alias)
256 char *uri_alias;
257 gboolean result = FALSE;
259 if (!uri) return FALSE;
260 if (!alias) return TRUE;
262 if (g_str_has_prefix(alias, "sip:") || g_str_has_prefix(alias, "sips:")) return TRUE;
264 /* check if alias is just SIP URI but without 'sip:' prefix */
265 uri_alias = sip_uri_from_name(alias);
266 if (sipe_strcase_equal(uri, uri_alias)) {
267 result = TRUE;
269 g_free(uri_alias);
271 return result;
274 gboolean
275 is_empty(const char *st)
277 if (!st || strlen(st) == 0)
279 return TRUE;
281 /* suspecious leading or trailing staces */
282 else if (isspace((unsigned char) *st) ||
283 isspace((unsigned char) *(st + strlen(st) - 1)))
285 /* to not modify original string */
286 char *dup = g_strdup(st);
287 if (strlen(g_strstrip(dup)) == 0) {
288 g_free(dup);
289 return TRUE;
291 g_free(dup);
293 return FALSE;
296 /** Returns newly allocated string. Must be g_free()'d */
297 char *
298 replace(const char *st,
299 const char *search,
300 const char *replace)
302 char **tmp;
303 char *res;
305 if (!st) return NULL;
307 res = g_strjoinv(replace, tmp = g_strsplit(st, search, -1));
308 g_strfreev(tmp);
309 return res;
312 void sipe_utils_message_debug(const gchar *type,
313 const gchar *header,
314 const gchar *body,
315 gboolean sending)
317 if (sipe_backend_debug_enabled()) {
318 GString *str = g_string_new("");
319 GTimeVal currtime;
320 gchar *time_str;
321 const char *marker = sending ?
322 ">>>>>>>>>>" :
323 "<<<<<<<<<<";
324 gchar *tmp;
326 g_get_current_time(&currtime);
327 time_str = g_time_val_to_iso8601(&currtime);
328 g_string_append_printf(str, "\nMESSAGE START %s %s - %s\n", marker, type, time_str);
329 g_string_append(str, tmp = replace(header, "\r\n", "\n"));
330 g_free(tmp);
331 g_string_append(str, "\n");
332 if (body) {
333 g_string_append(str, tmp = replace(body, "\r\n", "\n"));
334 g_free(tmp);
335 g_string_append(str, "\n");
337 g_string_append_printf(str, "MESSAGE END %s %s - %s", marker, type, time_str);
338 g_free(time_str);
339 SIPE_DEBUG_INFO_NOFORMAT(str->str);
340 g_string_free(str, TRUE);
344 gboolean
345 sipe_strequal(const gchar *left, const gchar *right)
347 #if GLIB_CHECK_VERSION(2,16,0)
348 return (g_strcmp0(left, right) == 0);
349 #else
350 return ((left == NULL && right == NULL) ||
351 (left != NULL && right != NULL && strcmp(left, right) == 0));
352 #endif
355 gboolean
356 sipe_strcase_equal(const gchar *left, const gchar *right)
358 return ((left == NULL && right == NULL) ||
359 (left != NULL && right != NULL && g_ascii_strcasecmp(left, right) == 0));
362 gint sipe_strcompare(gconstpointer a, gconstpointer b)
364 #if GLIB_CHECK_VERSION(2,16,0)
365 return (g_strcmp0(a, b));
366 #else
367 if (!a)
368 return -(a != b);
369 if (!b)
370 return a != b;
371 return strcmp(a, b);
372 #endif
375 time_t
376 sipe_utils_str_to_time(const gchar *timestamp)
378 GTimeVal time;
379 gboolean success = FALSE;
381 /* g_time_val_from_iso8601() warns about NULL pointer */
382 if (timestamp) {
383 guint len;
385 /* We have to make sure that the ISO8601 contains a time zone offset,
386 otherwise the time is interpreted as local time, not UTC!
387 @TODO: is there a better way to check this? */
388 if (((len = strlen(timestamp)) > 0) &&
389 isdigit(timestamp[len-1])) {
390 gchar *tmp = g_strdup_printf("%sZ", timestamp);
391 success = g_time_val_from_iso8601(tmp, &time);
392 g_free(tmp);
393 } else {
394 success = g_time_val_from_iso8601(timestamp, &time);
398 if (!success) {
399 SIPE_DEBUG_ERROR("sipe_utils_str_to_time: failed to parse ISO8601 string '%s'",
400 timestamp ? timestamp : "");
401 time.tv_sec = 0;
404 return time.tv_sec;
407 gchar *
408 sipe_utils_time_to_str(time_t timestamp)
410 GTimeVal time = { timestamp, 0 };
411 return g_time_val_to_iso8601(&time);
414 size_t
415 hex_str_to_buff(const char *hex_str, guint8 **buff)
417 char two_digits[3];
418 size_t length;
419 size_t i;
421 if (!buff) return 0;
422 if (!hex_str) return 0;
424 length = strlen(hex_str)/2;
425 *buff = (unsigned char *)g_malloc(length);
426 for (i = 0; i < length; i++) {
427 two_digits[0] = hex_str[i * 2];
428 two_digits[1] = hex_str[i * 2 + 1];
429 two_digits[2] = '\0';
430 (*buff)[i] = (unsigned char)strtoul(two_digits, NULL, 16);
433 return length;
436 char *
437 buff_to_hex_str(const guint8 *buff, const size_t buff_len)
439 char *res;
440 size_t i, j;
442 if (!buff) return NULL;
444 res = g_malloc(buff_len * 2 + 1);
445 for (i = 0, j = 0; i < buff_len; i++, j+=2) {
446 sprintf(&res[j], "%02X", buff[i]);
448 res[j] = '\0';
449 return res;
452 gboolean
453 sipe_utils_parse_lines(GSList **list, gchar **lines, gchar *delimiter)
455 int i;
456 gchar **parts;
457 gchar *dummy;
458 gchar *dummy2;
459 gchar *tmp;
461 for(i = 0; lines[i] && strlen(lines[i]) > 2; i++) {
462 parts = g_strsplit(lines[i], delimiter, 2);
463 if(!parts[0] || !parts[1]) {
464 g_strfreev(parts);
465 return FALSE;
467 dummy = parts[1];
468 dummy2 = 0;
469 while(*dummy==' ' || *dummy=='\t') dummy++;
470 dummy2 = g_strdup(dummy);
471 while(lines[i+1] && (lines[i+1][0]==' ' || lines[i+1][0]=='\t')) {
472 i++;
473 dummy = lines[i];
474 while(*dummy==' ' || *dummy=='\t') dummy++;
475 tmp = g_strdup_printf("%s %s",dummy2, dummy);
476 g_free(dummy2);
477 dummy2 = tmp;
479 *list = sipe_utils_nameval_add(*list, parts[0], dummy2);
480 g_free(dummy2);
481 g_strfreev(parts);
484 return TRUE;
487 GSList*
488 sipe_utils_nameval_add(GSList* list, const gchar *name, const gchar *value)
490 struct sipnameval *element = g_new0(struct sipnameval,1);
492 /* SANITY CHECK: the calling code must be fixed if this happens! */
493 if (!value) {
494 SIPE_DEBUG_ERROR("sipe_utils_nameval_add: NULL value for %s",
495 name);
496 value = "";
499 element->name = g_strdup(name);
500 element->value = g_strdup(value);
501 return g_slist_append(list, element);
504 void
505 sipe_utils_nameval_free(GSList *list) {
506 struct sipnameval *elem;
507 while(list) {
508 elem = list->data;
509 list = g_slist_remove(list,elem);
510 g_free(elem->name);
511 g_free(elem->value);
512 g_free(elem);
516 const gchar *
517 sipe_utils_nameval_find(const GSList *list, const gchar *name)
519 return sipe_utils_nameval_find_instance (list, name, 0);
522 const gchar *
523 sipe_utils_nameval_find_instance(const GSList *list, const gchar *name, int which)
525 const GSList *tmp;
526 struct sipnameval *elem;
527 int i = 0;
528 tmp = list;
529 while(tmp) {
530 elem = tmp->data;
531 // OCS2005 can send the same header in either all caps or mixed case
532 if (sipe_strcase_equal(elem->name, name)) {
533 if (i == which) {
534 return elem->value;
536 i++;
538 tmp = g_slist_next(tmp);
540 return NULL;
543 gchar *sipe_utils_str_replace(const gchar *string,
544 const gchar *delimiter,
545 const gchar *replacement)
547 gchar **split;
548 gchar *result;
550 if (!string || !delimiter || !replacement) return NULL;
552 split = g_strsplit(string, delimiter, 0);
553 result = g_strjoinv(replacement, split);
554 g_strfreev(split);
556 return result;
559 void sipe_utils_shrink_buffer(struct sipe_transport_connection *conn,
560 const gchar *unread)
562 conn->buffer_used -= unread - conn->buffer;
563 /* string terminator is not included in buffer_used */
564 memmove(conn->buffer, unread, conn->buffer_used + 1);
567 gboolean sipe_utils_ip_is_private(const char *ip)
569 return g_str_has_prefix(ip, "10.") ||
570 g_str_has_prefix(ip, "172.16.") ||
571 g_str_has_prefix(ip, "192.168.");
574 gchar *sipe_utils_presence_key(const gchar *uri)
576 return g_strdup_printf("<presence><%s>", uri);
579 gchar *
580 sipe_utils_uri_unescape(const gchar *string)
582 gchar *unescaped;
583 gchar *tmp;
585 if (!string)
586 return NULL;
588 #if GLIB_CHECK_VERSION(2,16,0)
589 unescaped = g_uri_unescape_string(string, NULL);
590 #else
591 // based on libpurple/util.c:purple_url_decode()
593 GString *buf = g_string_new(NULL);
594 size_t len = strlen(string);
595 char hex[3];
597 hex[2] = '\0';
599 while (len--) {
600 gchar c = *string++;
602 if ((len >= 2) && (c == '%')) {
603 strncpy(hex, string, 2);
604 c = strtol(hex, NULL, 16);
606 string += 2;
607 len -= 2;
610 g_string_append_c(buf, c);
613 unescaped = g_string_free(buf, FALSE);
615 #endif
616 if (unescaped && !g_utf8_validate(unescaped, -1, (const gchar **)&tmp))
617 *tmp = '\0';
619 return unescaped;
622 GSList *sipe_utils_slist_insert_unique_sorted(GSList *list,
623 gpointer data,
624 GCompareFunc func,
625 GDestroyNotify destroy)
627 if (g_slist_find_custom(list, data, func)) {
628 /* duplicate */
629 if (destroy)
630 (*destroy)(data);
631 return(list);
632 } else {
633 /* unique: list takes ownership of "data" */
634 return(g_slist_insert_sorted(list, data, func));
638 void sipe_utils_slist_free_full(GSList *list,
639 GDestroyNotify free)
641 #if GLIB_CHECK_VERSION(2,28,0)
642 g_slist_free_full(list, free);
643 #else
644 GSList *entry = list;
645 while (entry) {
646 (*free)(entry->data);
647 entry = entry->next;
649 g_slist_free(list);
650 #endif
654 Local Variables:
655 mode: c
656 c-file-style: "bsd"
657 indent-tabs-mode: t
658 tab-width: 8
659 End: