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