Whitespace cleanups.
[platform/upstream/evolution-data-server.git] / addressbook / libebook / e-contact.c
1 /* -*- Mode: C; tab-width: 8; indent-tabs-mode: t; c-basic-offset: 8 -*- */
2 /* e-contact.c
3  *
4  * Copyright (C) 1999-2008 Novell, Inc. (www.novell.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  * Author: Chris Toshok (toshok@ximian.com)
21  */
22
23 #if HAVE_CONFIG_H
24 #include <config.h>
25 #endif
26
27 #include <stdio.h>
28 #include <ctype.h>
29 #include <string.h>
30 #include <gio/gio.h>
31 #include <glib/gi18n-lib.h>
32 #include "e-contact.h"
33 #include "e-book.h"
34 #include "e-name-western.h"
35
36 #ifdef G_OS_WIN32
37 #include "libedataserver/e-data-server-util.h"
38 #undef LOCALEDIR
39 #define LOCALEDIR e_util_get_localedir ()
40 #endif
41
42 #define d(x)
43
44 #define E_CONTACT_GET_PRIVATE(obj) \
45         (G_TYPE_INSTANCE_GET_PRIVATE \
46         ((obj), E_TYPE_CONTACT, EContactPrivate))
47
48 G_DEFINE_TYPE (EContact, e_contact, E_TYPE_VCARD)
49
50 struct _EContactPrivate {
51         gchar *cached_strings[E_CONTACT_FIELD_LAST];
52 };
53
54 #define E_CONTACT_FIELD_TYPE_STRING       0x00000001   /* used for simple single valued attributes */
55 /*E_CONTACT_FIELD_TYPE_FLOAT*/
56 #define E_CONTACT_FIELD_TYPE_LIST         0x00000002   /* used for multivalued single attributes - the elements are of type gchar * */
57 #define E_CONTACT_FIELD_TYPE_MULTI        0x00000004   /* used for multivalued attributes - the elements are of type EVCardAttribute */
58 #define E_CONTACT_FIELD_TYPE_GETSET       0x00000008   /* used for attributes that need custom handling for getting/setting */
59 #define E_CONTACT_FIELD_TYPE_STRUCT       0x00000010   /* used for structured types (N and ADR properties, in particular) */
60 #define E_CONTACT_FIELD_TYPE_BOOLEAN      0x00000020   /* used for boolean types (WANTS_HTML) */
61
62 #define E_CONTACT_FIELD_TYPE_SYNTHETIC    0x10000000   /* used when there isn't a corresponding vcard field (such as email_1) */
63 #define E_CONTACT_FIELD_TYPE_LIST_ELEM    0x20000000   /* used when a synthetic attribute is a numbered list element */
64 #define E_CONTACT_FIELD_TYPE_MULTI_ELEM   0x40000000   /* used when we're looking for the nth attribute where more than 1 can be present in the vcard */
65 #define E_CONTACT_FIELD_TYPE_ATTR_TYPE    0x80000000   /* used when a synthetic attribute is flagged with a TYPE= that we'll be looking for */
66
67 typedef struct {
68         guint32 t;
69
70         EContactField field_id;
71         const gchar *vcard_field_name;
72         const gchar *field_name;      /* non translated */
73         const gchar *pretty_name;     /* translated */
74
75         gboolean read_only;
76
77         gint list_elem;
78         const gchar *attr_type1;
79         const gchar *attr_type2;
80
81         gpointer  (*struct_getter)(EContact *contact, EVCardAttribute *attribute);
82         void (*struct_setter)(EContact *contact, EVCardAttribute *attribute, gpointer data);
83
84         GType (*boxed_type_getter) (void);
85 } EContactFieldInfo;
86
87 static gpointer  photo_getter (EContact *contact, EVCardAttribute *attr);
88 static void photo_setter (EContact *contact, EVCardAttribute *attr, gpointer data);
89 static gpointer  geo_getter (EContact *contact, EVCardAttribute *attr);
90 static void geo_setter (EContact *contact, EVCardAttribute *attr, gpointer data);
91 static gpointer  fn_getter (EContact *contact, EVCardAttribute *attr);
92 static void fn_setter (EContact *contact, EVCardAttribute *attr, gpointer data);
93 static gpointer  n_getter (EContact *contact, EVCardAttribute *attr);
94 static void n_setter (EContact *contact, EVCardAttribute *attr, gpointer data);
95 static gpointer  fileas_getter (EContact *contact, EVCardAttribute *attr);
96 static void fileas_setter (EContact *contact, EVCardAttribute *attr, gpointer data);
97 static gpointer  adr_getter (EContact *contact, EVCardAttribute *attr);
98 static void adr_setter (EContact *contact, EVCardAttribute *attr, gpointer data);
99 static gpointer  date_getter (EContact *contact, EVCardAttribute *attr);
100 static void date_setter (EContact *contact, EVCardAttribute *attr, gpointer data);
101 static gpointer  cert_getter (EContact *contact, EVCardAttribute *attr);
102 static void cert_setter (EContact *contact, EVCardAttribute *attr, gpointer data);
103
104 #define STRING_FIELD(id,vc,n,pn,ro)  { E_CONTACT_FIELD_TYPE_STRING, (id), (vc), (n), (pn), (ro) }
105 #define BOOLEAN_FIELD(id,vc,n,pn,ro)  { E_CONTACT_FIELD_TYPE_BOOLEAN, (id), (vc), (n), (pn), (ro) }
106 #define LIST_FIELD(id,vc,n,pn,ro)      { E_CONTACT_FIELD_TYPE_LIST, (id), (vc), (n), (pn), (ro) }
107 #define MULTI_LIST_FIELD(id,vc,n,pn,ro) { E_CONTACT_FIELD_TYPE_MULTI, (id), (vc), (n), (pn), (ro) }
108 #define GETSET_FIELD(id,vc,n,pn,ro,get,set)    { E_CONTACT_FIELD_TYPE_STRING | E_CONTACT_FIELD_TYPE_GETSET, (id), (vc), (n), (pn), (ro), -1, NULL, NULL, (get), (set) }
109 #define STRUCT_FIELD(id,vc,n,pn,ro,get,set,ty)    { E_CONTACT_FIELD_TYPE_STRUCT | E_CONTACT_FIELD_TYPE_GETSET, (id), (vc), (n), (pn), (ro), -1, NULL, NULL, (get), (set), (ty) }
110 #define SYNTH_STR_FIELD(id,n,pn,ro)  { E_CONTACT_FIELD_TYPE_STRING | E_CONTACT_FIELD_TYPE_SYNTHETIC, (id), NULL, (n), (pn), (ro) }
111 #define LIST_ELEM_STR_FIELD(id,vc,n,pn,ro,nm) { E_CONTACT_FIELD_TYPE_LIST_ELEM | E_CONTACT_FIELD_TYPE_SYNTHETIC | E_CONTACT_FIELD_TYPE_STRING, (id), (vc), (n), (pn), (ro), (nm) }
112 #define MULTI_ELEM_STR_FIELD(id,vc,n,pn,ro,nm) { E_CONTACT_FIELD_TYPE_MULTI_ELEM | E_CONTACT_FIELD_TYPE_SYNTHETIC | E_CONTACT_FIELD_TYPE_STRING, (id), (vc), (n), (pn), (ro), (nm) }
113 #define ATTR_TYPE_STR_FIELD(id,vc,n,pn,ro,at1,nth) { E_CONTACT_FIELD_TYPE_ATTR_TYPE | E_CONTACT_FIELD_TYPE_SYNTHETIC | E_CONTACT_FIELD_TYPE_STRING, (id), (vc), (n), (pn), (ro), (nth), (at1), NULL }
114 #define ATTR_TYPE_GETSET_FIELD(id,vc,n,pn,ro,at1,nth,get,set) { E_CONTACT_FIELD_TYPE_ATTR_TYPE | E_CONTACT_FIELD_TYPE_GETSET, (id), (vc), (n), (pn), (ro), (nth), (at1), NULL, (get), (set) }
115 #define ATTR2_TYPE_STR_FIELD(id,vc,n,pn,ro,at1,at2,nth) { E_CONTACT_FIELD_TYPE_ATTR_TYPE | E_CONTACT_FIELD_TYPE_SYNTHETIC | E_CONTACT_FIELD_TYPE_STRING, (id), (vc), (n), (pn), (ro), (nth), (at1), (at2) }
116 #define ATTR_TYPE_STRUCT_FIELD(id,vc,n,pn,ro,at,get,set,ty) { E_CONTACT_FIELD_TYPE_ATTR_TYPE | E_CONTACT_FIELD_TYPE_SYNTHETIC | E_CONTACT_FIELD_TYPE_GETSET | E_CONTACT_FIELD_TYPE_STRUCT, (id), (vc), (n), (pn), (ro), 0, (at), NULL, (get), (set), (ty) }
117
118 /* This *must* be kept in the same order as the EContactField enum */
119 static const EContactFieldInfo field_info[] = {
120         {0,}, /* Dummy row as EContactField starts from 1 */
121         STRING_FIELD (E_CONTACT_UID,        EVC_UID,       "id",         N_("Unique ID"),  FALSE),
122         /* FILE_AS is not really a structured field - we use a getter/setter
123          * so we can generate its value if necessary in the getter */
124         /* Translators: This is an EContact field description, in this case it's a
125          * preferred user's description (or display name) of the contact. Note 'File' is a verb here. */
126         GETSET_FIELD (E_CONTACT_FILE_AS,    EVC_X_FILE_AS, "file_as", N_("File Under"),    FALSE, fileas_getter, fileas_setter),
127         /* URI of the book to which the contact belongs to */
128         STRING_FIELD (E_CONTACT_BOOK_URI, EVC_X_BOOK_URI, "book_uri", N_("Book URI"), FALSE),
129
130         /* Name fields */
131         /* FN isn't really a structured field - we use a getter/setter
132          * so we can set the N property (since evo 1.4 works fine with
133          * vcards that don't even have a N attribute.  *sigh*) */
134         GETSET_FIELD        (E_CONTACT_FULL_NAME,   EVC_FN,       "full_name",   N_("Full Name"),   FALSE, fn_getter, fn_setter),
135         LIST_ELEM_STR_FIELD (E_CONTACT_GIVEN_NAME,  EVC_N,        "given_name",  N_("Given Name"),  FALSE, 1),
136         LIST_ELEM_STR_FIELD (E_CONTACT_FAMILY_NAME, EVC_N,        "family_name", N_("Family Name"), FALSE, 0),
137         STRING_FIELD        (E_CONTACT_NICKNAME,    EVC_NICKNAME, "nickname",    N_("Nickname"),    FALSE),
138
139         /* Email fields */
140         MULTI_ELEM_STR_FIELD (E_CONTACT_EMAIL_1,    EVC_EMAIL,        "email_1",    N_("Email 1"),         FALSE, 0),
141         MULTI_ELEM_STR_FIELD (E_CONTACT_EMAIL_2,    EVC_EMAIL,        "email_2",    N_("Email 2"),         FALSE, 1),
142         MULTI_ELEM_STR_FIELD (E_CONTACT_EMAIL_3,    EVC_EMAIL,        "email_3",    N_("Email 3"),         FALSE, 2),
143         MULTI_ELEM_STR_FIELD (E_CONTACT_EMAIL_4,    EVC_EMAIL,        "email_4",    N_("Email 4"),         FALSE, 3),
144
145         STRING_FIELD         (E_CONTACT_MAILER,     EVC_MAILER,       "mailer",     N_("Mailer"),          FALSE),
146
147         /* Address Labels */
148         ATTR_TYPE_STR_FIELD (E_CONTACT_ADDRESS_LABEL_HOME,  EVC_LABEL, "address_label_home",  N_("Home Address Label"),  FALSE, "HOME", 0),
149         ATTR_TYPE_STR_FIELD (E_CONTACT_ADDRESS_LABEL_WORK,  EVC_LABEL, "address_label_work",  N_("Work Address Label"),  FALSE, "WORK", 0),
150         ATTR_TYPE_STR_FIELD (E_CONTACT_ADDRESS_LABEL_OTHER, EVC_LABEL, "address_label_other", N_("Other Address Label"), FALSE, "OTHER", 0),
151
152         /* Phone fields */
153         ATTR_TYPE_STR_FIELD  (E_CONTACT_PHONE_ASSISTANT,    EVC_TEL, "assistant_phone",   N_("Assistant Phone"),  FALSE, EVC_X_ASSISTANT, 0),
154         ATTR2_TYPE_STR_FIELD (E_CONTACT_PHONE_BUSINESS,     EVC_TEL, "business_phone",    N_("Business Phone"),   FALSE, "WORK", "VOICE",         0),
155         ATTR2_TYPE_STR_FIELD (E_CONTACT_PHONE_BUSINESS_2,   EVC_TEL, "business_phone_2",  N_("Business Phone 2"), FALSE, "WORK", "VOICE",         1),
156         ATTR2_TYPE_STR_FIELD (E_CONTACT_PHONE_BUSINESS_FAX, EVC_TEL, "business_fax",      N_("Business Fax"),     FALSE, "WORK", "FAX",           0),
157         ATTR_TYPE_STR_FIELD  (E_CONTACT_PHONE_CALLBACK,     EVC_TEL, "callback_phone",    N_("Callback Phone"),   FALSE, EVC_X_CALLBACK,  0),
158         ATTR_TYPE_STR_FIELD  (E_CONTACT_PHONE_CAR,          EVC_TEL, "car_phone",         N_("Car Phone"),        FALSE, "CAR",                   0),
159         ATTR_TYPE_STR_FIELD  (E_CONTACT_PHONE_COMPANY,      EVC_TEL, "company_phone",     N_("Company Phone"),    FALSE, EVC_X_COMPANY,   0),
160         ATTR2_TYPE_STR_FIELD (E_CONTACT_PHONE_HOME,         EVC_TEL, "home_phone",        N_("Home Phone"),       FALSE, "HOME", "VOICE",         0),
161         ATTR2_TYPE_STR_FIELD (E_CONTACT_PHONE_HOME_2,       EVC_TEL, "home_phone_2",      N_("Home Phone 2"),     FALSE, "HOME", "VOICE",         1),
162         ATTR2_TYPE_STR_FIELD (E_CONTACT_PHONE_HOME_FAX,     EVC_TEL, "home_fax",          N_("Home Fax"),         FALSE, "HOME", "FAX",           0),
163         ATTR_TYPE_STR_FIELD  (E_CONTACT_PHONE_ISDN,         EVC_TEL, "isdn_phone",        N_("ISDN"),             FALSE, "ISDN",                  0),
164         ATTR_TYPE_STR_FIELD  (E_CONTACT_PHONE_MOBILE,       EVC_TEL, "mobile_phone",      N_("Mobile Phone"),     FALSE, "CELL",                  0),
165         ATTR_TYPE_STR_FIELD  (E_CONTACT_PHONE_OTHER,        EVC_TEL, "other_phone",       N_("Other Phone"),      FALSE, "VOICE",                 0),
166         ATTR_TYPE_STR_FIELD  (E_CONTACT_PHONE_OTHER_FAX,    EVC_TEL, "other_fax",         N_("Other Fax"),        FALSE, "FAX",                   0),
167         ATTR_TYPE_STR_FIELD  (E_CONTACT_PHONE_PAGER,        EVC_TEL, "pager",             N_("Pager"),            FALSE, "PAGER",                 0),
168         ATTR_TYPE_STR_FIELD  (E_CONTACT_PHONE_PRIMARY,      EVC_TEL, "primary_phone",     N_("Primary Phone"),    FALSE, "PREF",                  0),
169         ATTR_TYPE_STR_FIELD  (E_CONTACT_PHONE_RADIO,        EVC_TEL, "radio",             N_("Radio"),            FALSE, EVC_X_RADIO,     0),
170         ATTR_TYPE_STR_FIELD  (E_CONTACT_PHONE_TELEX,        EVC_TEL, "telex",             N_("Telex"),            FALSE, EVC_X_TELEX,     0),
171         /* To translators: TTY is Teletypewriter */
172         ATTR_TYPE_STR_FIELD  (E_CONTACT_PHONE_TTYTDD,       EVC_TEL, "tty",               N_("TTY"),              FALSE, EVC_X_TTYTDD,    0),
173
174         /* Organizational fields */
175         LIST_ELEM_STR_FIELD (E_CONTACT_ORG,      EVC_ORG, "org",      N_("Organization"),        FALSE, 0),
176         LIST_ELEM_STR_FIELD (E_CONTACT_ORG_UNIT, EVC_ORG, "org_unit", N_("Organizational Unit"), FALSE, 1),
177         LIST_ELEM_STR_FIELD (E_CONTACT_OFFICE,   EVC_ORG, "office",   N_("Office"),              FALSE, 2),
178         STRING_FIELD    (E_CONTACT_TITLE,     EVC_TITLE,       "title",     N_("Title"),           FALSE),
179         STRING_FIELD    (E_CONTACT_ROLE,      EVC_ROLE,        "role",      N_("Role"),            FALSE),
180         STRING_FIELD    (E_CONTACT_MANAGER,   EVC_X_MANAGER,   "manager",   N_("Manager"),         FALSE),
181         STRING_FIELD    (E_CONTACT_ASSISTANT, EVC_X_ASSISTANT, "assistant", N_("Assistant"),       FALSE),
182
183         /* Web fields */
184         STRING_FIELD (E_CONTACT_HOMEPAGE_URL, EVC_URL,         "homepage_url", N_("Homepage URL"), FALSE),
185         STRING_FIELD (E_CONTACT_BLOG_URL,     EVC_X_BLOG_URL,  "blog_url",     N_("Weblog URL"),   FALSE),
186
187         /* Contact categories */
188         SYNTH_STR_FIELD (E_CONTACT_CATEGORIES,                    "categories",    N_("Categories"),    FALSE),
189
190         /* Collaboration fields */
191         STRING_FIELD (E_CONTACT_CALENDAR_URI, EVC_CALURI,      "caluri",     N_("Calendar URI"),  FALSE),
192         STRING_FIELD (E_CONTACT_FREEBUSY_URL, EVC_FBURL,       "fburl",       N_("Free/Busy URL"), FALSE),
193         STRING_FIELD (E_CONTACT_ICS_CALENDAR, EVC_ICSCALENDAR, "icscalendar", N_("ICS Calendar"),  FALSE),
194         STRING_FIELD (E_CONTACT_VIDEO_URL,    EVC_X_VIDEO_URL, "video_url",    N_("Video Conferencing URL"),   FALSE),
195
196         /* Misc fields */
197         STRING_FIELD (E_CONTACT_SPOUSE, EVC_X_SPOUSE,    "spouse", N_("Spouse's Name"), FALSE),
198         STRING_FIELD (E_CONTACT_NOTE,   EVC_NOTE,        "note",   N_("Note"),          FALSE),
199
200         /* Instant messaging fields */
201         ATTR_TYPE_STR_FIELD (E_CONTACT_IM_AIM_HOME_1,    EVC_X_AIM,    "im_aim_home_1",    N_("AIM Home Screen Name 1"),    FALSE, "HOME", 0),
202         ATTR_TYPE_STR_FIELD (E_CONTACT_IM_AIM_HOME_2,    EVC_X_AIM,    "im_aim_home_2",    N_("AIM Home Screen Name 2"),    FALSE, "HOME", 1),
203         ATTR_TYPE_STR_FIELD (E_CONTACT_IM_AIM_HOME_3,    EVC_X_AIM,    "im_aim_home_3",    N_("AIM Home Screen Name 3"),    FALSE, "HOME", 2),
204         ATTR_TYPE_STR_FIELD (E_CONTACT_IM_AIM_WORK_1,    EVC_X_AIM,    "im_aim_work_1",    N_("AIM Work Screen Name 1"),    FALSE, "WORK", 0),
205         ATTR_TYPE_STR_FIELD (E_CONTACT_IM_AIM_WORK_2,    EVC_X_AIM,    "im_aim_work_2",    N_("AIM Work Screen Name 2"),    FALSE, "WORK", 1),
206         ATTR_TYPE_STR_FIELD (E_CONTACT_IM_AIM_WORK_3,    EVC_X_AIM,    "im_aim_work_3",    N_("AIM Work Screen Name 3"),    FALSE, "WORK", 2),
207         ATTR_TYPE_STR_FIELD (E_CONTACT_IM_GROUPWISE_HOME_1, EVC_X_GROUPWISE, "im_groupwise_home_1", N_("GroupWise Home Screen Name 1"),    FALSE, "HOME", 0),
208         ATTR_TYPE_STR_FIELD (E_CONTACT_IM_GROUPWISE_HOME_2, EVC_X_GROUPWISE, "im_groupwise_home_2", N_("GroupWise Home Screen Name 2"),    FALSE, "HOME", 1),
209         ATTR_TYPE_STR_FIELD (E_CONTACT_IM_GROUPWISE_HOME_3, EVC_X_GROUPWISE, "im_groupwise_home_3", N_("GroupWise Home Screen Name 3"),    FALSE, "HOME", 2),
210         ATTR_TYPE_STR_FIELD (E_CONTACT_IM_GROUPWISE_WORK_1, EVC_X_GROUPWISE, "im_groupwise_work_1", N_("GroupWise Work Screen Name 1"),    FALSE, "WORK", 0),
211         ATTR_TYPE_STR_FIELD (E_CONTACT_IM_GROUPWISE_WORK_2, EVC_X_GROUPWISE, "im_groupwise_work_2", N_("GroupWise Work Screen Name 2"),    FALSE, "WORK", 1),
212         ATTR_TYPE_STR_FIELD (E_CONTACT_IM_GROUPWISE_WORK_3, EVC_X_GROUPWISE, "im_groupwise_work_3", N_("GroupWise Work Screen Name 3"),    FALSE, "WORK", 2),
213         ATTR_TYPE_STR_FIELD (E_CONTACT_IM_JABBER_HOME_1, EVC_X_JABBER, "im_jabber_home_1", N_("Jabber Home ID 1"),          FALSE, "HOME", 0),
214         ATTR_TYPE_STR_FIELD (E_CONTACT_IM_JABBER_HOME_2, EVC_X_JABBER, "im_jabber_home_2", N_("Jabber Home ID 2"),          FALSE, "HOME", 1),
215         ATTR_TYPE_STR_FIELD (E_CONTACT_IM_JABBER_HOME_3, EVC_X_JABBER, "im_jabber_home_3", N_("Jabber Home ID 3"),          FALSE, "HOME", 2),
216         ATTR_TYPE_STR_FIELD (E_CONTACT_IM_JABBER_WORK_1, EVC_X_JABBER, "im_jabber_work_1", N_("Jabber Work ID 1"),          FALSE, "WORK", 0),
217         ATTR_TYPE_STR_FIELD (E_CONTACT_IM_JABBER_WORK_2, EVC_X_JABBER, "im_jabber_work_3", N_("Jabber Work ID 2"),          FALSE, "WORK", 1),
218         ATTR_TYPE_STR_FIELD (E_CONTACT_IM_JABBER_WORK_3, EVC_X_JABBER, "im_jabber_work_2", N_("Jabber Work ID 3"),          FALSE, "WORK", 2),
219         ATTR_TYPE_STR_FIELD (E_CONTACT_IM_YAHOO_HOME_1,  EVC_X_YAHOO,  "im_yahoo_home_1",  N_("Yahoo! Home Screen Name 1"), FALSE, "HOME", 0),
220         ATTR_TYPE_STR_FIELD (E_CONTACT_IM_YAHOO_HOME_2,  EVC_X_YAHOO,  "im_yahoo_home_2",  N_("Yahoo! Home Screen Name 2"), FALSE, "HOME", 1),
221         ATTR_TYPE_STR_FIELD (E_CONTACT_IM_YAHOO_HOME_3,  EVC_X_YAHOO,  "im_yahoo_home_3",  N_("Yahoo! Home Screen Name 3"), FALSE, "HOME", 2),
222         ATTR_TYPE_STR_FIELD (E_CONTACT_IM_YAHOO_WORK_1,  EVC_X_YAHOO,  "im_yahoo_work_1",  N_("Yahoo! Work Screen Name 1"), FALSE, "WORK", 0),
223         ATTR_TYPE_STR_FIELD (E_CONTACT_IM_YAHOO_WORK_2,  EVC_X_YAHOO,  "im_yahoo_work_2",  N_("Yahoo! Work Screen Name 2"), FALSE, "WORK", 1),
224         ATTR_TYPE_STR_FIELD (E_CONTACT_IM_YAHOO_WORK_3,  EVC_X_YAHOO,  "im_yahoo_work_3",  N_("Yahoo! Work Screen Name 3"), FALSE, "WORK", 2),
225         ATTR_TYPE_STR_FIELD (E_CONTACT_IM_MSN_HOME_1,    EVC_X_MSN,    "im_msn_home_1",    N_("MSN Home Screen Name 1"),    FALSE, "HOME", 0),
226         ATTR_TYPE_STR_FIELD (E_CONTACT_IM_MSN_HOME_2,    EVC_X_MSN,    "im_msn_home_2",    N_("MSN Home Screen Name 2"),    FALSE, "HOME", 1),
227         ATTR_TYPE_STR_FIELD (E_CONTACT_IM_MSN_HOME_3,    EVC_X_MSN,    "im_msn_home_3",    N_("MSN Home Screen Name 3"),    FALSE, "HOME", 2),
228         ATTR_TYPE_STR_FIELD (E_CONTACT_IM_MSN_WORK_1,    EVC_X_MSN,    "im_msn_work_1",    N_("MSN Work Screen Name 1"),    FALSE, "WORK", 0),
229         ATTR_TYPE_STR_FIELD (E_CONTACT_IM_MSN_WORK_2,    EVC_X_MSN,    "im_msn_work_2",    N_("MSN Work Screen Name 2"),    FALSE, "WORK", 1),
230         ATTR_TYPE_STR_FIELD (E_CONTACT_IM_MSN_WORK_3,    EVC_X_MSN,    "im_msn_work_3",    N_("MSN Work Screen Name 3"),    FALSE, "WORK", 2),
231         ATTR_TYPE_STR_FIELD (E_CONTACT_IM_ICQ_HOME_1,    EVC_X_ICQ,    "im_icq_home_1",    N_("ICQ Home ID 1"),             FALSE, "HOME", 0),
232         ATTR_TYPE_STR_FIELD (E_CONTACT_IM_ICQ_HOME_2,    EVC_X_ICQ,    "im_icq_home_2",    N_("ICQ Home ID 2"),             FALSE, "HOME", 1),
233         ATTR_TYPE_STR_FIELD (E_CONTACT_IM_ICQ_HOME_3,    EVC_X_ICQ,    "im_icq_home_3",    N_("ICQ Home ID 3"),             FALSE, "HOME", 2),
234         ATTR_TYPE_STR_FIELD (E_CONTACT_IM_ICQ_WORK_1,    EVC_X_ICQ,    "im_icq_work_1",    N_("ICQ Work ID 1"),             FALSE, "WORK", 0),
235         ATTR_TYPE_STR_FIELD (E_CONTACT_IM_ICQ_WORK_2,    EVC_X_ICQ,    "im_icq_work_2",    N_("ICQ Work ID 2"),             FALSE, "WORK", 1),
236         ATTR_TYPE_STR_FIELD (E_CONTACT_IM_ICQ_WORK_3,    EVC_X_ICQ,    "im_icq_work_3",    N_("ICQ Work ID 3"),             FALSE, "WORK", 2),
237
238         /* Last modified time */
239         STRING_FIELD (E_CONTACT_REV, EVC_REV, "Rev", N_("Last Revision"), FALSE),
240         /* Translators: This is an EContact field description, in this case it's a
241          * virtual field, which returns either name of the contact or the organization
242          * name, recognized by multiple other fields, where the first filled is used. */
243         SYNTH_STR_FIELD     (E_CONTACT_NAME_OR_ORG,               "name_or_org", N_("Name or Org"), TRUE),
244
245         /* Address fields */
246         MULTI_LIST_FIELD       (E_CONTACT_ADDRESS,       EVC_ADR, "address",       N_("Address List"),  FALSE),
247         ATTR_TYPE_STRUCT_FIELD (E_CONTACT_ADDRESS_HOME,  EVC_ADR, "address_home",  N_("Home Address"),  FALSE, "HOME",  adr_getter, adr_setter, e_contact_address_get_type),
248         ATTR_TYPE_STRUCT_FIELD (E_CONTACT_ADDRESS_WORK,  EVC_ADR, "address_work",  N_("Work Address"),  FALSE, "WORK",  adr_getter, adr_setter, e_contact_address_get_type),
249         ATTR_TYPE_STRUCT_FIELD (E_CONTACT_ADDRESS_OTHER, EVC_ADR, "address_other", N_("Other Address"), FALSE, "OTHER", adr_getter, adr_setter, e_contact_address_get_type),
250
251         /* Contact categories */
252         LIST_FIELD      (E_CONTACT_CATEGORY_LIST, EVC_CATEGORIES, "category_list", N_("Category List"), FALSE),
253
254         /* Photo/Logo */
255         STRUCT_FIELD    (E_CONTACT_PHOTO, EVC_PHOTO, "photo", N_("Photo"), FALSE, photo_getter, photo_setter, e_contact_photo_get_type),
256         STRUCT_FIELD    (E_CONTACT_LOGO,  EVC_LOGO,  "logo",  N_("Logo"),  FALSE, photo_getter, photo_setter, e_contact_photo_get_type),
257
258         /* Translators: This is an EContact field description, in this case it's a name
259          * of the contact, as specified in http://tools.ietf.org/html/rfc6350#section-6.2.2 */
260         STRUCT_FIELD        (E_CONTACT_NAME,        EVC_N,        "name", N_("Name"),        FALSE, n_getter, n_setter, e_contact_name_get_type),
261         MULTI_LIST_FIELD     (E_CONTACT_EMAIL,      EVC_EMAIL,        "email",      N_("Email List"),      FALSE),
262
263         /* Instant messaging fields */
264         MULTI_LIST_FIELD (E_CONTACT_IM_AIM,       EVC_X_AIM,       "im_aim",       N_("AIM Screen Name List"),    FALSE),
265         MULTI_LIST_FIELD (E_CONTACT_IM_GROUPWISE, EVC_X_GROUPWISE, "im_groupwise", N_("GroupWise ID List"),       FALSE),
266         MULTI_LIST_FIELD (E_CONTACT_IM_JABBER,    EVC_X_JABBER,    "im_jabber",    N_("Jabber ID List"),          FALSE),
267         MULTI_LIST_FIELD (E_CONTACT_IM_YAHOO,     EVC_X_YAHOO,     "im_yahoo",     N_("Yahoo! Screen Name List"), FALSE),
268         MULTI_LIST_FIELD (E_CONTACT_IM_MSN,       EVC_X_MSN,       "im_msn",       N_("MSN Screen Name List"),    FALSE),
269         MULTI_LIST_FIELD (E_CONTACT_IM_ICQ,       EVC_X_ICQ,       "im_icq",       N_("ICQ ID List"),             FALSE),
270
271         BOOLEAN_FIELD        (E_CONTACT_WANTS_HTML, EVC_X_WANTS_HTML, "wants_html", N_("Wants HTML Mail"), FALSE),
272
273         /* Translators: This is an EContact field description, in this case it's a
274          * field describing whether it's a Contact list (list of email addresses) or a
275          * regular contact for one person/organization/... */
276         BOOLEAN_FIELD (E_CONTACT_IS_LIST,             EVC_X_LIST, "list", N_("List"), FALSE),
277         /* Translators: This is an EContact field description, in this case it's a flag
278          * used to determine whether when sending to Contact lists the addresses should be
279          * shown or not to other recipients - basically whether to use BCC field or CC
280          * message header when sending messages to this Contact list. */
281         BOOLEAN_FIELD (E_CONTACT_LIST_SHOW_ADDRESSES, EVC_X_LIST_SHOW_ADDRESSES, "list_show_addresses", N_("List Shows Addresses"), FALSE),
282
283         STRUCT_FIELD (E_CONTACT_BIRTH_DATE,  EVC_BDAY,          "birth_date",  N_("Birth Date"), FALSE, date_getter, date_setter, e_contact_date_get_type),
284         STRUCT_FIELD (E_CONTACT_ANNIVERSARY, EVC_X_ANNIVERSARY, "anniversary", N_("Anniversary"), FALSE, date_getter, date_setter, e_contact_date_get_type),
285
286         /* Security fields */
287         ATTR_TYPE_STRUCT_FIELD (E_CONTACT_X509_CERT,  EVC_KEY, "x509Cert",  N_("X.509 Certificate"), FALSE, "X509", cert_getter, cert_setter, e_contact_cert_get_type),
288
289         ATTR_TYPE_STR_FIELD (E_CONTACT_IM_GADUGADU_HOME_1,  EVC_X_GADUGADU,  "im_gadugadu_home_1",  N_("Gadu-Gadu Home ID 1"), FALSE, "HOME", 0),
290         ATTR_TYPE_STR_FIELD (E_CONTACT_IM_GADUGADU_HOME_2,  EVC_X_GADUGADU,  "im_gadugadu_home_2",  N_("Gadu-Gadu Home ID 2"), FALSE, "HOME", 1),
291         ATTR_TYPE_STR_FIELD (E_CONTACT_IM_GADUGADU_HOME_3,  EVC_X_GADUGADU,  "im_gadugadu_home_3",  N_("Gadu-Gadu Home ID 3"), FALSE, "HOME", 2),
292         ATTR_TYPE_STR_FIELD (E_CONTACT_IM_GADUGADU_WORK_1,  EVC_X_GADUGADU,  "im_gadugadu_work_1",  N_("Gadu-Gadu Work ID 1"), FALSE, "WORK", 0),
293         ATTR_TYPE_STR_FIELD (E_CONTACT_IM_GADUGADU_WORK_2,  EVC_X_GADUGADU,  "im_gadugadu_work_2",  N_("Gadu-Gadu Work ID 2"), FALSE, "WORK", 1),
294         ATTR_TYPE_STR_FIELD (E_CONTACT_IM_GADUGADU_WORK_3,  EVC_X_GADUGADU,  "im_gadugadu_work_3",  N_("Gadu-Gadu Work ID 3"), FALSE, "WORK", 2),
295         MULTI_LIST_FIELD (E_CONTACT_IM_GADUGADU,  EVC_X_GADUGADU,  "im_gadugadu", N_("Gadu-Gadu ID List"), FALSE),
296
297         /* Geo information */
298         STRUCT_FIELD    (E_CONTACT_GEO,  EVC_GEO, "geo",  N_("Geographic Information"),  FALSE, geo_getter, geo_setter, e_contact_geo_get_type),
299
300         MULTI_LIST_FIELD     (E_CONTACT_TEL,      EVC_TEL,        "phone",      N_("Telephone"),      FALSE),
301
302         ATTR_TYPE_STR_FIELD (E_CONTACT_IM_SKYPE_HOME_1,  EVC_X_SKYPE,  "im_skype_home_1",  N_("Skype Home Name 1"),         FALSE, "HOME", 0),
303         ATTR_TYPE_STR_FIELD (E_CONTACT_IM_SKYPE_HOME_2,  EVC_X_SKYPE,  "im_skype_home_2",  N_("Skype Home Name 2"),         FALSE, "HOME", 1),
304         ATTR_TYPE_STR_FIELD (E_CONTACT_IM_SKYPE_HOME_3,  EVC_X_SKYPE,  "im_skype_home_3",  N_("Skype Home Name 3"),         FALSE, "HOME", 2),
305         ATTR_TYPE_STR_FIELD (E_CONTACT_IM_SKYPE_WORK_1,  EVC_X_SKYPE,  "im_skype_work_1",  N_("Skype Work Name 1"),         FALSE, "WORK", 0),
306         ATTR_TYPE_STR_FIELD (E_CONTACT_IM_SKYPE_WORK_2,  EVC_X_SKYPE,  "im_skype_work_2",  N_("Skype Work Name 2"),         FALSE, "WORK", 1),
307         ATTR_TYPE_STR_FIELD (E_CONTACT_IM_SKYPE_WORK_3,  EVC_X_SKYPE,  "im_skype_work_3",  N_("Skype Work Name 3"),         FALSE, "WORK", 2),
308         MULTI_LIST_FIELD (E_CONTACT_IM_SKYPE,     EVC_X_SKYPE,     "im_skype",     N_("Skype Name List"),         FALSE),
309
310         MULTI_LIST_FIELD (E_CONTACT_SIP,          EVC_X_SIP,    "sip",    N_("SIP address"),          FALSE),
311
312         ATTR_TYPE_STR_FIELD (E_CONTACT_IM_GOOGLE_TALK_HOME_1,  EVC_X_GOOGLE_TALK,  "im_google_talk_home_1",  N_("Google Talk Home Name 1"),         FALSE, "HOME", 0),
313         ATTR_TYPE_STR_FIELD (E_CONTACT_IM_GOOGLE_TALK_HOME_2,  EVC_X_GOOGLE_TALK,  "im_google_talk_home_2",  N_("Google Talk Home Name 2"),         FALSE, "HOME", 1),
314         ATTR_TYPE_STR_FIELD (E_CONTACT_IM_GOOGLE_TALK_HOME_3,  EVC_X_GOOGLE_TALK,  "im_google_talk_home_3",  N_("Google Talk Home Name 3"),         FALSE, "HOME", 2),
315         ATTR_TYPE_STR_FIELD (E_CONTACT_IM_GOOGLE_TALK_WORK_1,  EVC_X_GOOGLE_TALK,  "im_google_talk_work_1",  N_("Google Talk Work Name 1"),         FALSE, "WORK", 0),
316         ATTR_TYPE_STR_FIELD (E_CONTACT_IM_GOOGLE_TALK_WORK_2,  EVC_X_GOOGLE_TALK,  "im_google_talk_work_2",  N_("Google Talk Work Name 2"),         FALSE, "WORK", 1),
317         ATTR_TYPE_STR_FIELD (E_CONTACT_IM_GOOGLE_TALK_WORK_3,  EVC_X_GOOGLE_TALK,  "im_google_talk_work_3",  N_("Google Talk Work Name 3"),         FALSE, "WORK", 2),
318         MULTI_LIST_FIELD (E_CONTACT_IM_GOOGLE_TALK,       EVC_X_GOOGLE_TALK,     "im_google_talk",     N_("Google Talk Name List"),         FALSE),
319
320         MULTI_LIST_FIELD (E_CONTACT_IM_TWITTER,   EVC_X_TWITTER,     "im_twitter",     N_("Twitter Name List"),         FALSE)
321 };
322
323 #undef LIST_ELEM_STR_FIELD
324 #undef STRING_FIELD
325 #undef SYNTH_STR_FIELD
326 #undef LIST_FIELD
327 #undef GETSET_FIELD
328
329 static void e_contact_set_property (GObject *object, guint prop_id, const GValue *value, GParamSpec *pspec);
330 static void e_contact_get_property (GObject *object, guint prop_id, GValue *value, GParamSpec *pspec);
331
332 static void
333 e_contact_finalize (GObject *object)
334 {
335         EContactPrivate *priv;
336         gint i;
337
338         priv = E_CONTACT_GET_PRIVATE (object);
339
340         for (i = E_CONTACT_FIELD_FIRST; i < E_CONTACT_FIELD_LAST; i++)
341                 g_free (priv->cached_strings[i]);
342
343         /* Chain up to parent's finalize() method. */
344         G_OBJECT_CLASS (e_contact_parent_class)->finalize (object);
345 }
346
347 static void
348 e_contact_class_init (EContactClass *class)
349 {
350         GObjectClass *object_class;
351         gint i;
352
353         g_type_class_add_private (class, sizeof (EContactPrivate));
354
355         object_class = G_OBJECT_CLASS (class);
356         object_class->set_property = e_contact_set_property;
357         object_class->get_property = e_contact_get_property;
358         object_class->finalize = e_contact_finalize;
359
360         for (i = E_CONTACT_FIELD_FIRST; i < E_CONTACT_FIELD_LAST; i++) {
361                 GParamSpec *pspec = NULL;
362
363                 /* Verify the table is correctly ordered */
364                 g_assert (i == field_info[i].field_id);
365
366                 if (field_info[i].t & E_CONTACT_FIELD_TYPE_STRING)
367                         pspec = g_param_spec_string (field_info[i].field_name,
368                                                      _(field_info[i].pretty_name),
369                                                     field_info[i].pretty_name,
370                                                      NULL,
371                                                      (field_info[i].read_only ? G_PARAM_READABLE : G_PARAM_READWRITE)
372                                                      | G_PARAM_STATIC_NICK | G_PARAM_STATIC_BLURB);
373                 else if (field_info[i].t & E_CONTACT_FIELD_TYPE_BOOLEAN)
374                         pspec = g_param_spec_boolean (field_info[i].field_name,
375                                                       _(field_info[i].pretty_name),
376                                                     field_info[i].pretty_name,
377                                                       FALSE,
378                                                      (field_info[i].read_only ? G_PARAM_READABLE : G_PARAM_READWRITE)
379                                                      | G_PARAM_STATIC_NICK | G_PARAM_STATIC_BLURB);
380                 else if (field_info[i].t & E_CONTACT_FIELD_TYPE_STRUCT)
381                         pspec = g_param_spec_boxed (field_info[i].field_name,
382                                                     _(field_info[i].pretty_name),
383                                                     field_info[i].pretty_name,
384                                                     field_info[i].boxed_type_getter (),
385                                                      (field_info[i].read_only ? G_PARAM_READABLE : G_PARAM_READWRITE)
386                                                      | G_PARAM_STATIC_NICK | G_PARAM_STATIC_BLURB);
387                 else
388                         pspec = g_param_spec_pointer (field_info[i].field_name,
389                                                       _(field_info[i].pretty_name),
390                                                     field_info[i].pretty_name,
391                                                      (field_info[i].read_only ? G_PARAM_READABLE : G_PARAM_READWRITE)
392                                                      | G_PARAM_STATIC_NICK | G_PARAM_STATIC_BLURB);
393
394                 g_object_class_install_property (object_class, field_info[i].field_id,
395                                                  pspec);
396         }
397 }
398
399 static void
400 e_contact_init (EContact *ec)
401 {
402         ec->priv = E_CONTACT_GET_PRIVATE (ec);
403 }
404
405 static gpointer
406 geo_getter (EContact *contact,
407             EVCardAttribute *attr)
408 {
409         if (attr) {
410                 GList *p = e_vcard_attribute_get_values (attr);
411                 EContactGeo *geo = g_new0 (EContactGeo, 1);
412
413                 geo->latitude  = (p && p->data ? g_ascii_strtod (p->data, NULL) : 0); if (p) p = p->next;
414                 geo->longitude = (p && p->data ? g_ascii_strtod (p->data, NULL) : 0);
415
416                 return geo;
417         }
418         else
419                 return NULL;
420 }
421
422 static void
423 geo_setter (EContact *contact,
424             EVCardAttribute *attr,
425             gpointer data)
426 {
427         EContactGeo *geo = data;
428         gchar buf[G_ASCII_DTOSTR_BUF_SIZE];
429
430         e_vcard_attribute_add_value
431                 (attr, g_ascii_dtostr (buf, sizeof (buf), geo->latitude));
432
433         e_vcard_attribute_add_value
434                 (attr, g_ascii_dtostr (buf, sizeof (buf), geo->longitude));
435 }
436
437 static gpointer
438 photo_getter (EContact *contact,
439               EVCardAttribute *attr)
440 {
441         GList *values;
442
443         if (!attr)
444                 return NULL;
445
446         values = e_vcard_attribute_get_param (attr, EVC_ENCODING);
447         if (values && (g_ascii_strcasecmp (values->data, "b") == 0 ||
448                        /* second for photo vCard 2.1 support */
449                        g_ascii_strcasecmp (values->data, "base64") == 0)) {
450                 values = e_vcard_attribute_get_values_decoded (attr);
451                 if (values && values->data) {
452                         GString *s = values->data;
453                         EContactPhoto *photo;
454
455                         if (!s->len)
456                                 return NULL;
457
458                         photo = g_new0 (EContactPhoto, 1);
459                         photo->type = E_CONTACT_PHOTO_TYPE_INLINED;
460                         photo->data.inlined.length = s->len;
461                         photo->data.inlined.data = g_malloc (photo->data.inlined.length);
462                         memcpy (photo->data.inlined.data, s->str, photo->data.inlined.length);
463
464                         values = e_vcard_attribute_get_param (attr, EVC_TYPE);
465                         if (values && values->data)
466                                 photo->data.inlined.mime_type = g_strdup_printf("image/%s", (gchar *)values->data);
467                         return photo;
468                 }
469         }
470
471         values = e_vcard_attribute_get_param (attr, EVC_VALUE);
472         if (values && g_ascii_strcasecmp (values->data, "uri") == 0) {
473                 EContactPhoto *photo;
474                 photo = g_new0 (EContactPhoto, 1);
475                 photo->type = E_CONTACT_PHOTO_TYPE_URI;
476                 photo->data.uri = e_vcard_attribute_get_value (attr);
477                 return photo;
478         }
479         return NULL;
480 }
481
482 static void
483 photo_setter (EContact *contact,
484               EVCardAttribute *attr,
485               gpointer data)
486 {
487         EContactPhoto *photo = data;
488         const gchar *image_type, *p;
489
490         switch (photo->type) {
491         case E_CONTACT_PHOTO_TYPE_INLINED:
492                 g_return_if_fail (photo->data.inlined.length > 0);
493
494                 e_vcard_attribute_add_param_with_value (attr,
495                                                         e_vcard_attribute_param_new (EVC_ENCODING),
496                                                         "b");
497                 if (photo->data.inlined.mime_type && (p = strchr (photo->data.inlined.mime_type, '/'))) {
498                         image_type = p + 1;
499                 } else {
500                         image_type = "X-EVOLUTION-UNKNOWN";
501                 }
502                 e_vcard_attribute_add_param_with_value (attr,
503                                                         e_vcard_attribute_param_new (EVC_TYPE),
504                                                         image_type);
505
506                 e_vcard_attribute_add_value_decoded (attr, (gchar *) photo->data.inlined.data, photo->data.inlined.length);
507                 break;
508         case E_CONTACT_PHOTO_TYPE_URI:
509                 e_vcard_attribute_add_param_with_value (attr,
510                                                         e_vcard_attribute_param_new (EVC_VALUE),
511                                                         "uri");
512                 e_vcard_attribute_add_value (attr, photo->data.uri);
513                 break;
514         default:
515                 g_warning ("Unknown EContactPhotoType %d", photo->type);
516                 break;
517         }
518 }
519
520 \f
521 static gpointer
522 fn_getter (EContact *contact,
523            EVCardAttribute *attr)
524 {
525         /* this fills FN, if not there yet */
526         if (!attr) {
527                 EContactName *name;
528
529                 name = e_contact_get (contact, E_CONTACT_NAME);
530                 if (name)
531                         e_contact_name_free (name);
532
533                 attr = e_vcard_get_attribute (E_VCARD (contact), EVC_FN);
534         }
535
536         if (attr) {
537                 GList *p = e_vcard_attribute_get_values (attr);
538
539                 return p && p->data ? p->data : (gpointer) "";
540         } else
541                 return NULL;
542 }
543
544 static void
545 fn_setter (EContact *contact,
546            EVCardAttribute *attr,
547            gpointer data)
548 {
549         gchar *name_str = data;
550
551         e_vcard_attribute_add_value (attr, name_str);
552
553         attr = e_vcard_get_attribute (E_VCARD (contact), EVC_N);
554         if (!attr) {
555                 EContactName *name = e_contact_name_from_string ((gchar *) data);
556
557                 attr = e_vcard_attribute_new (NULL, EVC_N);
558                 e_vcard_append_attribute (E_VCARD (contact), attr);
559
560                 /* call the setter directly */
561                 n_setter (contact, attr, name);
562
563                 e_contact_name_free (name);
564         }
565 }
566
567 static gpointer
568 fileas_getter (EContact *contact,
569                EVCardAttribute *attr)
570 {
571         GList *p = NULL;
572
573         if (attr) {
574                 p = e_vcard_attribute_get_values (attr);
575                 if (!p || !p->data || !*((const gchar *) p->data))
576                         p = NULL;
577         }
578
579         if (!p) {
580                 /* Generate a FILE_AS field */
581                 EContactName *name;
582                 gchar *new_file_as = NULL;
583
584                 name = e_contact_get (contact, E_CONTACT_NAME);
585
586                 /* Use name if available */
587                 if (name) {
588                         gchar *strings[3], **stringptr;
589
590                         stringptr = strings;
591                         if (name->family && *name->family)
592                                 *(stringptr++) = name->family;
593                         if (name->given && *name->given)
594                                 *(stringptr++) = name->given;
595                         if (stringptr != strings) {
596                                 *stringptr = NULL;
597                                 new_file_as = g_strjoinv (", ", strings);
598                         }
599
600                         e_contact_name_free (name);
601                 }
602
603                 /* Use org as fallback */
604                 if (!new_file_as) {
605                         const gchar *org = e_contact_get_const (contact, E_CONTACT_ORG);
606
607                         if (org && *org) {
608                                 new_file_as = g_strdup (org);
609                         }
610                 }
611
612                 /* Add the FILE_AS attribute to the vcard */
613                 if (new_file_as) {
614                         attr = e_vcard_attribute_new (NULL, EVC_X_FILE_AS);
615                         e_vcard_add_attribute_with_value (E_VCARD (contact), attr, new_file_as);
616
617                         g_free (new_file_as);
618                 }
619         }
620
621         if (attr) {
622                 p = e_vcard_attribute_get_values (attr);
623
624                 return p && p->data ? p->data : (gpointer) "";
625         } else {
626                 return NULL;
627         }
628 }
629
630 static void
631 fileas_setter (EContact *contact,
632                EVCardAttribute *attr,
633                gpointer data)
634 {
635         /* Default implementation */
636         const gchar *file_as = data;
637         e_vcard_attribute_add_value (attr, file_as ? : "");
638 }
639
640 \f
641
642 static gpointer
643 n_getter (EContact *contact,
644           EVCardAttribute *attr)
645 {
646         EContactName *name = g_new0 (EContactName, 1);
647         EVCardAttribute *new_attr;
648         gchar *name_str;
649
650         if (attr) {
651                 GList *p = e_vcard_attribute_get_values (attr);
652
653                 name->family     = g_strdup (p && p->data ? p->data : ""); if (p) p = p->next;
654                 name->given      = g_strdup (p && p->data ? p->data : ""); if (p) p = p->next;
655                 name->additional = g_strdup (p && p->data ? p->data : ""); if (p) p = p->next;
656                 name->prefixes   = g_strdup (p && p->data ? p->data : ""); if (p) p = p->next;
657                 name->suffixes   = g_strdup (p && p->data ? p->data : "");
658         }
659
660         new_attr = e_vcard_get_attribute (E_VCARD (contact), EVC_FN);
661         if (!new_attr) {
662                 new_attr = e_vcard_attribute_new (NULL, EVC_FN);
663                 e_vcard_append_attribute (E_VCARD (contact), new_attr);
664                 name_str = e_contact_name_to_string (name);
665                 e_vcard_attribute_add_value (new_attr, name_str);
666                 g_free (name_str);
667         }
668
669         return name;
670 }
671
672 static void
673 n_setter (EContact *contact,
674           EVCardAttribute *attr,
675           gpointer data)
676 {
677         EContactName *name = data;
678
679         e_vcard_attribute_add_value (attr, name->family ? name->family : "");
680         e_vcard_attribute_add_value (attr, name->given ? name->given : "");
681         e_vcard_attribute_add_value (attr, name->additional ? name->additional : "");
682         e_vcard_attribute_add_value (attr, name->prefixes ? name->prefixes : "");
683         e_vcard_attribute_add_value (attr, name->suffixes ? name->suffixes : "");
684
685         /* now find the attribute for FileAs.  if it's not present, fill it in */
686         attr = e_vcard_get_attribute (E_VCARD (contact), EVC_X_FILE_AS);
687         if (!attr) {
688                 gchar *strings[3], **stringptr;
689                 gchar *string;
690                 attr = e_vcard_attribute_new (NULL, EVC_X_FILE_AS);
691                 e_vcard_append_attribute (E_VCARD (contact), attr);
692
693                 stringptr = strings;
694                 if (name->family && *name->family)
695                         *(stringptr++) = name->family;
696                 if (name->given && *name->given)
697                         *(stringptr++) = name->given;
698                 *stringptr = NULL;
699                 string = g_strjoinv(", ", strings);
700
701                 e_vcard_attribute_add_value (attr, string);
702                 g_free (string);
703         }
704
705 }
706
707 \f
708
709 static gpointer
710 adr_getter (EContact *contact,
711             EVCardAttribute *attr)
712 {
713         if (attr) {
714                 GList *p = e_vcard_attribute_get_values (attr);
715                 EContactAddress *addr = g_new0 (EContactAddress, 1);
716
717                 addr->address_format = g_strdup ("");
718                 addr->po       = g_strdup (p && p->data ? p->data : ""); if (p) p = p->next;
719                 addr->ext      = g_strdup (p && p->data ? p->data : ""); if (p) p = p->next;
720                 addr->street   = g_strdup (p && p->data ? p->data : ""); if (p) p = p->next;
721                 addr->locality = g_strdup (p && p->data ? p->data : ""); if (p) p = p->next;
722                 addr->region   = g_strdup (p && p->data ? p->data : ""); if (p) p = p->next;
723                 addr->code     = g_strdup (p && p->data ? p->data : ""); if (p) p = p->next;
724                 addr->country  = g_strdup (p && p->data ? p->data : ""); if (p) p = p->next;
725
726                 return addr;
727         }
728
729         return NULL;
730 }
731
732 static void
733 adr_setter (EContact *contact,
734             EVCardAttribute *attr,
735             gpointer data)
736 {
737         EContactAddress *addr = data;
738
739         e_vcard_attribute_add_value (attr, addr->po);
740         e_vcard_attribute_add_value (attr, addr->ext);
741         e_vcard_attribute_add_value (attr, addr->street);
742         e_vcard_attribute_add_value (attr, addr->locality);
743         e_vcard_attribute_add_value (attr, addr->region);
744         e_vcard_attribute_add_value (attr, addr->code);
745         e_vcard_attribute_add_value (attr, addr->country);
746 }
747
748 \f
749
750 static gpointer
751 date_getter (EContact *contact,
752              EVCardAttribute *attr)
753 {
754         if (attr) {
755                 GList *p = e_vcard_attribute_get_values (attr);
756                 EContactDate *date;
757
758                 if (p && p->data && ((gchar *) p->data)[0])
759                         date = e_contact_date_from_string ((gchar *) p->data);
760                 else
761                         date = NULL;
762
763                 return date;
764         }
765
766         return NULL;
767 }
768
769 static void
770 date_setter (EContact *contact,
771              EVCardAttribute *attr,
772              gpointer data)
773 {
774         EContactDate *date = data;
775         gchar *str;
776
777         str = e_contact_date_to_string (date);
778
779         e_vcard_attribute_add_value (attr, str);
780         g_free (str);
781 }
782
783 \f
784
785 static gpointer
786 cert_getter (EContact *contact,
787              EVCardAttribute *attr)
788 {
789         if (attr) {
790                 /* the certificate is stored in this vcard.  just
791                  * return the data */
792                 GList *values = e_vcard_attribute_get_values_decoded (attr);
793
794                 if (values && values->data) {
795                         GString *s = values->data;
796                         EContactCert *cert = g_new0 (EContactCert, 1);
797
798                         cert->length = s->len;
799                         cert->data = g_malloc (cert->length);
800                         memcpy (cert->data, s->str, cert->length);
801
802                         return cert;
803                 }
804         }
805
806         /* XXX if we stored a fingerprint in the cert we could look it
807          * up via NSS, but that would require the additional NSS dep
808          * here, and we'd have more than one process opening the
809          * certdb, which is bad.  *sigh * */
810
811         return NULL;
812 }
813
814 static void
815 cert_setter (EContact *contact,
816              EVCardAttribute *attr,
817              gpointer data)
818 {
819         EContactCert *cert = data;
820
821         e_vcard_attribute_add_param_with_value (attr,
822                                                 e_vcard_attribute_param_new (EVC_ENCODING),
823                                                 "b");
824
825         e_vcard_attribute_add_value_decoded (attr, cert->data, cert->length);
826 }
827
828 \f
829
830 /* Set_arg handler for the contact */
831 static void
832 e_contact_set_property (GObject *object,
833                         guint prop_id,
834                         const GValue *value,
835                         GParamSpec *pspec)
836 {
837         EContact *contact = E_CONTACT (object);
838         const EContactFieldInfo *info = NULL;
839
840         if (prop_id < 1 || prop_id >= E_CONTACT_FIELD_LAST) {
841                 G_OBJECT_WARN_INVALID_PROPERTY_ID (object, prop_id, pspec);
842                 return;
843         }
844
845         info = &field_info[prop_id];
846
847         if (info->t & E_CONTACT_FIELD_TYPE_MULTI) {
848                 GList *new_values = g_value_get_pointer (value);
849                 GList *l;
850
851                 /* first we remove all attributes of the type we're
852                  * adding, then add new ones based on the values that
853                  * are passed in */
854                 e_vcard_remove_attributes (E_VCARD (contact), NULL, info->vcard_field_name);
855
856                 for (l = new_values; l; l = l->next)
857                         e_vcard_append_attribute_with_value (E_VCARD (contact),
858                                                           e_vcard_attribute_new (NULL, info->vcard_field_name),
859                                                           (gchar *) l->data);
860         }
861         else if (info->t & E_CONTACT_FIELD_TYPE_SYNTHETIC) {
862                 if (info->t & E_CONTACT_FIELD_TYPE_MULTI_ELEM) {
863                         /* XXX this is kinda broken - we don't insert
864                          * insert padding elements if, e.g. the user
865                          * sets email 3 when email 1 and 2 don't
866                          * exist.  But, if we *did* pad the lists we'd
867                          * end up with empty items in the vcard.  I
868                          * dunno which is worse. */
869                         EVCardAttribute *attr = NULL;
870                         gboolean found = FALSE;
871                         gint num_left = info->list_elem;
872                         GList *attrs = e_vcard_get_attributes (E_VCARD (contact));
873                         GList *l;
874                         const gchar *sval;
875
876                         for (l = attrs; l; l = l->next) {
877                                 const gchar *name;
878
879                                 attr = l->data;
880                                 name = e_vcard_attribute_get_name (attr);
881
882                                 if (!g_ascii_strcasecmp (name, info->vcard_field_name)) {
883                                         if (num_left-- == 0) {
884                                                 found = TRUE;
885                                                 break;
886                                         }
887                                 }
888                         }
889
890                         sval = g_value_get_string (value);
891                         if (sval && *sval) {
892                                 if (found) {
893                                         /* we found it, overwrite it */
894                                         e_vcard_attribute_remove_values (attr);
895                                 }
896                                 else {
897                                         /* we didn't find it - add a new attribute */
898                                         attr = e_vcard_attribute_new (NULL, info->vcard_field_name);
899                                         if (!g_ascii_strcasecmp (info->vcard_field_name, "EMAIL") &&
900                                             !info->attr_type1 &&
901                                             !info->attr_type2) {
902                                                 /* Add default type */
903                                                 e_vcard_attribute_add_param_with_value ( attr,
904                                                                 e_vcard_attribute_param_new (EVC_TYPE),
905                                                                 "OTHER");
906                                         }
907                                         e_vcard_append_attribute (E_VCARD (contact), attr);
908                                 }
909
910                                 e_vcard_attribute_add_value (attr, sval);
911                         }
912                         else {
913                                 if (found)
914                                         e_vcard_remove_attribute (E_VCARD (contact), attr);
915                         }
916                 }
917                 else if (info->t & E_CONTACT_FIELD_TYPE_ATTR_TYPE) {
918                         /* XXX this is kinda broken - we don't insert
919                          * insert padding elements if, e.g. the user
920                          * sets email 3 when email 1 and 2 don't
921                          * exist.  But, if we *did* pad the lists we'd
922                          * end up with empty items in the vcard.  I
923                          * dunno which is worse. */
924                         EVCardAttribute *attr = NULL;
925                         gboolean found = FALSE;
926                         gint num_left = info->list_elem;
927                         GList *attrs = e_vcard_get_attributes (E_VCARD (contact));
928                         GList *l;
929
930                         for (l = attrs; l && !found; l = l->next) {
931                                 const gchar *name;
932                                 gboolean found_needed1, found_needed2;
933
934                                 found_needed1 = (info->attr_type1 == NULL);
935                                 found_needed2 = (info->attr_type2 == NULL);
936
937                                 attr = l->data;
938                                 name = e_vcard_attribute_get_name (attr);
939
940                                 if (!g_ascii_strcasecmp (name, info->vcard_field_name)) {
941                                         GList *params;
942
943                                         for (params = e_vcard_attribute_get_params (attr); params; params = params->next) {
944                                                 EVCardAttributeParam *param = params->data;
945                                                 const gchar *param_name = e_vcard_attribute_param_get_name (param);
946
947                                                 if (!g_ascii_strcasecmp (param_name, EVC_TYPE)) {
948                                                         gboolean matches = FALSE;
949                                                         GList *values = e_vcard_attribute_param_get_values (param);
950
951                                                         while (values && values->data) {
952                                                                 if (!found_needed1 && !g_ascii_strcasecmp ((gchar *) values->data, info->attr_type1)) {
953                                                                         found_needed1 = TRUE;
954                                                                         matches = TRUE;
955                                                                 }
956                                                                 else if (!found_needed2 && !g_ascii_strcasecmp ((gchar *) values->data, info->attr_type2)) {
957                                                                         found_needed2 = TRUE;
958                                                                         matches = TRUE;
959                                                                 } else if (found_needed1) {
960                                                                         if (!matches || !found_needed2)
961                                                                                 matches = FALSE;
962                                                                         break;
963                                                                 }
964
965                                                                 values = values->next;
966                                                         }
967
968                                                         if (!matches) {
969                                                                 /* this is to enforce that we find an attribute
970                                                                  * with *only* the TYPE='s we need.  This may seem like
971                                                                  * an odd restriction but it's the only way at present to
972                                                                  * implement the Other Fax and Other Phone attributes. */
973                                                                 found_needed1 = FALSE;
974                                                                 break;
975                                                         }
976                                                 }
977
978                                                 if (found_needed1 && found_needed2) {
979                                                         if (num_left-- == 0) {
980                                                                 found = TRUE;
981                                                                 break;
982                                                         }
983                                                 }
984                                         }
985                                 }
986                         }
987
988                         if (found) {
989                                 /* we found it, overwrite it */
990                                 e_vcard_attribute_remove_values (attr);
991                         }
992                         else {
993                                 /* we didn't find it - add a new attribute */
994                                 attr = e_vcard_attribute_new (NULL, info->vcard_field_name);
995                                 e_vcard_append_attribute (E_VCARD (contact), attr);
996                                 if (info->attr_type1)
997                                         e_vcard_attribute_add_param_with_value (attr, e_vcard_attribute_param_new (EVC_TYPE),
998                                                                                 info->attr_type1);
999                                 if (info->attr_type2)
1000                                         e_vcard_attribute_add_param_with_value (attr, e_vcard_attribute_param_new (EVC_TYPE),
1001                                                                                 info->attr_type2);
1002                         }
1003
1004                         if (info->t & E_CONTACT_FIELD_TYPE_STRUCT || info->t & E_CONTACT_FIELD_TYPE_GETSET) {
1005                                 gpointer data = info->t & E_CONTACT_FIELD_TYPE_STRUCT ? g_value_get_boxed (value) : (gchar *) g_value_get_string (value);
1006
1007                                 if ((info->t & E_CONTACT_FIELD_TYPE_STRUCT && data)
1008                                     || (data && *(gchar *) data))
1009                                         info->struct_setter (contact, attr, data);
1010                                 else
1011                                         e_vcard_remove_attribute (E_VCARD (contact), attr);
1012                         }
1013                         else {
1014                                 const gchar *sval = g_value_get_string (value);
1015
1016                                 if (sval && *sval)
1017                                         e_vcard_attribute_add_value (attr, sval);
1018                                 else
1019                                         e_vcard_remove_attribute (E_VCARD (contact), attr);
1020                         }
1021                 }
1022                 else if (info->t & E_CONTACT_FIELD_TYPE_LIST_ELEM) {
1023                         EVCardAttribute *attr = e_vcard_get_attribute (E_VCARD (contact), info->vcard_field_name);
1024                         GList *values;
1025                         GList *p;
1026                         const gchar *sval = g_value_get_string (value);
1027
1028                         if (!attr) {
1029                                 if (!sval || !*sval)
1030                                         return;
1031
1032                                 d(printf ("adding new %s\n", info->vcard_field_name));
1033
1034                                 attr = e_vcard_attribute_new (NULL, info->vcard_field_name);
1035                                 e_vcard_append_attribute (E_VCARD (contact), attr);
1036                         }
1037
1038                         values = e_vcard_attribute_get_values (attr);
1039                         p = g_list_nth (values, info->list_elem);
1040
1041                         if (p) {
1042                                 g_free (p->data);
1043                                 p->data = g_strdup (g_value_get_string (value));
1044                         }
1045                         else {
1046                                 /* there weren't enough elements in the list, pad it */
1047                                 gint count = info->list_elem - g_list_length (values);
1048
1049                                 while (count--)
1050                                         e_vcard_attribute_add_value (attr, "");
1051
1052                                 e_vcard_attribute_add_value (attr, g_value_get_string (value));
1053                         }
1054                 }
1055                 else {
1056                         switch (info->field_id) {
1057                         case E_CONTACT_CATEGORIES: {
1058                                 EVCardAttribute *attr = e_vcard_get_attribute (E_VCARD (contact), EVC_CATEGORIES);
1059                                 gchar **split, **s;
1060                                 const gchar *str;
1061
1062                                 if (attr)
1063                                         e_vcard_attribute_remove_values (attr);
1064                                 else {
1065                                         /* we didn't find it - add a new attribute */
1066                                         attr = e_vcard_attribute_new (NULL, EVC_CATEGORIES);
1067                                         e_vcard_append_attribute (E_VCARD (contact), attr);
1068                                 }
1069
1070                                 str = g_value_get_string (value);
1071                                 if (str && *str) {
1072                                         split = g_strsplit (str, ",", 0);
1073                                         if (split) {
1074                                                 for (s = split; *s; s++) {
1075                                                         e_vcard_attribute_add_value (attr, g_strstrip (*s));
1076                                                 }
1077                                                 g_strfreev (split);
1078                                         } else
1079                                                 e_vcard_attribute_add_value (attr, str);
1080                                 }
1081                                 else {
1082                                         d(printf ("removing %s\n", info->vcard_field_name));
1083
1084                                         e_vcard_remove_attribute (E_VCARD (contact), attr);
1085                                 }
1086                                 break;
1087                         }
1088                         default:
1089                                 g_warning ("unhandled synthetic field 0x%02x", info->field_id);
1090                                 break;
1091                         }
1092                 }
1093         }
1094         else if (info->t & E_CONTACT_FIELD_TYPE_STRUCT || info->t & E_CONTACT_FIELD_TYPE_GETSET) {
1095                 EVCardAttribute *attr = e_vcard_get_attribute (E_VCARD (contact), info->vcard_field_name);
1096                 gpointer data = info->t & E_CONTACT_FIELD_TYPE_STRUCT ? g_value_get_boxed (value) : (gchar *) g_value_get_string (value);
1097
1098                 if (attr) {
1099                         if ((info->t & E_CONTACT_FIELD_TYPE_STRUCT && data)
1100                             || (data && *(gchar *) data)) {
1101                                 d(printf ("overwriting existing %s\n", info->vcard_field_name));
1102                                 /* remove all existing values and parameters.
1103                                  * the setter will add the correct ones */
1104                                 e_vcard_attribute_remove_values (attr);
1105                                 e_vcard_attribute_remove_params (attr);
1106
1107                                 info->struct_setter (contact, attr, data);
1108                         }
1109                         else {
1110                                 d(printf ("removing %s\n", info->vcard_field_name));
1111
1112                                 e_vcard_remove_attribute (E_VCARD (contact), attr);
1113                         }
1114                 }
1115                 else if ((info->t & E_CONTACT_FIELD_TYPE_STRUCT && data)
1116                          || (data && *(gchar *) data)) {
1117                         d(printf ("adding new %s\n", info->vcard_field_name));
1118                         attr = e_vcard_attribute_new (NULL, info->vcard_field_name);
1119
1120                         e_vcard_append_attribute (E_VCARD (contact), attr);
1121
1122                         info->struct_setter (contact, attr, data);
1123                 }
1124         }
1125         else if (info->t & E_CONTACT_FIELD_TYPE_BOOLEAN) {
1126                 EVCardAttribute *attr;
1127
1128                 /* first we search for an attribute we can overwrite */
1129                 attr = e_vcard_get_attribute (E_VCARD (contact), info->vcard_field_name);
1130                 if (attr) {
1131                         d(printf ("setting %s to `%s'\n", info->vcard_field_name, g_value_get_string (value)));
1132                         e_vcard_attribute_remove_values (attr);
1133                         e_vcard_attribute_add_value (attr, g_value_get_boolean (value) ? "TRUE" : "FALSE");
1134                 }
1135                 else {
1136                         /* and if we don't find one we create a new attribute */
1137                         e_vcard_append_attribute_with_value (E_VCARD (contact),
1138                                                           e_vcard_attribute_new (NULL, info->vcard_field_name),
1139                                                           g_value_get_boolean (value) ? "TRUE" : "FALSE");
1140                 }
1141         }
1142         else if (info->t & E_CONTACT_FIELD_TYPE_STRING) {
1143                 EVCardAttribute *attr;
1144                 const gchar *sval = g_value_get_string (value);
1145
1146                 /* first we search for an attribute we can overwrite */
1147                 if (sval == NULL || g_ascii_strcasecmp (info->vcard_field_name, EVC_UID) != 0) {
1148                         attr = e_vcard_get_attribute (E_VCARD (contact), info->vcard_field_name);
1149                 } else {
1150                         /* Avoid useless vcard parsing when trying to set a new non-empty UID.
1151                          * Parsing the vcard is pointless in this particular case because even
1152                          * if there is a UID in the unparsed vcard, it is going to be ignored
1153                          * upon parsing if we already have a UID for the vcard */
1154                         attr = e_vcard_get_attribute_if_parsed (E_VCARD (contact), EVC_UID);
1155                 }
1156
1157                 if (attr) {
1158                         d(printf ("setting %s to `%s'\n", info->vcard_field_name, sval));
1159                         e_vcard_attribute_remove_values (attr);
1160                         if (sval) {
1161                                 e_vcard_attribute_add_value (attr, sval);
1162                         }
1163                         else {
1164                                 d(printf ("removing %s\n", info->vcard_field_name));
1165
1166                                 e_vcard_remove_attribute (E_VCARD (contact), attr);
1167                         }
1168
1169                 }
1170                 else if (sval) {
1171                         /* and if we don't find one we create a new attribute */
1172                         e_vcard_append_attribute_with_value (E_VCARD (contact),
1173                                                           e_vcard_attribute_new (NULL, info->vcard_field_name),
1174                                                           sval);
1175                 }
1176         }
1177         else if (info->t & E_CONTACT_FIELD_TYPE_LIST) {
1178                 EVCardAttribute *attr;
1179                 GList *values, *l;
1180
1181                 values = g_value_get_pointer (value);
1182
1183                 attr = e_vcard_get_attribute (E_VCARD (contact), info->vcard_field_name);
1184
1185                 if (attr) {
1186                         e_vcard_attribute_remove_values (attr);
1187
1188                         if (!values)
1189                                 e_vcard_remove_attribute (E_VCARD (contact), attr);
1190                 }
1191                 else if (values) {
1192                         attr = e_vcard_attribute_new (NULL, info->vcard_field_name);
1193                         e_vcard_append_attribute (E_VCARD (contact), attr);
1194                 }
1195
1196                 for (l = values; l != NULL; l = l->next)
1197                         e_vcard_attribute_add_value (attr, l->data);
1198         }
1199         else {
1200                 g_warning ("unhandled attribute `%s'", info->vcard_field_name);
1201         }
1202 }
1203
1204 static EVCardAttribute *
1205 e_contact_find_attribute_with_types (EContact *contact,
1206                                      const gchar *attr_name,
1207                                      const gchar *type_needed1,
1208                                      const gchar *type_needed2,
1209                                      gint nth)
1210 {
1211         GList *l, *attrs;
1212         gboolean found_needed1, found_needed2;
1213
1214         attrs = e_vcard_get_attributes (E_VCARD (contact));
1215
1216         for (l = attrs; l; l = l->next) {
1217                 EVCardAttribute *attr = l->data;
1218                 const gchar *name;
1219
1220                 found_needed1 = (type_needed1 == NULL);
1221                 found_needed2 = (type_needed2 == NULL);
1222
1223                 name = e_vcard_attribute_get_name (attr);
1224
1225                 if (!g_ascii_strcasecmp (name, attr_name)) {
1226                         GList *params;
1227
1228                         for (params = e_vcard_attribute_get_params (attr); params; params = params->next) {
1229                                 EVCardAttributeParam *param = params->data;
1230                                 const gchar *param_name = e_vcard_attribute_param_get_name (param);
1231
1232                                 if (!g_ascii_strcasecmp (param_name, EVC_TYPE)) {
1233                                         gboolean matches = FALSE;
1234                                         GList *values = e_vcard_attribute_param_get_values (param);
1235
1236                                         while (values && values->data) {
1237                                                 if (!found_needed1 && !g_ascii_strcasecmp ((gchar *) values->data, type_needed1)) {
1238                                                         found_needed1 = TRUE;
1239                                                         matches = TRUE;
1240                                                 }
1241                                                 else if (!found_needed2 && !g_ascii_strcasecmp ((gchar *) values->data, type_needed2)) {
1242                                                         found_needed2 = TRUE;
1243                                                         matches = TRUE;
1244                                                 } else if (found_needed1) {
1245                                                         if (!matches || !found_needed2)
1246                                                                 matches = FALSE;
1247                                                         break;
1248                                                 }
1249                                                 values = values->next;
1250                                         }
1251
1252                                         if (!matches) {
1253                                                 /* this is to enforce that we find an attribute
1254                                                  * with *only* the TYPE='s we need.  This may seem like
1255                                                  * an odd restriction but it's the only way at present to
1256                                                  * implement the Other Fax and Other Phone attributes. */
1257                                                 found_needed1 = FALSE;
1258                                                 break;
1259                                         }
1260                                 }
1261
1262                                 if (found_needed1 && found_needed2) {
1263                                         if (nth-- == 0)
1264                                                 return attr;
1265                                         else
1266                                                 break;
1267                                 }
1268                         }
1269                 }
1270         }
1271
1272         return NULL;
1273 }
1274
1275 static void
1276 e_contact_get_property (GObject *object,
1277                         guint prop_id,
1278                         GValue *value,
1279                         GParamSpec *pspec)
1280 {
1281         const EContactFieldInfo *info = NULL;
1282         gpointer data;
1283
1284         if (prop_id < 1 || prop_id >= E_CONTACT_FIELD_LAST) {
1285                 G_OBJECT_WARN_INVALID_PROPERTY_ID (object, prop_id, pspec);
1286                 g_value_reset (value);
1287                 return;
1288         }
1289
1290         info = &field_info[prop_id];
1291         data = e_contact_get (E_CONTACT (object), prop_id);
1292
1293         if (info->t & E_CONTACT_FIELD_TYPE_BOOLEAN) {
1294                 g_value_set_boolean (value, data != NULL);
1295         } else if (info->t & E_CONTACT_FIELD_TYPE_LIST) {
1296                 g_value_set_pointer (value, data);
1297         } else if (info->t & E_CONTACT_FIELD_TYPE_STRUCT) {
1298                 g_value_take_boxed (value, data);
1299         } else if (info->t & E_CONTACT_FIELD_TYPE_GETSET) {
1300                 if (info->t & E_CONTACT_FIELD_TYPE_STRUCT) {
1301                         g_value_set_boxed (value, data);
1302                 } else {
1303                         g_value_set_string (value, data);
1304                 }
1305         } else if (info->t & E_CONTACT_FIELD_TYPE_STRING) {
1306                 g_value_set_string (value, data);
1307         } else {
1308                 g_value_set_pointer (value, data);
1309         }
1310 }
1311
1312 \f
1313
1314 /**
1315  * e_contact_new:
1316  *
1317  * Creates a new, blank #EContact.
1318  *
1319  * Returns: A new #EContact.
1320  **/
1321 EContact *
1322 e_contact_new (void)
1323 {
1324         return e_contact_new_from_vcard ("");
1325 }
1326
1327 /**
1328  * e_contact_new_from_vcard:
1329  * @vcard: a string representing a vcard
1330  *
1331  * Creates a new #EContact based on a vcard.
1332  *
1333  * Returns: A new #EContact.
1334  **/
1335 EContact *
1336 e_contact_new_from_vcard (const gchar *vcard)
1337 {
1338         EContact *contact;
1339         g_return_val_if_fail (vcard != NULL, NULL);
1340
1341         contact = g_object_new (E_TYPE_CONTACT, NULL);
1342         e_vcard_construct (E_VCARD (contact), vcard);
1343
1344         return contact;
1345 }
1346
1347 /**
1348  * e_contact_new_from_vcard_with_uid:
1349  * @vcard: a string representing a vcard
1350  * @uid: a contact UID
1351  *
1352  * Creates a new #EContact based on a vcard and a predefined UID.
1353  *
1354  * Returns: A new #EContact.
1355  *
1356  * Since: 3.4
1357  **/
1358 EContact *
1359 e_contact_new_from_vcard_with_uid (const gchar *vcard,
1360                                    const gchar *uid)
1361 {
1362         EContact *contact;
1363         g_return_val_if_fail (vcard != NULL, NULL);
1364
1365         contact = g_object_new (E_TYPE_CONTACT, NULL);
1366         e_vcard_construct_with_uid (E_VCARD (contact), vcard, uid);
1367
1368         return contact;
1369 }
1370
1371 /**
1372  * e_contact_duplicate:
1373  * @contact: an #EContact
1374  *
1375  * Creates a copy of @contact.
1376  *
1377  * Returns: (transfer full): A new #EContact identical to @contact.
1378  **/
1379 EContact *
1380 e_contact_duplicate (EContact *contact)
1381 {
1382         gchar *vcard;
1383         EContact *c;
1384
1385         g_return_val_if_fail (E_IS_CONTACT (contact), NULL);
1386
1387         vcard = e_vcard_to_string (E_VCARD (contact), EVC_FORMAT_VCARD_30);
1388         c = e_contact_new_from_vcard (vcard);
1389         g_free (vcard);
1390
1391         return c;
1392 }
1393
1394 /**
1395  * e_contact_field_name:
1396  * @field_id: an #EContactField
1397  *
1398  * Gets the string representation of @field_id.
1399  *
1400  * Returns: The string representation of @field_id, or %NULL if it doesn't exist.
1401  **/
1402 const gchar *
1403 e_contact_field_name (EContactField field_id)
1404 {
1405         g_return_val_if_fail (field_id >= 1 && field_id < E_CONTACT_FIELD_LAST, "");
1406
1407         return field_info[field_id].field_name;
1408 }
1409
1410 /**
1411  * e_contact_pretty_name:
1412  * @field_id: an #EContactField
1413  *
1414  * Gets a human-readable, translated string representation
1415  * of @field_id.
1416  *
1417  * Returns: The human-readable representation of @field_id, or %NULL if it doesn't exist.
1418  **/
1419 const gchar *
1420 e_contact_pretty_name (EContactField field_id)
1421 {
1422         g_return_val_if_fail (field_id >= 1 && field_id < E_CONTACT_FIELD_LAST, "");
1423
1424 #ifdef ENABLE_NLS
1425         bindtextdomain (GETTEXT_PACKAGE, LOCALEDIR);
1426         bind_textdomain_codeset (GETTEXT_PACKAGE, "UTF-8");
1427 #endif
1428
1429         return _(field_info[field_id].pretty_name);
1430 }
1431
1432 /**
1433  * e_contact_vcard_attribute:
1434  * @field_id: an #EContactField
1435  *
1436  * Gets the vcard attribute corresponding to @field_id, as a string.
1437  *
1438  * Returns: The vcard attribute corresponding to @field_id, or %NULL if it doesn't exist.
1439  **/
1440 const gchar *
1441 e_contact_vcard_attribute (EContactField field_id)
1442 {
1443         g_return_val_if_fail (field_id >= 1 && field_id < E_CONTACT_FIELD_LAST, "");
1444
1445         return field_info[field_id].vcard_field_name;
1446 }
1447
1448 /**
1449  * e_contact_field_id:
1450  * @field_name: a string representing a contact field
1451  *
1452  * Gets the #EContactField corresponding to the @field_name.
1453  *
1454  * Returns: An #EContactField corresponding to @field_name, or %0 if it doesn't exist.
1455  **/
1456 EContactField
1457 e_contact_field_id (const gchar *field_name)
1458 {
1459         gint i;
1460         for (i = E_CONTACT_FIELD_FIRST; i < E_CONTACT_FIELD_LAST; i++) {
1461                 if (!g_ascii_strcasecmp (field_info[i].field_name, field_name))
1462                         return field_info[i].field_id;
1463         }
1464
1465         return 0;
1466 }
1467
1468 /**
1469  * e_contact_field_id_from_vcard:
1470  * @vcard_field: a string representing a vCard field
1471  *
1472  * Gets the #EContactField corresponding to the @vcard_field.
1473  *
1474  * Returns: An #EContactField corresponding to @vcard_field, or %0 if it doesn't exist.
1475  *
1476  * Since: 2.26
1477  **/
1478 EContactField
1479 e_contact_field_id_from_vcard (const gchar *vcard_field)
1480 {
1481         gint i;
1482
1483         for (i = E_CONTACT_FIELD_FIRST; i < E_CONTACT_FIELD_LAST; i++) {
1484                 if (field_info[i].vcard_field_name == NULL)
1485                         continue;
1486                 if (field_info[i].t & E_CONTACT_FIELD_TYPE_SYNTHETIC)
1487                         continue;
1488                 if (!strcmp (field_info[i].vcard_field_name, vcard_field))
1489                         return field_info[i].field_id;
1490         }
1491
1492         g_warning ("unknown vCard field `%s'", vcard_field);
1493         return 0;
1494 }
1495
1496 /**
1497  * e_contact_get:
1498  * @contact: an #EContact
1499  * @field_id: an #EContactField
1500  *
1501  * Gets the value of @contact's field specified by @field_id.
1502  *
1503  * Returns: (transfer full) (allow-none): Depends on the field's type, owned by the caller. This may be %NULL if the field isn't set.
1504  **/
1505 gpointer
1506 e_contact_get (EContact *contact,
1507                EContactField field_id)
1508 {
1509         const EContactFieldInfo *info = NULL;
1510
1511         g_return_val_if_fail (contact && E_IS_CONTACT (contact), NULL);
1512         g_return_val_if_fail (field_id >= 1 && field_id < E_CONTACT_FIELD_LAST, NULL);
1513
1514         info = &field_info[field_id];
1515
1516         if (info->t & E_CONTACT_FIELD_TYPE_BOOLEAN) {
1517                 EVCardAttribute *attr = e_vcard_get_attribute (E_VCARD (contact), info->vcard_field_name);
1518                 gboolean rv = FALSE;
1519
1520                 if (attr) {
1521                         GList *v = e_vcard_attribute_get_values (attr);
1522                         rv = v && v->data && !g_ascii_strcasecmp ((gchar *)v->data, "true");
1523                         return rv ? (gpointer) "1" : NULL;
1524                 }
1525         }
1526         else if (info->t & E_CONTACT_FIELD_TYPE_LIST) {
1527                 EVCardAttribute *attr = e_vcard_get_attribute (E_VCARD (contact), info->vcard_field_name);
1528
1529                 if (attr) {
1530                         GList *list = g_list_copy (e_vcard_attribute_get_values (attr));
1531                         GList *l;
1532                         for (l = list; l; l = l->next)
1533                                 l->data = l->data ? g_strstrip (g_strdup (l->data)) : NULL;
1534                         return list;
1535                 }
1536         }
1537         else if (info->t & E_CONTACT_FIELD_TYPE_LIST_ELEM) {
1538                 if (info->t & E_CONTACT_FIELD_TYPE_STRING) {
1539                         EVCardAttribute *attr = e_vcard_get_attribute (E_VCARD (contact), info->vcard_field_name);
1540
1541                         if (attr) {
1542                                 GList *v;
1543
1544                                 v = e_vcard_attribute_get_values (attr);
1545                                 v = g_list_nth (v, info->list_elem);
1546
1547                                 return (v && v->data) ? g_strstrip (g_strdup (v->data)) : NULL;
1548                         }
1549                 }
1550         }
1551         else if (info->t & E_CONTACT_FIELD_TYPE_MULTI_ELEM) {
1552                 if (info->t & E_CONTACT_FIELD_TYPE_STRING) {
1553                         GList *attrs, *l;
1554                         gint num_left = info->list_elem;
1555
1556                         attrs = e_vcard_get_attributes (E_VCARD (contact));
1557
1558                         for (l = attrs; l; l = l->next) {
1559                                 EVCardAttribute *attr = l->data;
1560                                 const gchar *name;
1561
1562                                 name = e_vcard_attribute_get_name (attr);
1563
1564                                 if (!g_ascii_strcasecmp (name, info->vcard_field_name)) {
1565                                         if (num_left-- == 0) {
1566                                                 GList *v = e_vcard_attribute_get_values (attr);
1567
1568                                                 return (v && v->data) ? g_strstrip (g_strdup (v->data)) : NULL;
1569                                         }
1570                                 }
1571                         }
1572                 }
1573         }
1574         else if (info->t & E_CONTACT_FIELD_TYPE_ATTR_TYPE) {
1575                 EVCardAttribute *attr = e_contact_find_attribute_with_types (contact, info->vcard_field_name, info->attr_type1, info->attr_type2, info->list_elem);
1576
1577                 if (info->t & E_CONTACT_FIELD_TYPE_STRING) {
1578                         if (attr) {
1579                                 GList *p = e_vcard_attribute_get_values (attr);
1580                                 return (p && p->data) ? g_strstrip (g_strdup (p->data)) : NULL;
1581                         }
1582                         else {
1583                                 return NULL;
1584                         }
1585                 }
1586                 else { /* struct */
1587                         return info->struct_getter (contact, attr);
1588                 }
1589
1590         }
1591         else if (info->t & E_CONTACT_FIELD_TYPE_STRUCT) {
1592                 EVCardAttribute *attr = e_vcard_get_attribute (E_VCARD (contact), info->vcard_field_name);
1593                 if (attr)
1594                         return info->struct_getter (contact, attr);
1595         }
1596         else if (info->t & E_CONTACT_FIELD_TYPE_GETSET) {
1597                 EVCardAttribute *attr = e_vcard_get_attribute (E_VCARD (contact), info->vcard_field_name);
1598                 gpointer rv = NULL;
1599
1600                 rv = info->struct_getter (contact, attr);
1601
1602                 if (info->t & E_CONTACT_FIELD_TYPE_STRUCT)
1603                         return (gpointer) info->boxed_type_getter ();
1604                 else if (!rv)
1605                         return NULL;
1606                 else
1607                         return g_strstrip (g_strdup (rv));
1608         }
1609         else if (info->t & E_CONTACT_FIELD_TYPE_SYNTHETIC) {
1610                 switch (info->field_id) {
1611                 case E_CONTACT_NAME_OR_ORG: {
1612                         const gchar *str;
1613
1614                         str = e_contact_get_const (contact, E_CONTACT_FILE_AS);
1615                         if (!str)
1616                                 str = e_contact_get_const (contact, E_CONTACT_FULL_NAME);
1617                         if (!str)
1618                                 str = e_contact_get_const (contact, E_CONTACT_ORG);
1619                         if (!str) {
1620                                 gboolean is_list = GPOINTER_TO_INT (e_contact_get (contact, E_CONTACT_IS_LIST));
1621
1622                                 if (is_list)
1623                                         str = _("Unnamed List");
1624                                 else
1625                                         str = e_contact_get_const (contact, E_CONTACT_EMAIL_1);
1626                         }
1627
1628                         return str ? g_strstrip (g_strdup (str)) : NULL;
1629                 }
1630                 case E_CONTACT_CATEGORIES: {
1631                         EVCardAttribute *attr = e_vcard_get_attribute (E_VCARD (contact), EVC_CATEGORIES);
1632                         gchar *rv = NULL;
1633
1634                         if (attr) {
1635                                 GString *str = g_string_new ("");
1636                                 GList *v = e_vcard_attribute_get_values (attr);
1637                                 while (v) {
1638                                         g_string_append (str, (gchar *) v->data);
1639                                         v = v->next;
1640                                         if (v)
1641                                                 g_string_append_c (str, ',');
1642                                 }
1643
1644                                 rv = g_string_free (str, FALSE);
1645                         }
1646                         return rv;
1647                 }
1648                 default:
1649                         g_warning ("unhandled synthetic field 0x%02x", info->field_id);
1650                         break;
1651                 }
1652         }
1653         else if (info->t & E_CONTACT_FIELD_TYPE_STRING) {
1654                 EVCardAttribute *attr;
1655                 const gchar *cv = NULL;
1656                 GList *v = NULL;
1657
1658                 /* Do our best to avoid vcard parsing by not calling
1659                  * e_vcard_get_attributes */
1660
1661                 cv = contact->priv->cached_strings[field_id];
1662                 if (cv)
1663                         return g_strdup (cv);
1664
1665                 attr = e_vcard_get_attribute (E_VCARD (contact), info->vcard_field_name);
1666
1667                 if (attr)
1668                         v = e_vcard_attribute_get_values (attr);
1669
1670                 return ((v && v->data) ? g_strstrip (g_strdup (v->data)) : NULL);
1671         }
1672         else {
1673                 GList *attrs, *l;
1674                 GList *rv = NULL; /* used for multi attribute lists */
1675
1676                 attrs = e_vcard_get_attributes (E_VCARD (contact));
1677
1678                 for (l = attrs; l; l = l->next) {
1679                         EVCardAttribute *attr = l->data;
1680                         const gchar *name;
1681
1682                         name = e_vcard_attribute_get_name (attr);
1683
1684                         if (!g_ascii_strcasecmp (name, info->vcard_field_name)) {
1685                                 GList *v;
1686                                 v = e_vcard_attribute_get_values (attr);
1687
1688                                 rv = g_list_append (rv, (v && v->data) ? g_strstrip (g_strdup (v->data)) : NULL);
1689                         }
1690                 }
1691                 return rv;
1692         }
1693         return NULL;
1694 }
1695
1696 /**
1697  * e_contact_get_const:
1698  * @contact: an #EContact
1699  * @field_id: an #EContactField
1700  *
1701  * Gets the value of @contact's field specified by @field_id, caching
1702  * the result so it can be freed later.
1703  *
1704  * Returns: (transfer none): Depends on the field's type, owned by the
1705  * #EContact.
1706  **/
1707 gconstpointer
1708 e_contact_get_const (EContact *contact,
1709                      EContactField field_id)
1710 {
1711         gpointer value = NULL;
1712
1713         g_return_val_if_fail (E_IS_CONTACT (contact), NULL);
1714         g_return_val_if_fail (field_id >= 1 && field_id < E_CONTACT_FIELD_LAST, NULL);
1715         g_return_val_if_fail (field_info[field_id].t & E_CONTACT_FIELD_TYPE_STRING, NULL);
1716
1717         value = contact->priv->cached_strings[field_id];
1718
1719         if (!value) {
1720                 value = e_contact_get (contact, field_id);
1721                 if (value)
1722                         contact->priv->cached_strings[field_id] = value;
1723         }
1724
1725         return value;
1726 }
1727
1728 /**
1729  * e_contact_set;
1730  * @contact: an #EContact
1731  * @field_id: an #EContactField
1732  * @value: a value whose type depends on the @field_id
1733  *
1734  * Sets the value of @contact's field specified by @field_id to @value.
1735  **/
1736 void
1737 e_contact_set (EContact *contact,
1738                EContactField field_id,
1739                gconstpointer value)
1740 {
1741         d(printf ("e_contact_set (%p, %d, %p)\n", contact, field_id, value));
1742
1743         g_return_if_fail (contact && E_IS_CONTACT (contact));
1744         g_return_if_fail (field_id >= 1 && field_id < E_CONTACT_FIELD_LAST);
1745
1746         /* set the cached slot to NULL so we'll re-get the new string
1747          * if e_contact_get_const is called again */
1748         g_free (contact->priv->cached_strings[field_id]);
1749         contact->priv->cached_strings[field_id] = NULL;
1750
1751         g_object_set (contact,
1752                       e_contact_field_name (field_id), value,
1753                       NULL);
1754 }
1755
1756 /**
1757  * e_contact_get_attributes:
1758  * @contact: an #EContact
1759  * @field_id: an #EContactField
1760  *
1761  * Gets a list of the vcard attributes for @contact's @field_id.
1762  *
1763  * Returns: (transfer full) (element-type EVCardAttribute): A #GList of pointers
1764  * to #EVCardAttribute, owned by the caller.
1765  **/
1766 GList *
1767 e_contact_get_attributes (EContact *contact,
1768                           EContactField field_id)
1769 {
1770         GList *l = NULL;
1771         GList *attrs, *a;
1772         const EContactFieldInfo *info = NULL;
1773
1774         g_return_val_if_fail (contact && E_IS_CONTACT (contact), NULL);
1775         g_return_val_if_fail (field_id >= 1 && field_id < E_CONTACT_FIELD_LAST, NULL);
1776
1777         info = &field_info[field_id];
1778
1779         attrs = e_vcard_get_attributes (E_VCARD (contact));
1780
1781         for (a = attrs; a; a = a->next) {
1782                 EVCardAttribute *attr = a->data;
1783                 const gchar *name;
1784
1785                 name = e_vcard_attribute_get_name (attr);
1786
1787                 if (!g_ascii_strcasecmp (name, info->vcard_field_name)) {
1788                         l = g_list_prepend (l, e_vcard_attribute_copy (attr));
1789                 }
1790         }
1791
1792         return g_list_reverse (l);
1793 }
1794
1795 /**
1796  * e_contact_set_attributes:
1797  * @contact: an #EContact
1798  * @field_id: an #EContactField
1799  * @attributes: (element-type EVCardAttribute): a #GList of pointers to #EVCardAttribute
1800  *
1801  * Sets the vcard attributes for @contact's @field_id.
1802  * Attributes are added to the contact in the same order as they are in @attributes.
1803  **/
1804 void
1805 e_contact_set_attributes (EContact *contact,
1806                           EContactField field_id,
1807                           GList *attributes)
1808 {
1809         const EContactFieldInfo *info = NULL;
1810         GList *l;
1811
1812         g_return_if_fail (contact && E_IS_CONTACT (contact));
1813         g_return_if_fail (field_id >= 1 && field_id < E_CONTACT_FIELD_LAST);
1814
1815         info = &field_info[field_id];
1816
1817         e_vcard_remove_attributes (E_VCARD (contact), NULL, info->vcard_field_name);
1818
1819         for (l = attributes; l; l = l->next)
1820                 e_vcard_append_attribute (E_VCARD (contact),
1821                                        e_vcard_attribute_copy ((EVCardAttribute *) l->data));
1822 }
1823
1824 /**
1825  * e_contact_name_new:
1826  *
1827  * Creates a new #EContactName struct.
1828  *
1829  * Returns: A new #EContactName struct.
1830  **/
1831 EContactName *
1832 e_contact_name_new (void)
1833 {
1834         return g_new0 (EContactName, 1);
1835 }
1836
1837 /**
1838  * e_contact_name_to_string:
1839  * @name: an #EContactName
1840  *
1841  * Generates a string representation of @name.
1842  *
1843  * Returns: The string representation of @name.
1844  **/
1845 gchar *
1846 e_contact_name_to_string (const EContactName *name)
1847 {
1848         gchar *strings[6], **stringptr = strings;
1849
1850         g_return_val_if_fail (name != NULL, NULL);
1851
1852         if (name->prefixes && *name->prefixes)
1853                 *(stringptr++) = name->prefixes;
1854         if (name->given && *name->given)
1855                 *(stringptr++) = name->given;
1856         if (name->additional && *name->additional)
1857                 *(stringptr++) = name->additional;
1858         if (name->family && *name->family)
1859                 *(stringptr++) = name->family;
1860         if (name->suffixes && *name->suffixes)
1861                 *(stringptr++) = name->suffixes;
1862         *stringptr = NULL;
1863         return g_strjoinv(" ", strings);
1864 }
1865
1866 /**
1867  * e_contact_name_from_string:
1868  * @name_str: a string representing a contact's full name
1869  *
1870  * Creates a new #EContactName based on the parsed @name_str.
1871  *
1872  * Returns: A new #EContactName struct.
1873  **/
1874 EContactName *
1875 e_contact_name_from_string (const gchar *name_str)
1876 {
1877         EContactName *name;
1878         ENameWestern *western;
1879
1880         g_return_val_if_fail (name_str != NULL, NULL);
1881
1882         name = e_contact_name_new ();
1883         western = e_name_western_parse (name_str);
1884
1885         name->prefixes   = g_strdup (western->prefix);
1886         name->given      = g_strdup (western->first );
1887         name->additional = g_strdup (western->middle);
1888         name->family     = g_strdup (western->last  );
1889         name->suffixes   = g_strdup (western->suffix);
1890
1891         e_name_western_free (western);
1892
1893         return name;
1894 }
1895
1896 /**
1897  * e_contact_name_copy:
1898  * @n: an #EContactName
1899  *
1900  * Creates a copy of @n.
1901  *
1902  * Returns: A new #EContactName identical to @n.
1903  **/
1904 EContactName *
1905 e_contact_name_copy (EContactName *n)
1906 {
1907         EContactName *name;
1908
1909         g_return_val_if_fail (n != NULL, NULL);
1910
1911         name = e_contact_name_new ();
1912
1913         name->prefixes   = g_strdup (n->prefixes);
1914         name->given      = g_strdup (n->given);
1915         name->additional = g_strdup (n->additional);
1916         name->family     = g_strdup (n->family);
1917         name->suffixes   = g_strdup (n->suffixes);
1918
1919         return name;
1920 }
1921
1922 /**
1923  * e_contact_name_free:
1924  * @name: an #EContactName
1925  *
1926  * Frees @name and its contents.
1927  **/
1928 void
1929 e_contact_name_free (EContactName *name)
1930 {
1931         if (!name)
1932                 return;
1933
1934         g_free (name->family);
1935         g_free (name->given);
1936         g_free (name->additional);
1937         g_free (name->prefixes);
1938         g_free (name->suffixes);
1939
1940         g_free (name);
1941 }
1942
1943 #define E_CONTACT_DEFINE_BOXED_TYPE(_tp,_nm)                            \
1944         GType                                                           \
1945         _tp ## _get_type (void)                                         \
1946         {                                                               \
1947                 static volatile gsize type_id__volatile = 0;            \
1948                                                                         \
1949                 if (g_once_init_enter (&type_id__volatile)) {           \
1950                         GType type_id;                                  \
1951                                                                         \
1952                         type_id = g_boxed_type_register_static (_nm,    \
1953                                 (GBoxedCopyFunc) _tp ## _copy,          \
1954                                 (GBoxedFreeFunc) _tp ## _free);         \
1955                                                                         \
1956                         g_once_init_leave (&type_id__volatile, type_id);\
1957         }                                                               \
1958                                                                         \
1959         return type_id__volatile;                                       \
1960 }
1961
1962 E_CONTACT_DEFINE_BOXED_TYPE (e_contact_name, "EContactName")
1963
1964 /**
1965  * e_contact_date_from_string:
1966  * @str: a date string in the format YYYY-MM-DD or YYYYMMDD
1967  *
1968  * Creates a new #EContactDate based on @str.
1969  *
1970  * Returns: A new #EContactDate struct.
1971  **/
1972 EContactDate *
1973 e_contact_date_from_string (const gchar *str)
1974 {
1975         EContactDate * date;
1976         gint length;
1977         gchar *t;
1978
1979         g_return_val_if_fail (str != NULL, NULL);
1980
1981         date = e_contact_date_new ();
1982         /* ignore time part */
1983         if ((t = strchr (str, 'T')) != NULL)
1984                 length = t - str;
1985         else
1986                 length = strlen (str);
1987
1988         if (length == 10 ) {
1989                 date->year = str[0] * 1000 + str[1] * 100 + str[2] * 10 + str[3] - '0' * 1111;
1990                 date->month = str[5] * 10 + str[6] - '0' * 11;
1991                 date->day = str[8] * 10 + str[9] - '0' * 11;
1992         } else if (length == 8) {
1993                 date->year = str[0] * 1000 + str[1] * 100 + str[2] * 10 + str[3] - '0' * 1111;
1994                 date->month = str[4] * 10 + str[5] - '0' * 11;
1995                 date->day = str[6] * 10 + str[7] - '0' * 11;
1996         }
1997
1998         return date;
1999 }
2000
2001 /**
2002  * e_contact_date_to_string:
2003  * @dt: an #EContactDate
2004  *
2005  * Generates a date string in the format YYYY-MM-DD based
2006  * on the values of @dt.
2007  *
2008  * Returns: A date string, owned by the caller.
2009  **/
2010 gchar *
2011 e_contact_date_to_string (EContactDate *dt)
2012 {
2013         if (dt)
2014                 return g_strdup_printf ("%04d-%02d-%02d",
2015                                         CLAMP (dt->year, 1000, 9999),
2016                                         CLAMP (dt->month, 1, 12),
2017                                         CLAMP (dt->day, 1, 31));
2018         else
2019                 return NULL;
2020 }
2021
2022 /**
2023  * e_contact_date_equal:
2024  * @dt1: an #EContactDate
2025  * @dt2: an #EContactDate
2026  *
2027  * Checks if @dt1 and @dt2 are the same date.
2028  *
2029  * Returns: %TRUE if @dt1 and @dt2 are equal, %FALSE otherwise.
2030  **/
2031 gboolean
2032 e_contact_date_equal (EContactDate *dt1,
2033                       EContactDate *dt2)
2034 {
2035         if (dt1 && dt2) {
2036                 return (dt1->year == dt2->year &&
2037                         dt1->month == dt2->month &&
2038                         dt1->day == dt2->day);
2039         } else
2040                 return (!!dt1 == !!dt2);
2041 }
2042
2043 /**
2044  * e_contact_date_copy:
2045  * @dt: an #EContactDate
2046  *
2047  * Creates a copy of @dt.
2048  *
2049  * Returns: A new #EContactDate struct identical to @dt.
2050  **/
2051 static EContactDate *
2052 e_contact_date_copy (EContactDate *dt)
2053 {
2054         EContactDate *dt2 = e_contact_date_new ();
2055         dt2->year = dt->year;
2056         dt2->month = dt->month;
2057         dt2->day = dt->day;
2058
2059         return dt2;
2060 }
2061
2062 /**
2063  * e_contact_date_free:
2064  * @date: an #EContactDate
2065  *
2066  * Frees the @date struct and its contents.
2067  */
2068 void
2069 e_contact_date_free (EContactDate *date)
2070 {
2071         g_free (date);
2072 }
2073
2074 E_CONTACT_DEFINE_BOXED_TYPE (e_contact_date, "EContactDate")
2075
2076 /**
2077  * e_contact_date_new:
2078  *
2079  * Creates a new #EContactDate struct.
2080  *
2081  * Returns: A new #EContactDate struct.
2082  **/
2083 EContactDate *
2084 e_contact_date_new (void)
2085 {
2086         return g_new0 (EContactDate, 1);
2087 }
2088
2089 /**
2090  * e_contact_photo_new:
2091  *
2092  * Creates a new #EContactPhoto struct.
2093  *
2094  * Returns: (transfer full): A new #EContactPhoto struct.
2095  *
2096  * Since: 3.2
2097  **/
2098 EContactPhoto *
2099 e_contact_photo_new (void)
2100 {
2101         return g_new0 (EContactPhoto, 1);
2102 }
2103
2104 /**
2105  * e_contact_photo_free:
2106  * @photo: an #EContactPhoto struct
2107  *
2108  * Frees the @photo struct and its contents.
2109  **/
2110 void
2111 e_contact_photo_free (EContactPhoto *photo)
2112 {
2113         if (!photo)
2114                 return;
2115
2116         switch (photo->type) {
2117         case E_CONTACT_PHOTO_TYPE_INLINED:
2118                 g_free (photo->data.inlined.mime_type);
2119                 g_free (photo->data.inlined.data);
2120                 break;
2121         case E_CONTACT_PHOTO_TYPE_URI:
2122                 g_free (photo->data.uri);
2123                 break;
2124         default:
2125                 g_warning ("Unknown EContactPhotoType %d", photo->type);
2126                 break;
2127         }
2128
2129         g_free (photo);
2130 }
2131
2132 /**
2133  * e_contact_photo_copy:
2134  * @photo: an #EContactPhoto
2135  *
2136  * Creates a copy of @photo.
2137  *
2138  * Returns: A new #EContactPhoto struct identical to @photo.
2139  **/
2140 static EContactPhoto *
2141 e_contact_photo_copy (EContactPhoto *photo)
2142 {
2143         EContactPhoto *photo2 = g_new0 (EContactPhoto, 1);
2144         switch (photo->type) {
2145         case E_CONTACT_PHOTO_TYPE_INLINED:
2146                 photo2->type = E_CONTACT_PHOTO_TYPE_INLINED;
2147                 photo2->data.inlined.mime_type = g_strdup (photo->data.inlined.mime_type);
2148                 photo2->data.inlined.length = photo->data.inlined.length;
2149                 photo2->data.inlined.data = g_malloc (photo2->data.inlined.length);
2150                 memcpy (photo2->data.inlined.data, photo->data.inlined.data, photo->data.inlined.length);
2151                 break;
2152         case E_CONTACT_PHOTO_TYPE_URI:
2153                 photo2->type = E_CONTACT_PHOTO_TYPE_URI;
2154                 photo2->data.uri = g_strdup (photo->data.uri);
2155                 break;
2156         default:
2157                 g_warning ("Unknown EContactPhotoType %d", photo->type);
2158                 break;
2159         }
2160         return photo2;
2161 }
2162
2163 /**
2164  * e_contact_photo_get_inlined:
2165  * @photo: an #EContactPhoto
2166  * @len: (out caller-allocates) (transfer none): the length of the inlined data
2167  *
2168  * Gets the @photo's data.
2169  *
2170  * Returns: (transfer none) (array length=len) (allow-none): the inlined image in the
2171  * #EContactPhoto, or %NULL if it has not been set.
2172  *
2173  * Since: 3.2
2174  **/
2175 const guchar *
2176 e_contact_photo_get_inlined (EContactPhoto *photo,
2177                              gsize *len)
2178 {
2179         g_return_val_if_fail (photo != NULL, NULL);
2180         g_return_val_if_fail (photo->type == E_CONTACT_PHOTO_TYPE_INLINED, NULL);
2181
2182         *len = photo->data.inlined.length;
2183         return photo->data.inlined.data;
2184 }
2185
2186 /**
2187  * e_contact_photo_set_inlined:
2188  * @photo: an #EContactPhoto
2189  * @data: (transfer none) (array length=len): the inlined image data
2190  * @len: the length of @data
2191  *
2192  * Sets the @photo's inlined data.
2193  *
2194  * Since: 3.2
2195  **/
2196 void
2197 e_contact_photo_set_inlined (EContactPhoto *photo,
2198                              const guchar *data,
2199                              gsize len)
2200 {
2201         g_return_if_fail (photo != NULL);
2202         g_return_if_fail (photo->type == E_CONTACT_PHOTO_TYPE_INLINED);
2203
2204         photo->data.inlined.data = g_malloc (len);
2205         memcpy (photo->data.inlined.data, data, len);
2206         photo->data.inlined.length = len;
2207 }
2208
2209 /**
2210  * e_contact_photo_get_mime_type:
2211  * @photo: an #EContactPhoto
2212  *
2213  * Gets the @photo's mime type.
2214  *
2215  * Returns: (transfer none) (allow-none): the MIME type of the image, or %NULL if it has not been set.
2216  *
2217  * Since: 3.2
2218  **/
2219 const gchar *
2220 e_contact_photo_get_mime_type (EContactPhoto *photo)
2221 {
2222         g_return_val_if_fail (photo != NULL, NULL);
2223         g_return_val_if_fail (photo->type == E_CONTACT_PHOTO_TYPE_INLINED, NULL);
2224
2225         return photo->data.inlined.mime_type;
2226 }
2227
2228 /**
2229  * e_contact_photo_set_mime_type:
2230  * @photo: an #EContactPhoto
2231  * @mime_type: the mime type
2232  *
2233  * Sets the @photo's mime type.
2234  *
2235  * Since: 3.2
2236  **/
2237 void
2238 e_contact_photo_set_mime_type (EContactPhoto *photo,
2239                                const gchar *mime_type)
2240 {
2241         g_return_if_fail (photo != NULL);
2242         g_return_if_fail (photo->type == E_CONTACT_PHOTO_TYPE_INLINED);
2243
2244         g_free (photo->data.inlined.mime_type);
2245         photo->data.inlined.mime_type = g_strdup (mime_type);
2246 }
2247
2248 /**
2249  * e_contact_photo_get_uri:
2250  * @photo: an #EContactPhoto
2251  *
2252  * Gets the @photo's URI.
2253  *
2254  * Returns: (transfer none) (allow-none): the URI of the image, or %NULL if it has not been set
2255  *
2256  * Since: 3.2
2257  **/
2258 const gchar *
2259 e_contact_photo_get_uri (EContactPhoto *photo)
2260 {
2261         g_return_val_if_fail (photo != NULL, NULL);
2262         g_return_val_if_fail (photo->type == E_CONTACT_PHOTO_TYPE_URI, NULL);
2263
2264         return photo->data.uri;
2265 }
2266
2267 /**
2268  * e_contact_photo_set_uri:
2269  * @photo: an #EContactPhoto
2270  * @uri: the @photo's URI
2271  *
2272  * Sets the @photo's URI.
2273  *
2274  * Since: 3.2
2275  **/
2276 void
2277 e_contact_photo_set_uri (EContactPhoto *photo,
2278                          const gchar *uri)
2279 {
2280         g_return_if_fail (photo != NULL);
2281         g_return_if_fail (photo->type == E_CONTACT_PHOTO_TYPE_URI);
2282
2283         g_free (photo->data.uri);
2284         photo->data.uri = g_strdup (uri);
2285 }
2286
2287 /* Try to unescape a mime type which was encoded into
2288  * the filename, return the mime type if g_content_type_from_mime_type()
2289  * returns something for the decoded filename extension.
2290  */
2291 static gchar *
2292 mime_type_from_filename (const gchar *filename)
2293 {
2294         gchar *extension;
2295         gchar *mime_type;
2296         gchar *content_type;
2297
2298         extension = strrchr (filename, '.');
2299         if (extension)
2300                 extension++;
2301
2302         if (!extension)
2303                 return NULL;
2304
2305         mime_type    = g_uri_unescape_string (extension, NULL);
2306         content_type = g_content_type_from_mime_type (mime_type);
2307
2308         if (!content_type) {
2309                 g_free (mime_type);
2310                 mime_type = NULL;
2311         }
2312
2313         g_free (content_type);
2314
2315         return mime_type;
2316 }
2317
2318 static gboolean
2319 e_contact_photo_make_inline (EContactPhoto *photo,
2320                              GError **error)
2321 {
2322         gchar *filename;
2323         gchar *contents = NULL;
2324         gsize  length;
2325         gboolean success = FALSE;
2326
2327         /* Just a sanity check, this wont happen but return TRUE anyway */
2328         if (photo->type != E_CONTACT_PHOTO_TYPE_URI)
2329                 return TRUE;
2330
2331         filename = g_filename_from_uri (photo->data.uri, NULL, error);
2332         if (!filename)
2333                 return FALSE;
2334
2335         if (g_file_get_contents (filename, &contents, &length, error)) {
2336                 gchar *mime_type;
2337
2338                 mime_type = mime_type_from_filename (filename);
2339                 if (!mime_type) {
2340                         gchar *content_type =
2341                                 g_content_type_guess (NULL, (const guchar *) contents, length, NULL);
2342
2343                         if (content_type)
2344                                 mime_type = g_content_type_get_mime_type (content_type);
2345
2346                         g_free (content_type);
2347                 }
2348
2349                 g_free (photo->data.uri);
2350
2351                 photo->type = E_CONTACT_PHOTO_TYPE_INLINED;
2352                 photo->data.inlined.data      = (guchar *) contents;
2353                 photo->data.inlined.length    = length;
2354                 photo->data.inlined.mime_type = mime_type;
2355
2356                 success = TRUE;
2357         }
2358
2359         g_free (filename);
2360         return success;
2361 }
2362
2363 static gboolean
2364 e_contact_inline_photo_field (EContact *contact,
2365                               EContactField field,
2366                               GError **error)
2367 {
2368         EContactPhoto *photo;
2369         gboolean       success = TRUE;
2370
2371         photo = e_contact_get (contact, field);
2372         if (photo) {
2373
2374                 if (photo->type == E_CONTACT_PHOTO_TYPE_URI &&
2375                     g_str_has_prefix (photo->data.uri, "file://")) {
2376                         success = e_contact_photo_make_inline (photo, error);
2377
2378                         if (success)
2379                                 e_contact_set (contact, field, photo);
2380                 }
2381                 e_contact_photo_free (photo);
2382         }
2383
2384         return success;
2385 }
2386
2387 /**
2388  * e_contact_inline_local_photos:
2389  * @contact: an #EContact
2390  * @error: the location to store any #GError which might occur
2391  *
2392  * Tries to modify any #EContactPhoto fields which are
2393  * stored on the local file system as type %E_CONTACT_PHOTO_TYPE_URI
2394  * to be inlined and stored as %E_CONTACT_PHOTO_TYPE_INLINED instead.
2395  *
2396  * Returns: %TRUE if there were no errors, upon error %FALSE is returned
2397  *    and @error is set.
2398  *
2399  * Since: 3.4
2400  */
2401 gboolean
2402 e_contact_inline_local_photos (EContact *contact,
2403                                GError **error)
2404 {
2405         gboolean success = TRUE;
2406
2407         g_return_val_if_fail (E_IS_CONTACT (contact), FALSE);
2408
2409         success = e_contact_inline_photo_field (contact, E_CONTACT_PHOTO, error);
2410         if (success)
2411                 success = e_contact_inline_photo_field (contact, E_CONTACT_LOGO, error);
2412
2413         return success;
2414 }
2415
2416 E_CONTACT_DEFINE_BOXED_TYPE (e_contact_photo, "EContactPhoto")
2417
2418 /**
2419  * e_contact_geo_free:
2420  * @geo: an #EContactGeo
2421  *
2422  * Frees the @geo struct and its contents.
2423  *
2424  * Since: 1.12
2425  **/
2426 void
2427 e_contact_geo_free (EContactGeo *geo)
2428 {
2429         g_free (geo);
2430 }
2431
2432 static EContactGeo *
2433 e_contact_geo_copy (EContactGeo *geo)
2434 {
2435         EContactGeo *geo2 = g_new0 (EContactGeo, 1);
2436         geo2->latitude  = geo->latitude;
2437         geo2->longitude = geo->longitude;
2438
2439         return geo2;
2440 }
2441
2442 E_CONTACT_DEFINE_BOXED_TYPE (e_contact_geo, "EContactGeo")
2443
2444 /**
2445  * e_contact_address_new:
2446  *
2447  * Creates a new #EContactAddress struct.
2448  *
2449  * Returns: (transfer full): A new #EContactAddress struct.
2450  *
2451  * Since: 3.2
2452  **/
2453 EContactAddress *
2454 e_contact_address_new (void)
2455 {
2456         return g_new0 (EContactAddress, 1);
2457 }
2458
2459 /**
2460  * e_contact_address_free:
2461  * @address: an #EContactAddress
2462  *
2463  * Frees the @address struct and its contents.
2464  **/
2465 void
2466 e_contact_address_free (EContactAddress *address)
2467 {
2468         if (!address)
2469                 return;
2470
2471         g_free (address->address_format);
2472         g_free (address->po);
2473         g_free (address->ext);
2474         g_free (address->street);
2475         g_free (address->locality);
2476         g_free (address->region);
2477         g_free (address->code);
2478         g_free (address->country);
2479
2480         g_free (address);
2481 }
2482
2483 static EContactAddress *
2484 e_contact_address_copy (EContactAddress *address)
2485 {
2486         EContactAddress *address2 = g_new0 (EContactAddress, 1);
2487
2488         address2->address_format = g_strdup (address->address_format);
2489         address2->po = g_strdup (address->po);
2490         address2->ext = g_strdup (address->ext);
2491         address2->street = g_strdup (address->street);
2492         address2->locality = g_strdup (address->locality);
2493         address2->region = g_strdup (address->region);
2494         address2->code = g_strdup (address->code);
2495         address2->country = g_strdup (address->country);
2496
2497         return address2;
2498 }
2499
2500 E_CONTACT_DEFINE_BOXED_TYPE (e_contact_address, "EContactAddress")
2501
2502 /**
2503  * e_contact_cert_free:
2504  * @cert: an #EContactCert
2505  *
2506  * Frees the @cert struct and its contents.
2507  **/
2508 void
2509 e_contact_cert_free (EContactCert *cert)
2510 {
2511         if (!cert)
2512                 return;
2513
2514         g_free (cert->data);
2515         g_free (cert);
2516 }
2517
2518 static EContactCert *
2519 e_contact_cert_copy (EContactCert *cert)
2520 {
2521         EContactCert *cert2 = g_new0 (EContactCert, 1);
2522         cert2->length = cert->length;
2523         cert2->data = g_malloc (cert2->length);
2524         memcpy (cert2->data, cert->data, cert->length);
2525
2526         return cert2;
2527 }
2528
2529 E_CONTACT_DEFINE_BOXED_TYPE (e_contact_cert, "EContactCert")