core cleanup: move out ocs2005 code
[siplcs.git] / src / core / sipe-utils.c
blobe1458507c8e2c5728b1c59f54d3727ec0e608d19
1 /**
2 * @file sipe-utils.c
4 * pidgin-sipe
6 * Copyright (C) 2009-2011 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"
35 #include "sipe.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 const gchar *sipe_get_no_sip_uri(const gchar *sip_uri)
109 #define SIP_PREFIX "sip:"
111 if (!sip_uri) return NULL;
113 if (g_str_has_prefix(sip_uri, SIP_PREFIX)) {
114 return(sip_uri + strlen(SIP_PREFIX));
115 } else {
116 return sip_uri;
120 gchar *
121 get_epid(struct sipe_core_private *sipe_private)
123 if (!sipe_private->epid) {
124 gchar *self_sip_uri = sip_uri_self(sipe_private);
125 sipe_private->epid = sipe_get_epid(self_sip_uri,
126 g_get_host_name(),
127 sipe_backend_network_ip_address());
128 g_free(self_sip_uri);
130 return g_strdup(sipe_private->epid);
133 gchar *get_uuid(struct sipe_core_private *sipe_private)
135 gchar *epid = get_epid(sipe_private);
136 gchar *uuid = generateUUIDfromEPID(epid);
137 g_free(epid);
138 return(uuid);
142 guint
143 sipe_get_pub_instance(struct sipe_core_private *sipe_private,
144 int publication_key)
146 unsigned res = 0;
147 gchar *epid = get_epid(sipe_private);
149 sscanf(epid, "%08x", &res);
150 g_free(epid);
152 if (publication_key == SIPE_PUB_DEVICE) {
153 /* as is */
154 } else if (publication_key == SIPE_PUB_STATE_MACHINE) { /* First hexadecimal digit is 0x3 */
155 res = (res >> 4) | 0x30000000;
156 } else if (publication_key == SIPE_PUB_STATE_USER) {
157 res = 0x20000000; /* fixed */
158 } else if (publication_key == SIPE_PUB_STATE_CALENDAR) { /* First hexadecimal digit is 0x4 */
159 res = (res >> 4) | 0x40000000;
160 } else if (publication_key == SIPE_PUB_STATE_CALENDAR_OOF) { /* First hexadecimal digit is 0x5 */
161 res = (res >> 4) | 0x50000000;
162 } else if (publication_key == SIPE_PUB_CALENDAR_DATA ||
163 publication_key == SIPE_PUB_NOTE_OOF)
164 { /* First hexadecimal digit is 0x4 */
165 unsigned calendar_id = 0;
166 char *mail_hash = sipe_get_epid(SIPE_ACCOUNT_DATA_PRIVATE->email, "", "");
168 sscanf(mail_hash, "%08x", &calendar_id);
169 g_free(mail_hash);
170 res = (calendar_id >> 4) | 0x40000000;
173 return res;
176 gboolean
177 sipe_is_bad_alias(const char *uri,
178 const char *alias)
180 char *uri_alias;
181 gboolean result = FALSE;
183 if (!uri) return FALSE;
184 if (!alias) return TRUE;
186 if (g_str_has_prefix(alias, "sip:") || g_str_has_prefix(alias, "sips:")) return TRUE;
188 /* check if alias is just SIP URI but without 'sip:' prefix */
189 uri_alias = sip_uri_from_name(alias);
190 if (sipe_strcase_equal(uri, uri_alias)) {
191 result = TRUE;
193 g_free(uri_alias);
195 return result;
198 gboolean
199 is_empty(const char *st)
201 if (!st || strlen(st) == 0)
203 return TRUE;
205 /* suspecious leading or trailing staces */
206 else if (isspace((unsigned char) *st) ||
207 isspace((unsigned char) *(st + strlen(st) - 1)))
209 /* to not modify original string */
210 char *dup = g_strdup(st);
211 if (strlen(g_strstrip(dup)) == 0) {
212 g_free(dup);
213 return TRUE;
215 g_free(dup);
217 return FALSE;
220 /** Returns newly allocated string. Must be g_free()'d */
221 char *
222 replace(const char *st,
223 const char *search,
224 const char *replace)
226 char **tmp;
227 char *res;
229 if (!st) return NULL;
231 res = g_strjoinv(replace, tmp = g_strsplit(st, search, -1));
232 g_strfreev(tmp);
233 return res;
236 void sipe_utils_message_debug(const gchar *type,
237 const gchar *header,
238 const gchar *body,
239 gboolean sending)
241 if (sipe_backend_debug_enabled()) {
242 GString *str = g_string_new("");
243 GTimeVal currtime;
244 gchar *time_str;
245 const char *marker = sending ?
246 ">>>>>>>>>>" :
247 "<<<<<<<<<<";
248 gchar *tmp;
250 g_get_current_time(&currtime);
251 time_str = g_time_val_to_iso8601(&currtime);
252 g_string_append_printf(str, "\nMESSAGE START %s %s - %s\n", marker, type, time_str);
253 g_string_append(str, tmp = replace(header, "\r\n", "\n"));
254 g_free(tmp);
255 g_string_append(str, "\n");
256 if (body) {
257 g_string_append(str, tmp = replace(body, "\r\n", "\n"));
258 g_free(tmp);
259 g_string_append(str, "\n");
261 g_string_append_printf(str, "MESSAGE END %s %s - %s", marker, type, time_str);
262 g_free(time_str);
263 SIPE_DEBUG_INFO_NOFORMAT(str->str);
264 g_string_free(str, TRUE);
268 gboolean
269 sipe_strequal(const gchar *left, const gchar *right)
271 #if GLIB_CHECK_VERSION(2,16,0)
272 return (g_strcmp0(left, right) == 0);
273 #else
274 return ((left == NULL && right == NULL) ||
275 (left != NULL && right != NULL && strcmp(left, right) == 0));
276 #endif
279 gboolean
280 sipe_strcase_equal(const gchar *left, const gchar *right)
282 return ((left == NULL && right == NULL) ||
283 (left != NULL && right != NULL && g_ascii_strcasecmp(left, right) == 0));
286 gint sipe_strcompare(gconstpointer a, gconstpointer b)
288 #if GLIB_CHECK_VERSION(2,16,0)
289 return (g_strcmp0(a, b));
290 #else
291 if (!a)
292 return -(a != b);
293 if (!b)
294 return a != b;
295 return strcmp(a, b);
296 #endif
299 time_t
300 sipe_utils_str_to_time(const gchar *timestamp)
302 GTimeVal time;
303 guint len;
305 /* We have to make sure that the ISO8601 contains a time zone offset,
306 otherwise the time is interpreted as local time, not UTC!
307 @TODO: is there a better way to check this? */
308 if (timestamp && (len = strlen(timestamp) > 0) &&
309 isdigit(timestamp[len - 1])) {
310 gchar *tmp = g_strdup_printf("%sZ", timestamp);
311 g_time_val_from_iso8601(tmp, &time);
312 g_free(tmp);
313 } else {
314 g_time_val_from_iso8601(timestamp, &time);
316 return time.tv_sec;
319 gchar *
320 sipe_utils_time_to_str(time_t timestamp)
322 GTimeVal time = { timestamp, 0 };
323 return g_time_val_to_iso8601(&time);
326 size_t
327 hex_str_to_buff(const char *hex_str, guint8 **buff)
329 char two_digits[3];
330 size_t length;
331 size_t i;
333 if (!buff) return 0;
334 if (!hex_str) return 0;
336 length = strlen(hex_str)/2;
337 *buff = (unsigned char *)g_malloc(length);
338 for (i = 0; i < length; i++) {
339 two_digits[0] = hex_str[i * 2];
340 two_digits[1] = hex_str[i * 2 + 1];
341 two_digits[2] = '\0';
342 (*buff)[i] = (unsigned char)strtoul(two_digits, NULL, 16);
345 return length;
348 char *
349 buff_to_hex_str(const guint8 *buff, const size_t buff_len)
351 char *res;
352 size_t i, j;
354 if (!buff) return NULL;
356 res = g_malloc(buff_len * 2 + 1);
357 for (i = 0, j = 0; i < buff_len; i++, j+=2) {
358 sprintf(&res[j], "%02X", buff[i]);
360 res[j] = '\0';
361 return res;
364 gboolean
365 sipe_utils_parse_lines(GSList **list, gchar **lines, gchar *delimiter)
367 int i;
368 gchar **parts;
369 gchar *dummy;
370 gchar *dummy2;
371 gchar *tmp;
373 for(i = 0; lines[i] && strlen(lines[i]) > 2; i++) {
374 parts = g_strsplit(lines[i], delimiter, 2);
375 if(!parts[0] || !parts[1]) {
376 g_strfreev(parts);
377 return FALSE;
379 dummy = parts[1];
380 dummy2 = 0;
381 while(*dummy==' ' || *dummy=='\t') dummy++;
382 dummy2 = g_strdup(dummy);
383 while(lines[i+1] && (lines[i+1][0]==' ' || lines[i+1][0]=='\t')) {
384 i++;
385 dummy = lines[i];
386 while(*dummy==' ' || *dummy=='\t') dummy++;
387 tmp = g_strdup_printf("%s %s",dummy2, dummy);
388 g_free(dummy2);
389 dummy2 = tmp;
391 *list = sipe_utils_nameval_add(*list, parts[0], dummy2);
392 g_free(dummy2);
393 g_strfreev(parts);
396 return TRUE;
399 GSList*
400 sipe_utils_nameval_add(GSList* list, const gchar *name, const gchar *value)
402 struct sipnameval *element = g_new0(struct sipnameval,1);
404 /* SANITY CHECK: the calling code must be fixed if this happens! */
405 if (!value) {
406 SIPE_DEBUG_ERROR("sipe_utils_nameval_add: NULL value for %s",
407 name);
408 value = "";
411 element->name = g_strdup(name);
412 element->value = g_strdup(value);
413 return g_slist_append(list, element);
416 void
417 sipe_utils_nameval_free(GSList *list) {
418 struct sipnameval *elem;
419 while(list) {
420 elem = list->data;
421 list = g_slist_remove(list,elem);
422 g_free(elem->name);
423 g_free(elem->value);
424 g_free(elem);
428 const gchar *
429 sipe_utils_nameval_find(const GSList *list, const gchar *name)
431 return sipe_utils_nameval_find_instance (list, name, 0);
434 const gchar *
435 sipe_utils_nameval_find_instance(const GSList *list, const gchar *name, int which)
437 const GSList *tmp;
438 struct sipnameval *elem;
439 int i = 0;
440 tmp = list;
441 while(tmp) {
442 elem = tmp->data;
443 // OCS2005 can send the same header in either all caps or mixed case
444 if (sipe_strcase_equal(elem->name, name)) {
445 if (i == which) {
446 return elem->value;
448 i++;
450 tmp = g_slist_next(tmp);
452 return NULL;
455 gchar *sipe_utils_str_replace(const gchar *string,
456 const gchar *delimiter,
457 const gchar *replacement)
459 gchar **split;
460 gchar *result;
462 if (!string || !delimiter || !replacement) return NULL;
464 split = g_strsplit(string, delimiter, 0);
465 result = g_strjoinv(replacement, split);
466 g_strfreev(split);
468 return result;
471 void sipe_utils_shrink_buffer(struct sipe_transport_connection *conn,
472 const gchar *unread)
474 conn->buffer_used -= unread - conn->buffer;
475 /* string terminator is not included in buffer_used */
476 memmove(conn->buffer, unread, conn->buffer_used + 1);
479 gboolean sipe_utils_ip_is_private(const char *ip)
481 return g_str_has_prefix(ip, "10.") ||
482 g_str_has_prefix(ip, "172.16.") ||
483 g_str_has_prefix(ip, "192.168.");
486 gchar *sipe_utils_presence_key(const gchar *uri)
488 return g_strdup_printf("<presence><%s>", uri);
491 gchar *sipe_utils_subscription_key(const gchar *event,
492 const gchar *uri)
494 gchar *key = NULL;
496 if (!is_empty(event)) {
497 if (!g_ascii_strcasecmp(event, "presence")) {
498 /* Subscription is identified by <presence><uri> key */
499 key = sipe_utils_presence_key(uri);
500 } else {
501 /* Subscription is identified by <event> key */
502 key = g_strdup_printf("<%s>", event);
506 return key;
509 gchar *
510 sipe_utils_uri_unescape(const gchar *string)
512 gchar *unescaped;
513 gchar *tmp;
515 if (!string)
516 return NULL;
518 #if GLIB_CHECK_VERSION(2,16,0)
519 unescaped = g_uri_unescape_string(string, NULL);
520 #else
521 // based on libpurple/util.c:purple_url_decode()
523 GString *buf = g_string_new(NULL);
524 size_t len = strlen(string);
525 char hex[3];
527 hex[2] = '\0';
529 while (len--) {
530 gchar c = *string++;
532 if ((len >= 2) && (c == '%')) {
533 strncpy(hex, string, 2);
534 c = strtol(hex, NULL, 16);
536 string += 2;
537 len -= 2;
540 g_string_append_c(buf, c);
543 unescaped = g_string_free(buf, FALSE);
545 #endif
546 if (unescaped && !g_utf8_validate(unescaped, -1, (const gchar **)&tmp))
547 *tmp = '\0';
549 return unescaped;
552 gboolean
553 sipe_utils_is_avconf_uri(const gchar *uri)
555 return g_strstr_len(uri, -1, "app:conf:audio-video:") != NULL;
559 * Only appends if no such value already stored.
560 * Like Set in Java.
562 GSList *
563 slist_insert_unique_sorted(GSList *list, gpointer data, GCompareFunc func) {
564 GSList * res = list;
565 if (!g_slist_find_custom(list, data, func)) {
566 res = g_slist_insert_sorted(list, data, func);
568 return res;
572 Local Variables:
573 mode: c
574 c-file-style: "bsd"
575 indent-tabs-mode: t
576 tab-width: 8
577 End: