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