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