Extending test-client-custom-summary to try e_book_client_get_contacts_uids()
[platform/upstream/evolution-data-server.git] / camel / camel-internet-address.c
1 /*
2  *  Copyright (C) 1999-2008 Novell, Inc. (www.novell.com)
3  *
4  *  Authors: Michael Zucchi <notzed@ximian.com>
5  *
6  * This program is free software; you can redistribute it and/or
7  * modify it under the terms of version 2 of the GNU Lesser General Public
8  * License as published by the Free Software Foundation.
9  *
10  * This program is distributed in the hope that it will be useful,
11  * but WITHOUT ANY WARRANTY; without even the implied warranty of
12  * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
13  * General Public License for more details.
14  *
15  * You should have received a copy of the GNU Lesser General Public
16  * License along with this program; if not, write to the
17  * Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor,
18  * Boston, MA 02110-1301, USA.
19  */
20
21 #include <stdio.h>
22 #include <string.h>
23
24 #include "camel-internet-address.h"
25 #include "camel-mime-utils.h"
26
27 #define d(x)
28
29 struct _address {
30         gchar *name;
31         gchar *address;
32 };
33
34 G_DEFINE_TYPE (CamelInternetAddress, camel_internet_address, CAMEL_TYPE_ADDRESS)
35
36 static gint
37 internet_address_decode (CamelAddress *a,
38                          const gchar *raw)
39 {
40         struct _camel_header_address *ha, *n;
41         gint count = a->addresses->len;
42
43         /* Should probably use its own decoder or something */
44         ha = camel_header_address_decode (raw, NULL);
45         if (ha) {
46                 n = ha;
47                 while (n) {
48                         if (n->type == CAMEL_HEADER_ADDRESS_NAME) {
49                                 camel_internet_address_add ((CamelInternetAddress *) a, n->name, n->v.addr);
50                         } else if (n->type == CAMEL_HEADER_ADDRESS_GROUP) {
51                                 struct _camel_header_address *g = n->v.members;
52                                 while (g) {
53                                         if (g->type == CAMEL_HEADER_ADDRESS_NAME)
54                                                 camel_internet_address_add ((CamelInternetAddress *) a, g->name, g->v.addr);
55                                         /* otherwise, it's an error, infact */
56                                         g = g->next;
57                                 }
58                         }
59                         n = n->next;
60                 }
61                 camel_header_address_list_clear (&ha);
62         }
63
64         return a->addresses->len - count;
65 }
66
67 static gchar *
68 internet_address_encode (CamelAddress *a)
69 {
70         gint i;
71         GString *out;
72         gchar *ret;
73         gint len = 6;           /* "From: ", assume longer of the address headers */
74
75         if (a->addresses->len == 0)
76                 return NULL;
77
78         out = g_string_new ("");
79
80         for (i = 0; i < a->addresses->len; i++) {
81                 struct _address *addr = g_ptr_array_index (a->addresses, i);
82                 gchar *enc;
83
84                 if (i != 0)
85                         g_string_append (out, ", ");
86
87                 enc = camel_internet_address_encode_address (&len, addr->name, addr->address);
88                 g_string_append (out, enc);
89                 g_free (enc);
90         }
91
92         ret = out->str;
93         g_string_free (out, FALSE);
94
95         return ret;
96 }
97
98 static gint
99 internet_address_unformat (CamelAddress *a,
100                            const gchar *raw)
101 {
102         gchar *buffer, *p, *name, *addr;
103         gint c;
104         gint count = a->addresses->len;
105
106         if (raw == NULL)
107                 return 0;
108
109         d (printf ("unformatting address: %s\n", raw));
110
111         /* we copy, so we can modify as we go */
112         buffer = g_strdup (raw);
113
114         /* this can be simpler than decode, since there are much fewer rules */
115         p = buffer;
116         name = NULL;
117         addr = p;
118         do {
119                 c = (guchar) * p++;
120                 switch (c) {
121                         /* removes quotes, they should only be around the total name anyway */
122                 case '"':
123                         p[-1] = ' ';
124                         while (*p)
125                                 if (*p == '"') {
126                                         *p++ = ' ';
127                                         break;
128                                 } else {
129                                         p++;
130                                 }
131                         break;
132                 case '<':
133                         if (name == NULL)
134                                 name = addr;
135                         addr = p;
136                         addr[-1] = 0;
137                         while (*p && *p != '>')
138                                 p++;
139                         if (*p == 0)
140                                 break;
141                         p++;
142                         /* falls through */
143                 case ',':
144                         p[-1] = 0;
145                         /* falls through */
146                 case 0:
147                         if (name)
148                                 name = g_strstrip (name);
149                         addr = g_strstrip (addr);
150                         if (addr[0]) {
151                                 d (printf ("found address: '%s' <%s>\n", name, addr));
152                                 camel_internet_address_add ((CamelInternetAddress *) a, name, addr);
153                         }
154                         name = NULL;
155                         addr = p;
156                         break;
157                 }
158         } while (c);
159
160         g_free (buffer);
161
162         return a->addresses->len - count;
163 }
164
165 static gchar *
166 internet_address_format (CamelAddress *a)
167 {
168         gint i;
169         GString *out;
170         gchar *ret;
171
172         if (a->addresses->len == 0)
173                 return NULL;
174
175         out = g_string_new ("");
176
177         for (i = 0; i < a->addresses->len; i++) {
178                 struct _address *addr = g_ptr_array_index (a->addresses, i);
179                 gchar *enc;
180
181                 if (i != 0)
182                         g_string_append (out, ", ");
183
184                 enc = camel_internet_address_format_address (addr->name, addr->address);
185                 g_string_append (out, enc);
186                 g_free (enc);
187         }
188
189         ret = out->str;
190         g_string_free (out, FALSE);
191
192         return ret;
193 }
194
195 static void
196 internet_address_remove (CamelAddress *a,
197                          gint index)
198 {
199         struct _address *addr;
200
201         if (index < 0 || index >= a->addresses->len)
202                 return;
203
204         addr = g_ptr_array_index (a->addresses, index);
205         g_free (addr->name);
206         g_free (addr->address);
207         g_free (addr);
208         g_ptr_array_remove_index (a->addresses, index);
209 }
210
211 static gint
212 internet_address_cat (CamelAddress *dest,
213                       CamelAddress *source)
214 {
215         gint i;
216
217         g_assert (CAMEL_IS_INTERNET_ADDRESS (source));
218
219         for (i = 0; i < source->addresses->len; i++) {
220                 struct _address *addr = g_ptr_array_index (source->addresses, i);
221                 camel_internet_address_add ((CamelInternetAddress *) dest, addr->name, addr->address);
222         }
223
224         return i;
225 }
226
227 static void
228 camel_internet_address_class_init (CamelInternetAddressClass *class)
229 {
230         CamelAddressClass *address_class;
231
232         address_class = CAMEL_ADDRESS_CLASS (class);
233         address_class->decode = internet_address_decode;
234         address_class->encode = internet_address_encode;
235         address_class->unformat = internet_address_unformat;
236         address_class->format = internet_address_format;
237         address_class->remove = internet_address_remove;
238         address_class->cat = internet_address_cat;
239 }
240
241 static void
242 camel_internet_address_init (CamelInternetAddress *internet_address)
243 {
244 }
245
246 /**
247  * camel_internet_address_new:
248  *
249  * Create a new #CamelInternetAddress object.
250  *
251  * Returns: a new #CamelInternetAddress object
252  **/
253 CamelInternetAddress *
254 camel_internet_address_new (void)
255 {
256         return g_object_new (CAMEL_TYPE_INTERNET_ADDRESS, NULL);
257 }
258
259 /**
260  * camel_internet_address_add:
261  * @addr: a #CamelInternetAddress object
262  * @name: name associated with the new address
263  * @address: routing address associated with the new address
264  *
265  * Add a new internet address to @addr.
266  *
267  * Returns: the index of added entry
268  **/
269 gint
270 camel_internet_address_add (CamelInternetAddress *addr,
271                             const gchar *name,
272                             const gchar *address)
273 {
274         struct _address *new;
275         gint index;
276
277         g_assert (CAMEL_IS_INTERNET_ADDRESS (addr));
278
279         new = g_malloc (sizeof (*new));
280         new->name = g_strdup (name);
281         new->address = g_strdup (address);
282         index = ((CamelAddress *) addr)->addresses->len;
283         g_ptr_array_add (((CamelAddress *) addr)->addresses, new);
284
285         return index;
286 }
287
288 /**
289  * camel_internet_address_get:
290  * @addr: a #CamelInternetAddress object
291  * @index: address's array index
292  * @namep: holder for the returned name, or %NULL, if not required.
293  * @addressp: holder for the returned address, or %NULL, if not required.
294  *
295  * Get the address at @index.
296  *
297  * Returns: %TRUE if such an address exists, or %FALSE otherwise
298  **/
299 gboolean
300 camel_internet_address_get (CamelInternetAddress *addr,
301                             gint index,
302                             const gchar **namep,
303                             const gchar **addressp)
304 {
305         struct _address *a;
306
307         g_assert (CAMEL_IS_INTERNET_ADDRESS (addr));
308
309         if (index < 0 || index >= ((CamelAddress *) addr)->addresses->len)
310                 return FALSE;
311
312         a = g_ptr_array_index (((CamelAddress *) addr)->addresses, index);
313         if (namep)
314                 *namep = a->name;
315         if (addressp)
316                 *addressp = a->address;
317         return TRUE;
318 }
319
320 /**
321  * camel_internet_address_find_name:
322  * @addr: a #CamelInternetAddress object
323  * @name: name to lookup
324  * @addressp: holder for address part, or %NULL, if not required.
325  *
326  * Find address by real name.
327  *
328  * Returns: the index of the address matching the name, or %-1 if no
329  * match was found
330  **/
331 gint
332 camel_internet_address_find_name (CamelInternetAddress *addr,
333                                   const gchar *name,
334                                   const gchar **addressp)
335 {
336         struct _address *a;
337         gint i, len;
338
339         g_assert (CAMEL_IS_INTERNET_ADDRESS (addr));
340
341         len = ((CamelAddress *) addr)->addresses->len;
342         for (i = 0; i < len; i++) {
343                 a = g_ptr_array_index (((CamelAddress *) addr)->addresses, i);
344                 if (a->name && !strcmp (a->name, name)) {
345                         if (addressp)
346                                 *addressp = a->address;
347                         return i;
348                 }
349         }
350         return -1;
351 }
352
353 /**
354  * camel_internet_address_find_address:
355  * @addr: a #CamelInternetAddress object
356  * @address: address to lookup
357  * @namep: holder for the matching name, or %NULL, if not required.
358  *
359  * Find an address by address.
360  *
361  * Returns: the index of the address, or %-1 if not found
362  **/
363 gint
364 camel_internet_address_find_address (CamelInternetAddress *addr,
365                                      const gchar *address,
366                                      const gchar **namep)
367 {
368         struct _address *a;
369         gint i, len;
370
371         g_assert (CAMEL_IS_INTERNET_ADDRESS (addr));
372
373         len = ((CamelAddress *) addr)->addresses->len;
374         for (i = 0; i < len; i++) {
375                 a = g_ptr_array_index (((CamelAddress *) addr)->addresses, i);
376                 if (!strcmp (a->address, address)) {
377                         if (namep)
378                                 *namep = a->name;
379                         return i;
380                 }
381         }
382         return -1;
383 }
384
385 static void
386 cia_encode_addrspec (GString *out,
387                      const gchar *addr)
388 {
389         const gchar *at, *p;
390
391         at = strchr (addr, '@');
392         if (at == NULL)
393                 goto append;
394
395         p = addr;
396         while (p < at) {
397                 gchar c = *p++;
398
399                 /* strictly by rfc, we should split local parts on dots.
400                  * however i think 2822 changes this, and not many clients grok it, so
401                  * just quote the whole local part if need be */
402                 if (!(camel_mime_is_atom (c) || c == '.')) {
403                         g_string_append_c (out, '"');
404
405                         p = addr;
406                         while (p < at) {
407                                 c = *p++;
408                                 if (c == '"' || c == '\\')
409                                         g_string_append_c (out, '\\');
410                                 g_string_append_c (out, c);
411                         }
412                         g_string_append_c (out, '"');
413                         g_string_append (out, p);
414
415                         return;
416                 }
417         }
418
419 append:
420         g_string_append (out, addr);
421 }
422
423 /**
424  * camel_internet_address_encode_address:
425  * @len: the length of the line the address is being appended to
426  * @name: the unencoded real name associated with the address
427  * @addr: the routing address
428  *
429  * Encode a single address ready for internet usage.  Header folding
430  * as per rfc822 is also performed, based on the length *@len.  If @len
431  * is %NULL, then no folding will occur.
432  *
433  * Note: The value at *@in will be updated based on any linewrapping done
434  *
435  * Returns: the encoded address
436  **/
437 gchar *
438 camel_internet_address_encode_address (gint *inlen,
439                                        const gchar *real,
440                                        const gchar *addr)
441 {
442         gchar *name = camel_header_encode_phrase ((const guchar *) real);
443         gchar *ret = NULL;
444         gint len = 0;
445         GString *out = g_string_new ("");
446
447         g_assert (addr);
448
449         if (inlen != NULL)
450                 len = *inlen;
451
452         if (name && name[0]) {
453                 if (inlen != NULL && (strlen (name) + len) > CAMEL_FOLD_SIZE) {
454                         gchar *folded = camel_header_address_fold (name, len);
455                         gchar *last;
456                         g_string_append (out, folded);
457                         g_free (folded);
458                         last = strrchr (out->str, '\n');
459                         if (last)
460                                 len = last - (out->str + out->len);
461                         else
462                                 len = out->len;
463                 } else {
464                         g_string_append (out, name);
465                         len += strlen (name);
466                 }
467         }
468
469         /* NOTE: Strictly speaking, we could and should split the
470          * internal address up if we need to, on atom or specials
471          * boundaries - however, to aid interoperability with mailers
472          * that will probably not handle this case, we will just move
473          * the whole address to its own line. */
474         if (inlen != NULL && (strlen (addr) + len) > CAMEL_FOLD_SIZE) {
475                 g_string_append (out, "\n\t");
476                 len = 1;
477         }
478
479         len -= out->len;
480
481         if (name && name[0])
482                 g_string_append_printf (out, " <");
483         cia_encode_addrspec (out, addr);
484         if (name && name[0])
485                 g_string_append_printf (out, ">");
486
487         len += out->len;
488
489         if (inlen != NULL)
490                 *inlen = len;
491
492         g_free (name);
493
494         ret = out->str;
495         g_string_free (out, FALSE);
496
497         return ret;
498 }
499
500 /**
501  * camel_internet_address_format_address:
502  * @name: a name, quotes may be stripped from it
503  * @addr: an rfc822 routing address
504  *
505  * Function to format a single address, suitable for display.
506  *
507  * Returns: a nicely formatted string containing the rfc822 address
508  **/
509 gchar *
510 camel_internet_address_format_address (const gchar *name,
511                                        const gchar *addr)
512 {
513         gchar *ret = NULL;
514
515         g_assert (addr);
516
517         if (name && name[0]) {
518                 const gchar *p = name;
519                 gchar *o, c;
520
521                 while ((c = *p++)) {
522                         if (c == '\"' || c == ',') {
523                                 o = ret = g_malloc (strlen (name) + 3 + strlen (addr) + 3 + 1);
524                                 p = name;
525                                 *o++ = '\"';
526                                 while ((c = *p++))
527                                         if (c != '\"')
528                                                 *o++ = c;
529                                 *o++ = '\"';
530                                 sprintf (o, " <%s>", addr);
531                                 d (printf ("encoded '%s' => '%s'\n", name, ret));
532                                 return ret;
533                         }
534                 }
535                 ret = g_strdup_printf ("%s <%s>", name, addr);
536         } else
537                 ret = g_strdup (addr);
538
539         return ret;
540 }