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