1
/* -*- Mode: C; tab-width: 8; indent-tabs-mode: t; c-basic-offset: 8 -*- */
3
/* Copyright (C) 2001-2004 Novell, Inc.
5
* This program is free software; you can redistribute it and/or
6
* modify it under the terms of version 2 of the GNU Lesser General Public
7
* License as published by the Free Software Foundation.
9
* This program is distributed in the hope that it will be useful,
10
* but WITHOUT ANY WARRANTY; without even the implied warranty of
11
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
12
* General Public License for more details.
14
* You should have received a copy of the GNU Lesser General Public
15
* License along with this program; if not, write to the
16
* Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor,
17
* Boston, MA 02110-1301, USA.
24
#include "e2k-utils.h"
25
#include "e2k-autoconfig.h"
26
#include "e2k-propnames.h"
29
#include <libedataserver/e-time-utils.h>
35
/* Do not internationalize */
36
const gchar *e2k_rfc822_months [] = {
37
"Jan", "Feb", "Mar", "Apr", "May", "Jun",
38
"Jul", "Aug", "Sep", "Oct", "Nov", "Dec"
42
* e2k_parse_timestamp:
43
* @timestamp: an ISO8601 timestamp returned by the Exchange server
45
* Converts @timestamp to a %time_t value. @timestamp must be in one
46
* of the two ISO8601 variants used by Exchange.
48
* Note that the timestamps used (in most contexts) by Exchange have
49
* millisecond resolution, so converting them to %time_t loses
50
* resolution. Since ISO8601 timestamps can be compared using
51
* strcmp(), it is often best to keep them as strings.
53
* Return value: the %time_t corresponding to @timestamp, or -1 on
57
e2k_parse_timestamp (const gchar *timestamp)
61
tm.tm_year = strtoul (timestamp, (gchar **)×tamp, 10) - 1900;
62
if (*timestamp++ != '-')
64
tm.tm_mon = strtoul (timestamp, (gchar **)×tamp, 10) - 1;
65
if (*timestamp++ != '-')
67
tm.tm_mday = strtoul (timestamp, (gchar **)×tamp, 10);
68
if (*timestamp++ != 'T')
70
tm.tm_hour = strtoul (timestamp, (gchar **)×tamp, 10);
71
if (*timestamp++ != ':')
73
tm.tm_min = strtoul (timestamp, (gchar **)×tamp, 10);
74
if (*timestamp++ != ':')
76
tm.tm_sec = strtoul (timestamp, (gchar **)×tamp, 10);
77
if (*timestamp != '.' && *timestamp != 'Z')
80
return e_mktime_utc (&tm);
85
* @when: the %time_t to convert to an ISO8601 timestamp
87
* Creates an ISO8601 timestamp (in an format acceptable to Exchange)
88
* corresponding to @when.
90
* Return value: the timestamp, which the caller must free.
93
e2k_make_timestamp (time_t when)
98
return g_strdup_printf ("%04d-%02d-%02dT%02d:%02d:%02dZ",
99
tm->tm_year + 1900, tm->tm_mon + 1, tm->tm_mday,
100
tm->tm_hour, tm->tm_min, tm->tm_sec);
104
* e2k_make_timestamp_rfc822:
105
* @when: the %time_t to convert to an RFC822 timestamp
107
* Creates an RFC822 Date header value corresponding to @when, in the
110
* Return value: the timestamp, which the caller must free.
113
e2k_make_timestamp_rfc822 (time_t when)
118
e_localtime_with_offset (when, &tm, &offset);
119
offset = (offset / 3600) * 100 + (offset / 60) % 60;
121
return g_strdup_printf ("%02d %s %04d %02d:%02d:%02d %+05d",
122
tm.tm_mday, e2k_rfc822_months[tm.tm_mon],
124
tm.tm_hour, tm.tm_min, tm.tm_sec,
128
/* SYSTIME_OFFSET is the number of minutes between the Windows epoch
129
* (1601-01-01T00:00:00Z) and the time_t epoch (1970-01-01T00:00:00Z):
130
* 369 years, 89 of which are leap years.
132
#define SYSTIME_OFFSET 194074560UL
135
* e2k_systime_to_time_t:
136
* @systime: a MAPI PT_SYSTIME value (minutes since Windows epoch)
138
* Converts the MAPI PT_SYSTIME value @systime to a corresponding
139
* %time_t value (assuming it is within the valid range of a %time_t).
141
* Return value: a %time_t corresponding to @systime.
144
e2k_systime_to_time_t (guint32 systime)
146
return (systime - SYSTIME_OFFSET) * 60;
150
* e2k_systime_from_time_t:
151
* @tt: a %time_t value
153
* Converts the %time_t value @tt to a corresponding MAPI PT_SYSTIME
154
* value, losing some precision if @tt does not fall on a minute
157
* Return value: the Windows systime value corresponding to @tt
160
e2k_systime_from_time_t (time_t tt)
162
return (tt / 60) + SYSTIME_OFFSET;
166
* e2k_filetime_to_time_t:
167
* @filetime: a Windows FILETIME value (100ns intervals since
170
* Converts the Windows FILETIME value @filetime to a corresponding
171
* %time_t value (assuming it is within the valid range of a %time_t),
172
* truncating to a second boundary.
174
* Return value: a %time_t corresponding to @filetime.
177
e2k_filetime_to_time_t (guint64 filetime)
179
return (time_t)(filetime / 10000000 - SYSTIME_OFFSET * 60);
183
* e2k_filetime_from_time_t:
184
* @tt: a %time_t value
186
* Converts the %time_t value @tt to a corresponding Windows FILETIME
189
* Return value: the Windows FILETIME value corresponding to @tt
192
e2k_filetime_from_time_t (time_t tt)
194
return (((guint64)tt) + ((guint64)SYSTIME_OFFSET) * 60) * 10000000;
199
* @in: input text in UNIX ("\n") format
201
* Creates a copy of @in with all LFs converted to CRLFs.
203
* Return value: the converted text, which the caller must free.
206
e2k_lf_to_crlf (const gchar *in)
212
g_return_val_if_fail (in != NULL, NULL);
215
for (s = strchr (in, '\n'); s; s = strchr (s + 1, '\n'))
218
out = g_malloc (len + 1);
219
for (s = in, d = out; *s; s++) {
231
* @in: input text in network ("\r\n") format
233
* Creates a copy of @in with all CRLFs converted to LFs. (Actually,
234
* it just strips CRs, so any raw CRs will be removed.)
236
* Return value: the converted text, which the caller must free.
239
e2k_crlf_to_lf (const gchar *in)
246
g_return_val_if_fail (in != NULL, NULL);
248
str = g_string_new ("");
251
for (s = in; *s; s++) {
253
str = g_string_append_c (str, *s);
257
g_string_free (str, FALSE);
263
* e2k_strdup_with_trailing_slash:
264
* @path: a URI or path
266
* Copies @path, appending a "/" to it if and only if it did not
267
* already end in "/".
269
* Return value: the path, which the caller must free
272
e2k_strdup_with_trailing_slash (const gchar *path)
279
p = strrchr (path, '/');
281
return g_strdup (path);
283
return g_strdup_printf ("%s/", path);
288
* @entryid: an Exchange entryid
290
* Finds an Exchange 5.5 DN inside a binary entryid property (such as
291
* #PR_STORE_ENTRYID or an element of #PR_DELEGATES_ENTRYIDS).
293
* Return value: the entryid, which is a pointer into @entryid's data.
296
e2k_entryid_to_dn (GByteArray *entryid)
300
p = ((gchar *)entryid->data) + entryid->len - 1;
302
while (*(p - 1) && p > (gchar *)entryid->data)
311
append_permanenturl_section (GString *url, guint8 *entryid)
317
g_string_append_printf (url, "%02x", entryid[i++]);
319
/* Replace 0s with a single '-' */
320
g_string_append_c (url, '-');
321
while (i < 22 && entryid[i] == 0)
324
/* Last part; note that if the first non-0 byte can be
325
* expressed in a single hex digit, we do so. (ie, the 0
326
* in the 16's place was also accumulated into the
329
if (i < 22 && entryid[i] < 0x10)
330
g_string_append_printf (url, "%01x", entryid[i++]);
332
g_string_append_printf (url, "%02x", entryid[i++]);
335
#define E2K_PERMANENTURL_INFIX "-FlatUrlSpace-"
336
#define E2K_PERMANENTURL_INFIX_LEN (sizeof (E2K_PERMANENTURL_INFIX) - 1)
339
* e2k_entryid_to_permanenturl:
340
* @entryid: an ENTRYID (specifically, a PR_SOURCE_KEY)
341
* @base_uri: base URI of the store containing @entryid
343
* Creates a permanenturl based on @entryid and @base_uri.
345
* Return value: the permanenturl, which the caller must free.
348
e2k_entryid_to_permanenturl (GByteArray *entryid, const gchar *base_uri)
353
g_return_val_if_fail (entryid->len == 22 || entryid->len == 44, NULL);
355
url = g_string_new (base_uri);
356
if (url->str[url->len - 1] != '/')
357
g_string_append_c (url, '/');
358
g_string_append (url, E2K_PERMANENTURL_INFIX);
359
g_string_append_c (url, '/');
361
append_permanenturl_section (url, entryid->data);
362
if (entryid->len > 22) {
363
g_string_append_c (url, '/');
364
append_permanenturl_section (url, entryid->data + 22);
368
g_string_free (url, FALSE);
372
#define HEXVAL(c) (isdigit (c) ? (c) - '0' : g_ascii_tolower (c) - 'a' + 10)
375
append_entryid_section (GByteArray *entryid, const gchar **permanenturl)
378
guint8 buf[44], byte;
382
if (strspn (p, "0123456789abcdefABCDEF") != 32)
386
endlen = strspn (p + 33, "0123456789abcdefABCDEF");
390
/* Expand to the full form by replacing the "-" with "0"s */
392
memset (buf + 32, '0', sizeof (buf) - 32 - endlen);
393
memcpy (buf + sizeof (buf) - endlen, p + 33, endlen);
396
while (p < (gchar *) buf + sizeof (buf)) {
397
byte = (HEXVAL (*p) << 4) + HEXVAL (*(p + 1));
398
g_byte_array_append (entryid, &byte, 1);
402
*permanenturl += 33 + endlen;
407
* e2k_permanenturl_to_entryid:
408
* @permanenturl: an Exchange permanenturl
410
* Creates an ENTRYID (specifically, a PR_SOURCE_KEY) based on
413
* Return value: the entryid
416
e2k_permanenturl_to_entryid (const gchar *permanenturl)
420
permanenturl = strstr (permanenturl, E2K_PERMANENTURL_INFIX);
423
permanenturl += E2K_PERMANENTURL_INFIX_LEN;
425
entryid = g_byte_array_new ();
426
while (*permanenturl++ == '/') {
427
if (!append_entryid_section (entryid, &permanenturl)) {
428
g_byte_array_free (entryid, TRUE);
437
* e2k_ascii_strcase_equal
439
* @v2: another string
441
* ASCII-case-insensitive comparison function for use with #GHashTable.
443
* Return value: %TRUE if @v and @v2 are ASCII-case-insensitively
444
* equal, %FALSE if not.
447
e2k_ascii_strcase_equal (gconstpointer v, gconstpointer v2)
449
return !g_ascii_strcasecmp (v, v2);
453
* e2k_ascii_strcase_hash
456
* ASCII-case-insensitive hash function for use with #GHashTable.
458
* Return value: An ASCII-case-insensitive hashing of @v.
461
e2k_ascii_strcase_hash (gconstpointer v)
463
/* case-insensitive g_str_hash */
466
guint h = g_ascii_tolower (*p);
469
for (p += 1; *p != '\0'; p++)
470
h = (h << 5) - h + g_ascii_tolower (*p);
477
* e2k_restriction_folders_only:
480
* Examines @rn, and determines if it can only return folders
482
* Return value: %TRUE if @rn will cause only folders to be returned
485
e2k_restriction_folders_only (E2kRestriction *rn)
493
case E2K_RESTRICTION_PROPERTY:
494
if (strcmp (rn->res.property.pv.prop.name,
495
E2K_PR_DAV_IS_COLLECTION) != 0)
498
/* return TRUE if it's "= TRUE" or "!= FALSE" */
499
return (rn->res.property.relop == E2K_RELOP_EQ) ==
500
(rn->res.property.pv.value != NULL);
502
case E2K_RESTRICTION_AND:
503
for (i = 0; i < rn->res.and.nrns; i++) {
504
if (e2k_restriction_folders_only (rn->res.and.rns[i]))
509
case E2K_RESTRICTION_OR:
510
for (i = 0; i < rn->res.or.nrns; i++) {
511
if (!e2k_restriction_folders_only (rn->res.or.rns[i]))
516
case E2K_RESTRICTION_NOT:
517
return !e2k_restriction_folders_only (rn->res.not.rn);
519
case E2K_RESTRICTION_COMMENT:
520
return e2k_restriction_folders_only (rn->res.comment.rn);
527
/* From MAPIDEFS.H */
528
static const gchar MAPI_ONE_OFF_UID[] = {
529
0x81, 0x2b, 0x1f, 0xa4, 0xbe, 0xa3, 0x10, 0x19,
530
0x9d, 0x6e, 0x00, 0xdd, 0x01, 0x0f, 0x54, 0x02
532
#define MAPI_ONE_OFF_UNICODE 0x8000
533
#define MAPI_ONE_OFF_NO_RICH_INFO 0x0001
534
#define MAPI_ONE_OFF_MYSTERY_FLAG 0x1000
537
* e2k_entryid_generate_oneoff:
538
* @display_name: the display name of the user
539
* @email: the email address
540
* @unicode: %TRUE to generate a Unicode ENTRYID (in which case
541
* @display_name should be UTF-8), %FALSE for an ASCII ENTRYID.
543
* Constructs a "one-off" ENTRYID value that can be used as a MAPI
544
* recipient (eg, for a message forwarding server-side rule),
545
* corresponding to @display_name and @email.
547
* Return value: the recipient ENTRYID
550
e2k_entryid_generate_oneoff (const gchar *display_name, const gchar *email, gboolean unicode)
554
entryid = g_byte_array_new ();
556
e2k_rule_append_uint32 (entryid, 0);
557
g_byte_array_append (entryid, (guint8 *) MAPI_ONE_OFF_UID, sizeof (MAPI_ONE_OFF_UID));
558
e2k_rule_append_uint16 (entryid, 0);
559
e2k_rule_append_uint16 (entryid,
560
MAPI_ONE_OFF_NO_RICH_INFO |
561
MAPI_ONE_OFF_MYSTERY_FLAG |
562
(unicode ? MAPI_ONE_OFF_UNICODE : 0));
565
e2k_rule_append_unicode (entryid, display_name);
566
e2k_rule_append_unicode (entryid, "SMTP");
567
e2k_rule_append_unicode (entryid, email);
569
e2k_rule_append_string (entryid, display_name);
570
e2k_rule_append_string (entryid, "SMTP");
571
e2k_rule_append_string (entryid, email);
577
static const gchar MAPI_LOCAL_UID[] = {
578
0xdc, 0xa7, 0x40, 0xc8, 0xc0, 0x42, 0x10, 0x1a,
579
0xb4, 0xb9, 0x08, 0x00, 0x2b, 0x2f, 0xe1, 0x82
583
* e2k_entryid_generate_local:
584
* @exchange_dn: the Exchange 5.5-style DN of the local user
586
* Constructs an ENTRYID value that can be used as a MAPI
587
* recipient (eg, for a message forwarding server-side rule),
588
* corresponding to the local user identified by @exchange_dn.
590
* Return value: the recipient ENTRYID
593
e2k_entryid_generate_local (const gchar *exchange_dn)
597
entryid = g_byte_array_new ();
599
e2k_rule_append_uint32 (entryid, 0);
600
g_byte_array_append (entryid, (guint8 *) MAPI_LOCAL_UID, sizeof (MAPI_LOCAL_UID));
601
e2k_rule_append_uint16 (entryid, 1);
602
e2k_rule_append_uint16 (entryid, 0);
603
e2k_rule_append_string (entryid, exchange_dn);
608
static const gchar MAPI_CONTACT_UID[] = {
609
0xfe, 0x42, 0xaa, 0x0a, 0x18, 0xc7, 0x1a, 0x10,
610
0xe8, 0x85, 0x0b, 0x65, 0x1c, 0x24, 0x00, 0x00
614
* e2k_entryid_generate_contact:
615
* @contact_entryid: the #PR_ENTRYID of an item in the user's Contacts
617
* @nth_address: which of the contact's email addresses to use.
619
* Constructs an ENTRYID value that can be used as a MAPI recipient
620
* (eg, for a message forwarding server-side rule), corresponding to
621
* the Contacts folder entry identified by @contact_entryid.
623
* Return value: the recipient ENTRYID
626
e2k_entryid_generate_contact (GByteArray *contact_entryid, gint nth_address)
630
entryid = g_byte_array_new ();
632
e2k_rule_append_uint32 (entryid, 0);
633
g_byte_array_append (entryid, (guint8 *) MAPI_CONTACT_UID, sizeof (MAPI_CONTACT_UID));
634
e2k_rule_append_uint32 (entryid, 3);
635
e2k_rule_append_uint32 (entryid, 4);
636
e2k_rule_append_uint32 (entryid, nth_address);
637
e2k_rule_append_uint32 (entryid, contact_entryid->len);
638
g_byte_array_append (entryid, contact_entryid->data, contact_entryid->len);
644
* e2k_search_key_generate:
645
* @addrtype: the type of @address (usually "SMTP" or "EX")
646
* @address: the address data
648
* Constructs a PR_SEARCH_KEY value for @address
650
* Return value: the search key
653
e2k_search_key_generate (const gchar *addrtype, const gchar *address)
655
GByteArray *search_key;
658
search_key = g_byte_array_new ();
659
g_byte_array_append (search_key, (guint8 *) addrtype, strlen (addrtype));
660
g_byte_array_append (search_key, (guint8 *) ":", 1);
661
g_byte_array_append (search_key, (guint8 *) address, strlen (address));
662
g_byte_array_append (search_key, (guint8 *) "", 1);
664
for (p = search_key->data; *p; p++)
665
*p = g_ascii_toupper (*p);