buddy: add backend API to refresh properties
[siplcs.git] / src / core / sipe-utils.c
blob36e7333451c77813937234343d8fce905e8d5bd8
1 /**
2 * @file sipe-utils.c
4 * pidgin-sipe
6 * Copyright (C) 2009-2012 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 /* can't use g_uri_escape_string as it requires glib-2.0 >= 2.16.0 :-( */
107 gchar *sip_uri_if_valid(const gchar *string)
109 /* strip possible sip: prefix */
110 const gchar *s = sipe_get_no_sip_uri(string);
111 if (!s) return(NULL);
113 /* scan string for invalid URI characters */
114 while (*s) {
115 gchar c = *s++;
117 if (!(isascii(c) &&
118 (isalnum(c) ||
119 (c == '.') ||
120 (c == '-') ||
121 (c == '_') ||
122 (c == '@'))))
123 return(NULL);
126 /* name is valid for URI, convert it */
127 return(sip_uri(string));
130 const gchar *sipe_get_no_sip_uri(const gchar *sip_uri)
132 #define SIP_PREFIX "sip:"
134 if (!sip_uri) return NULL;
136 if (g_str_has_prefix(sip_uri, SIP_PREFIX)) {
137 return(sip_uri + strlen(SIP_PREFIX));
138 } else {
139 return sip_uri;
143 gchar *
144 get_epid(struct sipe_core_private *sipe_private)
146 if (!sipe_private->epid) {
147 gchar *self_sip_uri = sip_uri_self(sipe_private);
148 sipe_private->epid = sipe_get_epid(self_sip_uri,
149 g_get_host_name(),
150 sipe_backend_network_ip_address(SIPE_CORE_PUBLIC));
151 g_free(self_sip_uri);
153 return g_strdup(sipe_private->epid);
156 gchar *get_uuid(struct sipe_core_private *sipe_private)
158 gchar *epid = get_epid(sipe_private);
159 gchar *uuid = generateUUIDfromEPID(epid);
160 g_free(epid);
161 return(uuid);
165 guint
166 sipe_get_pub_instance(struct sipe_core_private *sipe_private,
167 int publication_key)
169 unsigned res = 0;
170 gchar *epid = get_epid(sipe_private);
172 sscanf(epid, "%08x", &res);
173 g_free(epid);
175 if (publication_key == SIPE_PUB_DEVICE) {
176 /* as is */
177 } else if (publication_key == SIPE_PUB_STATE_MACHINE) { /* First hexadecimal digit is 0x3 */
178 res = (res >> 4) | 0x30000000;
179 } else if (publication_key == SIPE_PUB_STATE_USER) {
180 res = 0x20000000; /* fixed */
181 } else if (publication_key == SIPE_PUB_STATE_CALENDAR) { /* First hexadecimal digit is 0x4 */
182 res = (res >> 4) | 0x40000000;
183 } else if (publication_key == SIPE_PUB_STATE_CALENDAR_OOF) { /* First hexadecimal digit is 0x5 */
184 res = (res >> 4) | 0x50000000;
185 } else if (publication_key == SIPE_PUB_CALENDAR_DATA ||
186 publication_key == SIPE_PUB_NOTE_OOF)
187 { /* First hexadecimal digit is 0x4 */
188 unsigned calendar_id = 0;
189 char *mail_hash = sipe_get_epid(sipe_private->email, "", "");
191 sscanf(mail_hash, "%08x", &calendar_id);
192 g_free(mail_hash);
193 res = (calendar_id >> 4) | 0x40000000;
194 } else if (publication_key == SIPE_PUB_STATE_PHONE_VOIP) { /* First hexadecimal digit is 0x8 */
195 res = (res >> 4) | 0x80000000;
198 return res;
201 gboolean
202 sipe_is_bad_alias(const char *uri,
203 const char *alias)
205 char *uri_alias;
206 gboolean result = FALSE;
208 if (!uri) return FALSE;
209 if (!alias) return TRUE;
211 if (g_str_has_prefix(alias, "sip:") || g_str_has_prefix(alias, "sips:")) return TRUE;
213 /* check if alias is just SIP URI but without 'sip:' prefix */
214 uri_alias = sip_uri_from_name(alias);
215 if (sipe_strcase_equal(uri, uri_alias)) {
216 result = TRUE;
218 g_free(uri_alias);
220 return result;
223 gboolean
224 is_empty(const char *st)
226 if (!st || strlen(st) == 0)
228 return TRUE;
230 /* suspecious leading or trailing staces */
231 else if (isspace((unsigned char) *st) ||
232 isspace((unsigned char) *(st + strlen(st) - 1)))
234 /* to not modify original string */
235 char *dup = g_strdup(st);
236 if (strlen(g_strstrip(dup)) == 0) {
237 g_free(dup);
238 return TRUE;
240 g_free(dup);
242 return FALSE;
245 /** Returns newly allocated string. Must be g_free()'d */
246 char *
247 replace(const char *st,
248 const char *search,
249 const char *replace)
251 char **tmp;
252 char *res;
254 if (!st) return NULL;
256 res = g_strjoinv(replace, tmp = g_strsplit(st, search, -1));
257 g_strfreev(tmp);
258 return res;
261 void sipe_utils_message_debug(const gchar *type,
262 const gchar *header,
263 const gchar *body,
264 gboolean sending)
266 if (sipe_backend_debug_enabled()) {
267 GString *str = g_string_new("");
268 GTimeVal currtime;
269 gchar *time_str;
270 const char *marker = sending ?
271 ">>>>>>>>>>" :
272 "<<<<<<<<<<";
273 gchar *tmp;
275 g_get_current_time(&currtime);
276 time_str = g_time_val_to_iso8601(&currtime);
277 g_string_append_printf(str, "\nMESSAGE START %s %s - %s\n", marker, type, time_str);
278 g_string_append(str, tmp = replace(header, "\r\n", "\n"));
279 g_free(tmp);
280 g_string_append(str, "\n");
281 if (body) {
282 g_string_append(str, tmp = replace(body, "\r\n", "\n"));
283 g_free(tmp);
284 g_string_append(str, "\n");
286 g_string_append_printf(str, "MESSAGE END %s %s - %s", marker, type, time_str);
287 g_free(time_str);
288 SIPE_DEBUG_INFO_NOFORMAT(str->str);
289 g_string_free(str, TRUE);
293 gboolean
294 sipe_strequal(const gchar *left, const gchar *right)
296 #if GLIB_CHECK_VERSION(2,16,0)
297 return (g_strcmp0(left, right) == 0);
298 #else
299 return ((left == NULL && right == NULL) ||
300 (left != NULL && right != NULL && strcmp(left, right) == 0));
301 #endif
304 gboolean
305 sipe_strcase_equal(const gchar *left, const gchar *right)
307 return ((left == NULL && right == NULL) ||
308 (left != NULL && right != NULL && g_ascii_strcasecmp(left, right) == 0));
311 gint sipe_strcompare(gconstpointer a, gconstpointer b)
313 #if GLIB_CHECK_VERSION(2,16,0)
314 return (g_strcmp0(a, b));
315 #else
316 if (!a)
317 return -(a != b);
318 if (!b)
319 return a != b;
320 return strcmp(a, b);
321 #endif
324 time_t
325 sipe_utils_str_to_time(const gchar *timestamp)
327 GTimeVal time;
328 guint len;
330 /* We have to make sure that the ISO8601 contains a time zone offset,
331 otherwise the time is interpreted as local time, not UTC!
332 @TODO: is there a better way to check this? */
333 if (timestamp && (len = strlen(timestamp) > 0) &&
334 isdigit(timestamp[len - 1])) {
335 gchar *tmp = g_strdup_printf("%sZ", timestamp);
336 g_time_val_from_iso8601(tmp, &time);
337 g_free(tmp);
338 } else {
339 g_time_val_from_iso8601(timestamp, &time);
341 return time.tv_sec;
344 gchar *
345 sipe_utils_time_to_str(time_t timestamp)
347 GTimeVal time = { timestamp, 0 };
348 return g_time_val_to_iso8601(&time);
351 size_t
352 hex_str_to_buff(const char *hex_str, guint8 **buff)
354 char two_digits[3];
355 size_t length;
356 size_t i;
358 if (!buff) return 0;
359 if (!hex_str) return 0;
361 length = strlen(hex_str)/2;
362 *buff = (unsigned char *)g_malloc(length);
363 for (i = 0; i < length; i++) {
364 two_digits[0] = hex_str[i * 2];
365 two_digits[1] = hex_str[i * 2 + 1];
366 two_digits[2] = '\0';
367 (*buff)[i] = (unsigned char)strtoul(two_digits, NULL, 16);
370 return length;
373 char *
374 buff_to_hex_str(const guint8 *buff, const size_t buff_len)
376 char *res;
377 size_t i, j;
379 if (!buff) return NULL;
381 res = g_malloc(buff_len * 2 + 1);
382 for (i = 0, j = 0; i < buff_len; i++, j+=2) {
383 sprintf(&res[j], "%02X", buff[i]);
385 res[j] = '\0';
386 return res;
389 gboolean
390 sipe_utils_parse_lines(GSList **list, gchar **lines, gchar *delimiter)
392 int i;
393 gchar **parts;
394 gchar *dummy;
395 gchar *dummy2;
396 gchar *tmp;
398 for(i = 0; lines[i] && strlen(lines[i]) > 2; i++) {
399 parts = g_strsplit(lines[i], delimiter, 2);
400 if(!parts[0] || !parts[1]) {
401 g_strfreev(parts);
402 return FALSE;
404 dummy = parts[1];
405 dummy2 = 0;
406 while(*dummy==' ' || *dummy=='\t') dummy++;
407 dummy2 = g_strdup(dummy);
408 while(lines[i+1] && (lines[i+1][0]==' ' || lines[i+1][0]=='\t')) {
409 i++;
410 dummy = lines[i];
411 while(*dummy==' ' || *dummy=='\t') dummy++;
412 tmp = g_strdup_printf("%s %s",dummy2, dummy);
413 g_free(dummy2);
414 dummy2 = tmp;
416 *list = sipe_utils_nameval_add(*list, parts[0], dummy2);
417 g_free(dummy2);
418 g_strfreev(parts);
421 return TRUE;
424 GSList*
425 sipe_utils_nameval_add(GSList* list, const gchar *name, const gchar *value)
427 struct sipnameval *element = g_new0(struct sipnameval,1);
429 /* SANITY CHECK: the calling code must be fixed if this happens! */
430 if (!value) {
431 SIPE_DEBUG_ERROR("sipe_utils_nameval_add: NULL value for %s",
432 name);
433 value = "";
436 element->name = g_strdup(name);
437 element->value = g_strdup(value);
438 return g_slist_append(list, element);
441 void
442 sipe_utils_nameval_free(GSList *list) {
443 struct sipnameval *elem;
444 while(list) {
445 elem = list->data;
446 list = g_slist_remove(list,elem);
447 g_free(elem->name);
448 g_free(elem->value);
449 g_free(elem);
453 const gchar *
454 sipe_utils_nameval_find(const GSList *list, const gchar *name)
456 return sipe_utils_nameval_find_instance (list, name, 0);
459 const gchar *
460 sipe_utils_nameval_find_instance(const GSList *list, const gchar *name, int which)
462 const GSList *tmp;
463 struct sipnameval *elem;
464 int i = 0;
465 tmp = list;
466 while(tmp) {
467 elem = tmp->data;
468 // OCS2005 can send the same header in either all caps or mixed case
469 if (sipe_strcase_equal(elem->name, name)) {
470 if (i == which) {
471 return elem->value;
473 i++;
475 tmp = g_slist_next(tmp);
477 return NULL;
480 gchar *sipe_utils_str_replace(const gchar *string,
481 const gchar *delimiter,
482 const gchar *replacement)
484 gchar **split;
485 gchar *result;
487 if (!string || !delimiter || !replacement) return NULL;
489 split = g_strsplit(string, delimiter, 0);
490 result = g_strjoinv(replacement, split);
491 g_strfreev(split);
493 return result;
496 void sipe_utils_shrink_buffer(struct sipe_transport_connection *conn,
497 const gchar *unread)
499 conn->buffer_used -= unread - conn->buffer;
500 /* string terminator is not included in buffer_used */
501 memmove(conn->buffer, unread, conn->buffer_used + 1);
504 gboolean sipe_utils_ip_is_private(const char *ip)
506 return g_str_has_prefix(ip, "10.") ||
507 g_str_has_prefix(ip, "172.16.") ||
508 g_str_has_prefix(ip, "192.168.");
511 gchar *sipe_utils_presence_key(const gchar *uri)
513 return g_strdup_printf("<presence><%s>", uri);
516 gchar *sipe_utils_subscription_key(const gchar *event,
517 const gchar *uri)
519 gchar *key = NULL;
521 if (!is_empty(event)) {
522 if (!g_ascii_strcasecmp(event, "presence")) {
523 /* Subscription is identified by <presence><uri> key */
524 key = sipe_utils_presence_key(uri);
525 } else {
526 /* Subscription is identified by <event> key */
527 key = g_strdup_printf("<%s>", event);
531 return key;
534 gchar *
535 sipe_utils_uri_unescape(const gchar *string)
537 gchar *unescaped;
538 gchar *tmp;
540 if (!string)
541 return NULL;
543 #if GLIB_CHECK_VERSION(2,16,0)
544 unescaped = g_uri_unescape_string(string, NULL);
545 #else
546 // based on libpurple/util.c:purple_url_decode()
548 GString *buf = g_string_new(NULL);
549 size_t len = strlen(string);
550 char hex[3];
552 hex[2] = '\0';
554 while (len--) {
555 gchar c = *string++;
557 if ((len >= 2) && (c == '%')) {
558 strncpy(hex, string, 2);
559 c = strtol(hex, NULL, 16);
561 string += 2;
562 len -= 2;
565 g_string_append_c(buf, c);
568 unescaped = g_string_free(buf, FALSE);
570 #endif
571 if (unescaped && !g_utf8_validate(unescaped, -1, (const gchar **)&tmp))
572 *tmp = '\0';
574 return unescaped;
578 * Only appends if no such value already stored.
579 * Like Set in Java.
581 GSList *
582 slist_insert_unique_sorted(GSList *list, gpointer data, GCompareFunc func) {
583 GSList * res = list;
584 if (!g_slist_find_custom(list, data, func)) {
585 res = g_slist_insert_sorted(list, data, func);
587 return res;
591 Local Variables:
592 mode: c
593 c-file-style: "bsd"
594 indent-tabs-mode: t
595 tab-width: 8
596 End: