Fix #3198585: Extra line breaks
[siplcs.git] / src / core / sipe-utils.c
blobc27ca22de4a7b4d546284958ecf4cf4119274679
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>
27 #include <time.h>
29 #include <glib.h>
31 #ifdef _WIN32
32 /* for network */
33 #include "win32/libc_interface.h"
34 #include <nspapi.h>
35 #else
36 #if defined(__APPLE__) || defined(__FreeBSD__)
37 #include <sys/socket.h>
38 #elif defined(__sun__)
39 #include <sys/sockio.h>
40 #endif
41 #include <unistd.h>
42 #include <net/if.h>
43 #include <sys/types.h>
44 #include <sys/ioctl.h>
45 #include <netinet/in.h>
46 #include <arpa/inet.h>
47 #endif
49 #include "sipe-backend.h"
50 #include "sipe-core.h" /* to ensure same API for backends */
51 #include "sipe-core-private.h"
52 #include "sipe-utils.h"
53 #include "uuid.h"
54 #include "sipe.h"
56 /* Generate 32 random bits */
57 #define RANDOM32BITS (rand() & 0xFFFF)
59 gchar *gencallid(void)
61 return g_strdup_printf("%04Xg%04Xa%04Xi%04Xm%04Xt%04Xb%04Xx%04Xx",
62 RANDOM32BITS, RANDOM32BITS, RANDOM32BITS,
63 RANDOM32BITS, RANDOM32BITS, RANDOM32BITS,
64 RANDOM32BITS, RANDOM32BITS);
67 gchar *gentag(void)
69 return g_strdup_printf("%04d%04d", RANDOM32BITS, RANDOM32BITS);
72 gchar *genconfid(void)
74 return g_strdup_printf("%04X%04X%04X%04X%04X%04X%04X%04X",
75 RANDOM32BITS, RANDOM32BITS, RANDOM32BITS,
76 RANDOM32BITS, RANDOM32BITS, RANDOM32BITS,
77 RANDOM32BITS, RANDOM32BITS);
80 gchar *get_contact(const struct sipe_core_private *sipe_private)
82 return g_strdup(sipe_private->contact);
85 gchar *parse_from(const gchar *hdr)
87 gchar *from;
88 const gchar *tmp, *tmp2 = hdr;
90 if (!hdr) return NULL;
91 SIPE_DEBUG_INFO("parsing address out of %s", hdr);
92 tmp = strchr(hdr, '<');
94 /* i hate the different SIP UA behaviours... */
95 if (tmp) { /* sip address in <...> */
96 tmp2 = tmp + 1;
97 tmp = strchr(tmp2, '>');
98 if (tmp) {
99 from = g_strndup(tmp2, tmp - tmp2);
100 } else {
101 SIPE_DEBUG_INFO_NOFORMAT("found < without > in From");
102 return NULL;
104 } else {
105 tmp = strchr(tmp2, ';');
106 if (tmp) {
107 from = g_strndup(tmp2, tmp - tmp2);
108 } else {
109 from = g_strdup(tmp2);
112 SIPE_DEBUG_INFO("got %s", from);
113 return from;
116 gchar *sip_uri_from_name(const gchar *name)
118 return(g_strdup_printf("sip:%s", name));
121 gchar *sip_uri(const gchar *string)
123 return(strstr(string, "sip:") ? g_strdup(string) : sip_uri_from_name(string));
126 gchar *
127 get_epid(struct sipe_core_private *sipe_private)
129 if (!sipe_private->epid) {
130 gchar *self_sip_uri = sip_uri_self(sipe_private);
131 sipe_private->epid = sipe_get_epid(self_sip_uri,
132 g_get_host_name(),
133 sipe_backend_network_ip_address());
134 g_free(self_sip_uri);
136 return g_strdup(sipe_private->epid);
139 guint
140 sipe_get_pub_instance(struct sipe_core_private *sipe_private,
141 int publication_key)
143 unsigned res = 0;
144 gchar *epid = get_epid(sipe_private);
146 sscanf(epid, "%08x", &res);
147 g_free(epid);
149 if (publication_key == SIPE_PUB_DEVICE) {
150 /* as is */
151 } else if (publication_key == SIPE_PUB_STATE_MACHINE) { /* First hexadecimal digit is 0x3 */
152 res = (res >> 4) | 0x30000000;
153 } else if (publication_key == SIPE_PUB_STATE_USER) {
154 res = 0x20000000; /* fixed */
155 } else if (publication_key == SIPE_PUB_STATE_CALENDAR) { /* First hexadecimal digit is 0x4 */
156 res = (res >> 4) | 0x40000000;
157 } else if (publication_key == SIPE_PUB_STATE_CALENDAR_OOF) { /* First hexadecimal digit is 0x5 */
158 res = (res >> 4) | 0x50000000;
159 } else if (publication_key == SIPE_PUB_CALENDAR_DATA ||
160 publication_key == SIPE_PUB_NOTE_OOF)
161 { /* First hexadecimal digit is 0x4 */
162 unsigned calendar_id = 0;
163 char *mail_hash = sipe_get_epid(SIPE_ACCOUNT_DATA_PRIVATE->email, "", "");
165 sscanf(mail_hash, "%08x", &calendar_id);
166 g_free(mail_hash);
167 res = (calendar_id >> 4) | 0x40000000;
170 return res;
173 gboolean
174 sipe_is_bad_alias(const char *uri,
175 const char *alias)
177 char *uri_alias;
178 gboolean result = FALSE;
180 if (!uri) return FALSE;
181 if (!alias) return TRUE;
183 if (g_str_has_prefix(alias, "sip:") || g_str_has_prefix(alias, "sips:")) return TRUE;
185 /* check if alias is just SIP URI but without 'sip:' prefix */
186 uri_alias = sip_uri_from_name(alias);
187 if (sipe_strcase_equal(uri, uri_alias)) {
188 result = TRUE;
190 g_free(uri_alias);
192 return result;
195 gboolean
196 is_empty(const char *st)
198 if (!st || strlen(st) == 0)
200 return TRUE;
202 /* suspecious leading or trailing staces */
203 else if (isspace((unsigned char) *st) ||
204 isspace((unsigned char) *(st + strlen(st) - 1)))
206 /* to not modify original string */
207 char *dup = g_strdup(st);
208 if (strlen(g_strstrip(dup)) == 0) {
209 g_free(dup);
210 return TRUE;
212 g_free(dup);
214 return FALSE;
217 /** Returns newly allocated string. Must be g_free()'d */
218 char *
219 replace(const char *st,
220 const char *search,
221 const char *replace)
223 char **tmp;
224 char *res;
226 if (!st) return NULL;
228 res = g_strjoinv(replace, tmp = g_strsplit(st, search, -1));
229 g_strfreev(tmp);
230 return res;
233 void sipe_utils_message_debug(const gchar *type,
234 const gchar *header,
235 const gchar *body,
236 gboolean sending)
238 if (sipe_backend_debug_enabled()) {
239 GString *str = g_string_new("");
240 GTimeVal currtime;
241 gchar *time_str;
242 const char *marker = sending ?
243 ">>>>>>>>>>" :
244 "<<<<<<<<<<";
245 gchar *tmp;
247 g_get_current_time(&currtime);
248 time_str = g_time_val_to_iso8601(&currtime);
249 g_string_append_printf(str, "\nMESSAGE START %s %s - %s\n", marker, type, time_str);
250 g_string_append(str, tmp = replace(header, "\r\n", "\n"));
251 g_free(tmp);
252 g_string_append(str, "\n");
253 if (body) {
254 g_string_append(str, tmp = replace(body, "\r\n", "\n"));
255 g_free(tmp);
256 g_string_append(str, "\n");
258 g_string_append_printf(str, "MESSAGE END %s %s - %s", marker, type, time_str);
259 g_free(time_str);
260 SIPE_DEBUG_INFO_NOFORMAT(str->str);
261 g_string_free(str, TRUE);
265 gboolean
266 sipe_strequal(const gchar *left, const gchar *right)
268 #if GLIB_CHECK_VERSION(2,16,0)
269 return (g_strcmp0(left, right) == 0);
270 #else
271 return ((left == NULL && right == NULL) ||
272 (left != NULL && right != NULL && strcmp(left, right) == 0));
273 #endif
276 gboolean
277 sipe_strcase_equal(const gchar *left, const gchar *right)
279 return ((left == NULL && right == NULL) ||
280 (left != NULL && right != NULL && g_ascii_strcasecmp(left, right) == 0));
283 gint sipe_strcompare(gconstpointer a, gconstpointer b)
285 #if GLIB_CHECK_VERSION(2,16,0)
286 return (g_strcmp0(a, b));
287 #else
288 if (!a)
289 return -(a != b);
290 if (!b)
291 return a != b;
292 return strcmp(a, b);
293 #endif
296 time_t
297 sipe_utils_str_to_time(const gchar *timestamp)
299 GTimeVal time;
300 guint len;
302 /* We have to make sure that the ISO8601 contains a time zone offset,
303 otherwise the time is interpreted as local time, not UTC!
304 @TODO: is there a better way to check this? */
305 if (timestamp && (len = strlen(timestamp) > 0) &&
306 isdigit(timestamp[len - 1])) {
307 gchar *tmp = g_strdup_printf("%sZ", timestamp);
308 g_time_val_from_iso8601(tmp, &time);
309 g_free(tmp);
310 } else {
311 g_time_val_from_iso8601(timestamp, &time);
313 return time.tv_sec;
316 gchar *
317 sipe_utils_time_to_str(time_t timestamp)
319 GTimeVal time = { timestamp, 0 };
320 return g_time_val_to_iso8601(&time);
323 size_t
324 hex_str_to_buff(const char *hex_str, guint8 **buff)
326 char two_digits[3];
327 size_t length;
328 size_t i;
330 if (!buff) return 0;
331 if (!hex_str) return 0;
333 length = strlen(hex_str)/2;
334 *buff = (unsigned char *)g_malloc(length);
335 for (i = 0; i < length; i++) {
336 two_digits[0] = hex_str[i * 2];
337 two_digits[1] = hex_str[i * 2 + 1];
338 two_digits[2] = '\0';
339 (*buff)[i] = (unsigned char)strtoul(two_digits, NULL, 16);
342 return length;
345 char *
346 buff_to_hex_str(const guint8 *buff, const size_t buff_len)
348 char *res;
349 size_t i, j;
351 if (!buff) return NULL;
353 res = g_malloc(buff_len * 2 + 1);
354 for (i = 0, j = 0; i < buff_len; i++, j+=2) {
355 sprintf(&res[j], "%02X", buff[i]);
357 res[j] = '\0';
358 return res;
361 gboolean
362 sipe_utils_parse_lines(GSList **list, gchar **lines, gchar *delimiter)
364 int i;
365 gchar **parts;
366 gchar *dummy;
367 gchar *dummy2;
368 gchar *tmp;
370 for(i = 0; lines[i] && strlen(lines[i]) > 2; i++) {
371 parts = g_strsplit(lines[i], delimiter, 2);
372 if(!parts[0] || !parts[1]) {
373 g_strfreev(parts);
374 return FALSE;
376 dummy = parts[1];
377 dummy2 = 0;
378 while(*dummy==' ' || *dummy=='\t') dummy++;
379 dummy2 = g_strdup(dummy);
380 while(lines[i+1] && (lines[i+1][0]==' ' || lines[i+1][0]=='\t')) {
381 i++;
382 dummy = lines[i];
383 while(*dummy==' ' || *dummy=='\t') dummy++;
384 tmp = g_strdup_printf("%s %s",dummy2, dummy);
385 g_free(dummy2);
386 dummy2 = tmp;
388 *list = sipe_utils_nameval_add(*list, parts[0], dummy2);
389 g_free(dummy2);
390 g_strfreev(parts);
393 return TRUE;
396 GSList*
397 sipe_utils_nameval_add(GSList* list, const gchar *name, const gchar *value)
399 struct sipnameval *element = g_new0(struct sipnameval,1);
401 /* SANITY CHECK: the calling code must be fixed if this happens! */
402 if (!value) {
403 SIPE_DEBUG_ERROR("sipe_utils_nameval_add: NULL value for %s",
404 name);
405 value = "";
408 element->name = g_strdup(name);
409 element->value = g_strdup(value);
410 return g_slist_append(list, element);
413 void
414 sipe_utils_nameval_free(GSList *list) {
415 struct sipnameval *elem;
416 while(list) {
417 elem = list->data;
418 list = g_slist_remove(list,elem);
419 g_free(elem->name);
420 g_free(elem->value);
421 g_free(elem);
425 const gchar *
426 sipe_utils_nameval_find(const GSList *list, const gchar *name)
428 return sipe_utils_nameval_find_instance (list, name, 0);
431 const gchar *
432 sipe_utils_nameval_find_instance(const GSList *list, const gchar *name, int which)
434 const GSList *tmp;
435 struct sipnameval *elem;
436 int i = 0;
437 tmp = list;
438 while(tmp) {
439 elem = tmp->data;
440 // OCS2005 can send the same header in either all caps or mixed case
441 if (sipe_strcase_equal(elem->name, name)) {
442 if (i == which) {
443 return elem->value;
445 i++;
447 tmp = g_slist_next(tmp);
449 return NULL;
452 gchar *sipe_utils_str_replace(const gchar *string,
453 const gchar *delimiter,
454 const gchar *replacement)
456 gchar **split;
457 gchar *result;
459 if (!string || !delimiter || !replacement) return NULL;
461 split = g_strsplit(string, delimiter, 0);
462 result = g_strjoinv(replacement, split);
463 g_strfreev(split);
465 return result;
468 void sipe_utils_shrink_buffer(struct sipe_transport_connection *conn,
469 const gchar *unread)
471 conn->buffer_used -= unread - conn->buffer;
472 /* string terminator is not included in buffer_used */
473 memmove(conn->buffer, unread, conn->buffer_used + 1);
477 * Calling sizeof(struct ifreq) isn't always correct on
478 * Mac OS X (and maybe others).
480 #ifdef _SIZEOF_ADDR_IFREQ
481 # define HX_SIZE_OF_IFREQ(a) _SIZEOF_ADDR_IFREQ(a)
482 #else
483 # define HX_SIZE_OF_IFREQ(a) sizeof(a)
484 #endif
486 #define IFREQ_MAX 32
488 const char * sipe_utils_get_suitable_local_ip(int fd)
490 int source = (fd >= 0) ? fd : socket(PF_INET,SOCK_STREAM, 0);
492 if (source >= 0) {
493 struct ifreq *buffer = g_new0(struct ifreq, IFREQ_MAX);
494 struct ifconf ifc;
495 guint32 lhost = htonl(127 * 256 * 256 * 256 + 1);
496 guint32 llocal = htonl((169 << 24) + (254 << 16));
497 guint i;
498 static char ip[16];
500 ifc.ifc_len = sizeof(struct ifreq) * IFREQ_MAX;
501 ifc.ifc_req = buffer;
502 ioctl(source, SIOCGIFCONF, &ifc);
504 if (fd < 0)
505 close(source);
507 for (i = 0; i < IFREQ_MAX; i++)
509 struct ifreq *ifr = &buffer[i];
511 if (ifr->ifr_addr.sa_family == AF_INET)
513 struct sockaddr_in sin;
514 memcpy(&sin, &ifr->ifr_addr, sizeof(struct sockaddr_in));
515 if (sin.sin_addr.s_addr != lhost
516 && (sin.sin_addr.s_addr & htonl(0xFFFF0000)) != llocal)
518 long unsigned int add = ntohl(sin.sin_addr.s_addr);
519 g_snprintf(ip, 16, "%lu.%lu.%lu.%lu",
520 ((add >> 24) & 255),
521 ((add >> 16) & 255),
522 ((add >> 8) & 255),
523 add & 255);
525 g_free(buffer);
526 return ip;
530 g_free(buffer);
533 return "0.0.0.0";
536 gboolean sipe_utils_ip_is_private(const char *ip)
538 return g_str_has_prefix(ip, "10.") ||
539 g_str_has_prefix(ip, "172.16.") ||
540 g_str_has_prefix(ip, "192.168.");
543 gchar *sipe_utils_presence_key(const gchar *uri)
545 return g_strdup_printf("<presence><%s>", uri);
548 gchar *sipe_utils_subscription_key(const gchar *event,
549 const gchar *uri)
551 gchar *key = NULL;
553 if (!is_empty(event)) {
554 if (!g_ascii_strcasecmp(event, "presence")) {
555 /* Subscription is identified by <presence><uri> key */
556 key = sipe_utils_presence_key(uri);
557 } else {
558 /* Subscription is identified by <event> key */
559 key = g_strdup_printf("<%s>", event);
563 return key;
567 Local Variables:
568 mode: c
569 c-file-style: "bsd"
570 indent-tabs-mode: t
571 tab-width: 8
572 End: