1 /* -*- Mode: C; tab-width: 8; indent-tabs-mode: t; c-basic-offset: 8 -*- */
4 * Copyright (C) 1999-2008 Novell, Inc. (www.novell.com)
6 * This program is free software; you can redistribute it and/or
7 * modify it under the terms of version 2 of the GNU Lesser General Public
8 * License as published by the Free Software Foundation.
10 * This program is distributed in the hope that it will be useful,
11 * but WITHOUT ANY WARRANTY; without even the implied warranty of
12 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
13 * General Public License for more details.
15 * You should have received a copy of the GNU Lesser General Public
16 * License along with this program; if not, write to the
17 * Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor,
18 * Boston, MA 02110-1301, USA.
20 * Author: Chris Toshok (toshok@ximian.com)
31 #include <glib/gi18n-lib.h>
32 #include "e-contact.h"
33 #include "e-name-western.h"
36 #include "libedataserver/e-data-server-util.h"
38 #define LOCALEDIR e_util_get_localedir ()
43 #define E_CONTACT_GET_PRIVATE(obj) \
44 (G_TYPE_INSTANCE_GET_PRIVATE \
45 ((obj), E_TYPE_CONTACT, EContactPrivate))
47 G_DEFINE_TYPE (EContact, e_contact, E_TYPE_VCARD)
49 struct _EContactPrivate {
50 gchar *cached_strings[E_CONTACT_FIELD_LAST];
53 #define E_CONTACT_FIELD_TYPE_STRING 0x00000001 /* used for simple single valued attributes */
54 /*E_CONTACT_FIELD_TYPE_FLOAT*/
55 #define E_CONTACT_FIELD_TYPE_LIST 0x00000002 /* used for multivalued single attributes - the elements are of type gchar * */
56 #define E_CONTACT_FIELD_TYPE_MULTI 0x00000004 /* used for multivalued attributes - the elements are of type EVCardAttribute */
57 #define E_CONTACT_FIELD_TYPE_GETSET 0x00000008 /* used for attributes that need custom handling for getting/setting */
58 #define E_CONTACT_FIELD_TYPE_STRUCT 0x00000010 /* used for structured types (N and ADR properties, in particular) */
59 #define E_CONTACT_FIELD_TYPE_BOOLEAN 0x00000020 /* used for boolean types (WANTS_HTML) */
61 #define E_CONTACT_FIELD_TYPE_SYNTHETIC 0x10000000 /* used when there isn't a corresponding vcard field (such as email_1) */
62 #define E_CONTACT_FIELD_TYPE_LIST_ELEM 0x20000000 /* used when a synthetic attribute is a numbered list element */
63 #define E_CONTACT_FIELD_TYPE_MULTI_ELEM 0x40000000 /* used when we're looking for the nth attribute where more than 1 can be present in the vcard */
64 #define E_CONTACT_FIELD_TYPE_ATTR_TYPE 0x80000000 /* used when a synthetic attribute is flagged with a TYPE= that we'll be looking for */
69 EContactField field_id;
70 const gchar *vcard_field_name;
71 const gchar *field_name; /* non translated */
72 const gchar *pretty_name; /* translated */
77 const gchar *attr_type1;
78 const gchar *attr_type2;
80 gpointer (*struct_getter)(EContact *contact, EVCardAttribute *attribute);
81 void (*struct_setter)(EContact *contact, EVCardAttribute *attribute, gpointer data);
83 GType (*boxed_type_getter) (void);
86 static gpointer photo_getter (EContact *contact, EVCardAttribute *attr);
87 static void photo_setter (EContact *contact, EVCardAttribute *attr, gpointer data);
88 static gpointer geo_getter (EContact *contact, EVCardAttribute *attr);
89 static void geo_setter (EContact *contact, EVCardAttribute *attr, gpointer data);
90 static gpointer fn_getter (EContact *contact, EVCardAttribute *attr);
91 static void fn_setter (EContact *contact, EVCardAttribute *attr, gpointer data);
92 static gpointer n_getter (EContact *contact, EVCardAttribute *attr);
93 static void n_setter (EContact *contact, EVCardAttribute *attr, gpointer data);
94 static gpointer fileas_getter (EContact *contact, EVCardAttribute *attr);
95 static void fileas_setter (EContact *contact, EVCardAttribute *attr, gpointer data);
96 static gpointer adr_getter (EContact *contact, EVCardAttribute *attr);
97 static void adr_setter (EContact *contact, EVCardAttribute *attr, gpointer data);
98 static gpointer date_getter (EContact *contact, EVCardAttribute *attr);
99 static void date_setter (EContact *contact, EVCardAttribute *attr, gpointer data);
100 static gpointer cert_getter (EContact *contact, EVCardAttribute *attr);
101 static void cert_setter (EContact *contact, EVCardAttribute *attr, gpointer data);
103 #define STRING_FIELD(id,vc,n,pn,ro) { E_CONTACT_FIELD_TYPE_STRING, (id), (vc), (n), (pn), (ro) }
104 #define BOOLEAN_FIELD(id,vc,n,pn,ro) { E_CONTACT_FIELD_TYPE_BOOLEAN, (id), (vc), (n), (pn), (ro) }
105 #define LIST_FIELD(id,vc,n,pn,ro) { E_CONTACT_FIELD_TYPE_LIST, (id), (vc), (n), (pn), (ro) }
106 #define MULTI_LIST_FIELD(id,vc,n,pn,ro) { E_CONTACT_FIELD_TYPE_MULTI, (id), (vc), (n), (pn), (ro) }
107 #define GETSET_FIELD(id,vc,n,pn,ro,get,set) { E_CONTACT_FIELD_TYPE_STRING | E_CONTACT_FIELD_TYPE_GETSET, (id), (vc), (n), (pn), (ro), -1, NULL, NULL, (get), (set) }
108 #define STRUCT_FIELD(id,vc,n,pn,ro,get,set,ty) { E_CONTACT_FIELD_TYPE_STRUCT | E_CONTACT_FIELD_TYPE_GETSET, (id), (vc), (n), (pn), (ro), -1, NULL, NULL, (get), (set), (ty) }
109 #define SYNTH_STR_FIELD(id,n,pn,ro) { E_CONTACT_FIELD_TYPE_STRING | E_CONTACT_FIELD_TYPE_SYNTHETIC, (id), NULL, (n), (pn), (ro) }
110 #define LIST_ELEM_STR_FIELD(id,vc,n,pn,ro,nm) { E_CONTACT_FIELD_TYPE_LIST_ELEM | E_CONTACT_FIELD_TYPE_SYNTHETIC | E_CONTACT_FIELD_TYPE_STRING, (id), (vc), (n), (pn), (ro), (nm) }
111 #define MULTI_ELEM_STR_FIELD(id,vc,n,pn,ro,nm) { E_CONTACT_FIELD_TYPE_MULTI_ELEM | E_CONTACT_FIELD_TYPE_SYNTHETIC | E_CONTACT_FIELD_TYPE_STRING, (id), (vc), (n), (pn), (ro), (nm) }
112 #define ATTR_TYPE_STR_FIELD(id,vc,n,pn,ro,at1,nth) { E_CONTACT_FIELD_TYPE_ATTR_TYPE | E_CONTACT_FIELD_TYPE_SYNTHETIC | E_CONTACT_FIELD_TYPE_STRING, (id), (vc), (n), (pn), (ro), (nth), (at1), NULL }
113 #define ATTR_TYPE_GETSET_FIELD(id,vc,n,pn,ro,at1,nth,get,set) { E_CONTACT_FIELD_TYPE_ATTR_TYPE | E_CONTACT_FIELD_TYPE_GETSET, (id), (vc), (n), (pn), (ro), (nth), (at1), NULL, (get), (set) }
114 #define ATTR2_TYPE_STR_FIELD(id,vc,n,pn,ro,at1,at2,nth) { E_CONTACT_FIELD_TYPE_ATTR_TYPE | E_CONTACT_FIELD_TYPE_SYNTHETIC | E_CONTACT_FIELD_TYPE_STRING, (id), (vc), (n), (pn), (ro), (nth), (at1), (at2) }
115 #define ATTR_TYPE_STRUCT_FIELD(id,vc,n,pn,ro,at,get,set,ty) { E_CONTACT_FIELD_TYPE_ATTR_TYPE | E_CONTACT_FIELD_TYPE_SYNTHETIC | E_CONTACT_FIELD_TYPE_GETSET | E_CONTACT_FIELD_TYPE_STRUCT, (id), (vc), (n), (pn), (ro), 0, (at), NULL, (get), (set), (ty) }
117 /* This *must* be kept in the same order as the EContactField enum */
118 static const EContactFieldInfo field_info[] = {
119 {0,}, /* Dummy row as EContactField starts from 1 */
120 STRING_FIELD (E_CONTACT_UID, EVC_UID, "id", N_("Unique ID"), FALSE),
121 /* FILE_AS is not really a structured field - we use a getter/setter
122 * so we can generate its value if necessary in the getter */
123 /* Translators: This is an EContact field description, in this case it's a
124 * preferred user's description (or display name) of the contact. Note 'File' is a verb here. */
125 GETSET_FIELD (E_CONTACT_FILE_AS, EVC_X_FILE_AS, "file_as", N_("File Under"), FALSE, fileas_getter, fileas_setter),
126 /* URI of the book to which the contact belongs to */
127 STRING_FIELD (E_CONTACT_BOOK_UID, EVC_X_BOOK_UID, "book_uid", N_("Book UID"), FALSE),
130 /* FN isn't really a structured field - we use a getter/setter
131 * so we can set the N property (since evo 1.4 works fine with
132 * vcards that don't even have a N attribute. *sigh*) */
133 GETSET_FIELD (E_CONTACT_FULL_NAME, EVC_FN, "full_name", N_("Full Name"), FALSE, fn_getter, fn_setter),
134 LIST_ELEM_STR_FIELD (E_CONTACT_GIVEN_NAME, EVC_N, "given_name", N_("Given Name"), FALSE, 1),
135 LIST_ELEM_STR_FIELD (E_CONTACT_FAMILY_NAME, EVC_N, "family_name", N_("Family Name"), FALSE, 0),
136 STRING_FIELD (E_CONTACT_NICKNAME, EVC_NICKNAME, "nickname", N_("Nickname"), FALSE),
139 MULTI_ELEM_STR_FIELD (E_CONTACT_EMAIL_1, EVC_EMAIL, "email_1", N_("Email 1"), FALSE, 0),
140 MULTI_ELEM_STR_FIELD (E_CONTACT_EMAIL_2, EVC_EMAIL, "email_2", N_("Email 2"), FALSE, 1),
141 MULTI_ELEM_STR_FIELD (E_CONTACT_EMAIL_3, EVC_EMAIL, "email_3", N_("Email 3"), FALSE, 2),
142 MULTI_ELEM_STR_FIELD (E_CONTACT_EMAIL_4, EVC_EMAIL, "email_4", N_("Email 4"), FALSE, 3),
144 STRING_FIELD (E_CONTACT_MAILER, EVC_MAILER, "mailer", N_("Mailer"), FALSE),
147 ATTR_TYPE_STR_FIELD (E_CONTACT_ADDRESS_LABEL_HOME, EVC_LABEL, "address_label_home", N_("Home Address Label"), FALSE, "HOME", 0),
148 ATTR_TYPE_STR_FIELD (E_CONTACT_ADDRESS_LABEL_WORK, EVC_LABEL, "address_label_work", N_("Work Address Label"), FALSE, "WORK", 0),
149 ATTR_TYPE_STR_FIELD (E_CONTACT_ADDRESS_LABEL_OTHER, EVC_LABEL, "address_label_other", N_("Other Address Label"), FALSE, "OTHER", 0),
152 ATTR_TYPE_STR_FIELD (E_CONTACT_PHONE_ASSISTANT, EVC_TEL, "assistant_phone", N_("Assistant Phone"), FALSE, EVC_X_ASSISTANT, 0),
153 ATTR2_TYPE_STR_FIELD (E_CONTACT_PHONE_BUSINESS, EVC_TEL, "business_phone", N_("Business Phone"), FALSE, "WORK", "VOICE", 0),
154 ATTR2_TYPE_STR_FIELD (E_CONTACT_PHONE_BUSINESS_2, EVC_TEL, "business_phone_2", N_("Business Phone 2"), FALSE, "WORK", "VOICE", 1),
155 ATTR2_TYPE_STR_FIELD (E_CONTACT_PHONE_BUSINESS_FAX, EVC_TEL, "business_fax", N_("Business Fax"), FALSE, "WORK", "FAX", 0),
156 ATTR_TYPE_STR_FIELD (E_CONTACT_PHONE_CALLBACK, EVC_TEL, "callback_phone", N_("Callback Phone"), FALSE, EVC_X_CALLBACK, 0),
157 ATTR_TYPE_STR_FIELD (E_CONTACT_PHONE_CAR, EVC_TEL, "car_phone", N_("Car Phone"), FALSE, "CAR", 0),
158 ATTR_TYPE_STR_FIELD (E_CONTACT_PHONE_COMPANY, EVC_TEL, "company_phone", N_("Company Phone"), FALSE, EVC_X_COMPANY, 0),
159 ATTR2_TYPE_STR_FIELD (E_CONTACT_PHONE_HOME, EVC_TEL, "home_phone", N_("Home Phone"), FALSE, "HOME", "VOICE", 0),
160 ATTR2_TYPE_STR_FIELD (E_CONTACT_PHONE_HOME_2, EVC_TEL, "home_phone_2", N_("Home Phone 2"), FALSE, "HOME", "VOICE", 1),
161 ATTR2_TYPE_STR_FIELD (E_CONTACT_PHONE_HOME_FAX, EVC_TEL, "home_fax", N_("Home Fax"), FALSE, "HOME", "FAX", 0),
162 ATTR_TYPE_STR_FIELD (E_CONTACT_PHONE_ISDN, EVC_TEL, "isdn_phone", N_("ISDN"), FALSE, "ISDN", 0),
163 ATTR_TYPE_STR_FIELD (E_CONTACT_PHONE_MOBILE, EVC_TEL, "mobile_phone", N_("Mobile Phone"), FALSE, "CELL", 0),
164 ATTR_TYPE_STR_FIELD (E_CONTACT_PHONE_OTHER, EVC_TEL, "other_phone", N_("Other Phone"), FALSE, "VOICE", 0),
165 ATTR_TYPE_STR_FIELD (E_CONTACT_PHONE_OTHER_FAX, EVC_TEL, "other_fax", N_("Other Fax"), FALSE, "FAX", 0),
166 ATTR_TYPE_STR_FIELD (E_CONTACT_PHONE_PAGER, EVC_TEL, "pager", N_("Pager"), FALSE, "PAGER", 0),
167 ATTR_TYPE_STR_FIELD (E_CONTACT_PHONE_PRIMARY, EVC_TEL, "primary_phone", N_("Primary Phone"), FALSE, "PREF", 0),
168 ATTR_TYPE_STR_FIELD (E_CONTACT_PHONE_RADIO, EVC_TEL, "radio", N_("Radio"), FALSE, EVC_X_RADIO, 0),
169 ATTR_TYPE_STR_FIELD (E_CONTACT_PHONE_TELEX, EVC_TEL, "telex", N_("Telex"), FALSE, EVC_X_TELEX, 0),
170 /* To translators: TTY is Teletypewriter */
171 ATTR_TYPE_STR_FIELD (E_CONTACT_PHONE_TTYTDD, EVC_TEL, "tty", N_("TTY"), FALSE, EVC_X_TTYTDD, 0),
173 /* Organizational fields */
174 LIST_ELEM_STR_FIELD (E_CONTACT_ORG, EVC_ORG, "org", N_("Organization"), FALSE, 0),
175 LIST_ELEM_STR_FIELD (E_CONTACT_ORG_UNIT, EVC_ORG, "org_unit", N_("Organizational Unit"), FALSE, 1),
176 LIST_ELEM_STR_FIELD (E_CONTACT_OFFICE, EVC_ORG, "office", N_("Office"), FALSE, 2),
177 STRING_FIELD (E_CONTACT_TITLE, EVC_TITLE, "title", N_("Title"), FALSE),
178 STRING_FIELD (E_CONTACT_ROLE, EVC_ROLE, "role", N_("Role"), FALSE),
179 STRING_FIELD (E_CONTACT_MANAGER, EVC_X_MANAGER, "manager", N_("Manager"), FALSE),
180 STRING_FIELD (E_CONTACT_ASSISTANT, EVC_X_ASSISTANT, "assistant", N_("Assistant"), FALSE),
183 STRING_FIELD (E_CONTACT_HOMEPAGE_URL, EVC_URL, "homepage_url", N_("Homepage URL"), FALSE),
184 STRING_FIELD (E_CONTACT_BLOG_URL, EVC_X_BLOG_URL, "blog_url", N_("Weblog URL"), FALSE),
186 /* Contact categories */
187 SYNTH_STR_FIELD (E_CONTACT_CATEGORIES, "categories", N_("Categories"), FALSE),
189 /* Collaboration fields */
190 STRING_FIELD (E_CONTACT_CALENDAR_URI, EVC_CALURI, "caluri", N_("Calendar URI"), FALSE),
191 STRING_FIELD (E_CONTACT_FREEBUSY_URL, EVC_FBURL, "fburl", N_("Free/Busy URL"), FALSE),
192 STRING_FIELD (E_CONTACT_ICS_CALENDAR, EVC_ICSCALENDAR, "icscalendar", N_("ICS Calendar"), FALSE),
193 STRING_FIELD (E_CONTACT_VIDEO_URL, EVC_X_VIDEO_URL, "video_url", N_("Video Conferencing URL"), FALSE),
196 STRING_FIELD (E_CONTACT_SPOUSE, EVC_X_SPOUSE, "spouse", N_("Spouse's Name"), FALSE),
197 STRING_FIELD (E_CONTACT_NOTE, EVC_NOTE, "note", N_("Note"), FALSE),
199 /* Instant messaging fields */
200 ATTR_TYPE_STR_FIELD (E_CONTACT_IM_AIM_HOME_1, EVC_X_AIM, "im_aim_home_1", N_("AIM Home Screen Name 1"), FALSE, "HOME", 0),
201 ATTR_TYPE_STR_FIELD (E_CONTACT_IM_AIM_HOME_2, EVC_X_AIM, "im_aim_home_2", N_("AIM Home Screen Name 2"), FALSE, "HOME", 1),
202 ATTR_TYPE_STR_FIELD (E_CONTACT_IM_AIM_HOME_3, EVC_X_AIM, "im_aim_home_3", N_("AIM Home Screen Name 3"), FALSE, "HOME", 2),
203 ATTR_TYPE_STR_FIELD (E_CONTACT_IM_AIM_WORK_1, EVC_X_AIM, "im_aim_work_1", N_("AIM Work Screen Name 1"), FALSE, "WORK", 0),
204 ATTR_TYPE_STR_FIELD (E_CONTACT_IM_AIM_WORK_2, EVC_X_AIM, "im_aim_work_2", N_("AIM Work Screen Name 2"), FALSE, "WORK", 1),
205 ATTR_TYPE_STR_FIELD (E_CONTACT_IM_AIM_WORK_3, EVC_X_AIM, "im_aim_work_3", N_("AIM Work Screen Name 3"), FALSE, "WORK", 2),
206 ATTR_TYPE_STR_FIELD (E_CONTACT_IM_GROUPWISE_HOME_1, EVC_X_GROUPWISE, "im_groupwise_home_1", N_("GroupWise Home Screen Name 1"), FALSE, "HOME", 0),
207 ATTR_TYPE_STR_FIELD (E_CONTACT_IM_GROUPWISE_HOME_2, EVC_X_GROUPWISE, "im_groupwise_home_2", N_("GroupWise Home Screen Name 2"), FALSE, "HOME", 1),
208 ATTR_TYPE_STR_FIELD (E_CONTACT_IM_GROUPWISE_HOME_3, EVC_X_GROUPWISE, "im_groupwise_home_3", N_("GroupWise Home Screen Name 3"), FALSE, "HOME", 2),
209 ATTR_TYPE_STR_FIELD (E_CONTACT_IM_GROUPWISE_WORK_1, EVC_X_GROUPWISE, "im_groupwise_work_1", N_("GroupWise Work Screen Name 1"), FALSE, "WORK", 0),
210 ATTR_TYPE_STR_FIELD (E_CONTACT_IM_GROUPWISE_WORK_2, EVC_X_GROUPWISE, "im_groupwise_work_2", N_("GroupWise Work Screen Name 2"), FALSE, "WORK", 1),
211 ATTR_TYPE_STR_FIELD (E_CONTACT_IM_GROUPWISE_WORK_3, EVC_X_GROUPWISE, "im_groupwise_work_3", N_("GroupWise Work Screen Name 3"), FALSE, "WORK", 2),
212 ATTR_TYPE_STR_FIELD (E_CONTACT_IM_JABBER_HOME_1, EVC_X_JABBER, "im_jabber_home_1", N_("Jabber Home ID 1"), FALSE, "HOME", 0),
213 ATTR_TYPE_STR_FIELD (E_CONTACT_IM_JABBER_HOME_2, EVC_X_JABBER, "im_jabber_home_2", N_("Jabber Home ID 2"), FALSE, "HOME", 1),
214 ATTR_TYPE_STR_FIELD (E_CONTACT_IM_JABBER_HOME_3, EVC_X_JABBER, "im_jabber_home_3", N_("Jabber Home ID 3"), FALSE, "HOME", 2),
215 ATTR_TYPE_STR_FIELD (E_CONTACT_IM_JABBER_WORK_1, EVC_X_JABBER, "im_jabber_work_1", N_("Jabber Work ID 1"), FALSE, "WORK", 0),
216 ATTR_TYPE_STR_FIELD (E_CONTACT_IM_JABBER_WORK_2, EVC_X_JABBER, "im_jabber_work_3", N_("Jabber Work ID 2"), FALSE, "WORK", 1),
217 ATTR_TYPE_STR_FIELD (E_CONTACT_IM_JABBER_WORK_3, EVC_X_JABBER, "im_jabber_work_2", N_("Jabber Work ID 3"), FALSE, "WORK", 2),
218 ATTR_TYPE_STR_FIELD (E_CONTACT_IM_YAHOO_HOME_1, EVC_X_YAHOO, "im_yahoo_home_1", N_("Yahoo! Home Screen Name 1"), FALSE, "HOME", 0),
219 ATTR_TYPE_STR_FIELD (E_CONTACT_IM_YAHOO_HOME_2, EVC_X_YAHOO, "im_yahoo_home_2", N_("Yahoo! Home Screen Name 2"), FALSE, "HOME", 1),
220 ATTR_TYPE_STR_FIELD (E_CONTACT_IM_YAHOO_HOME_3, EVC_X_YAHOO, "im_yahoo_home_3", N_("Yahoo! Home Screen Name 3"), FALSE, "HOME", 2),
221 ATTR_TYPE_STR_FIELD (E_CONTACT_IM_YAHOO_WORK_1, EVC_X_YAHOO, "im_yahoo_work_1", N_("Yahoo! Work Screen Name 1"), FALSE, "WORK", 0),
222 ATTR_TYPE_STR_FIELD (E_CONTACT_IM_YAHOO_WORK_2, EVC_X_YAHOO, "im_yahoo_work_2", N_("Yahoo! Work Screen Name 2"), FALSE, "WORK", 1),
223 ATTR_TYPE_STR_FIELD (E_CONTACT_IM_YAHOO_WORK_3, EVC_X_YAHOO, "im_yahoo_work_3", N_("Yahoo! Work Screen Name 3"), FALSE, "WORK", 2),
224 ATTR_TYPE_STR_FIELD (E_CONTACT_IM_MSN_HOME_1, EVC_X_MSN, "im_msn_home_1", N_("MSN Home Screen Name 1"), FALSE, "HOME", 0),
225 ATTR_TYPE_STR_FIELD (E_CONTACT_IM_MSN_HOME_2, EVC_X_MSN, "im_msn_home_2", N_("MSN Home Screen Name 2"), FALSE, "HOME", 1),
226 ATTR_TYPE_STR_FIELD (E_CONTACT_IM_MSN_HOME_3, EVC_X_MSN, "im_msn_home_3", N_("MSN Home Screen Name 3"), FALSE, "HOME", 2),
227 ATTR_TYPE_STR_FIELD (E_CONTACT_IM_MSN_WORK_1, EVC_X_MSN, "im_msn_work_1", N_("MSN Work Screen Name 1"), FALSE, "WORK", 0),
228 ATTR_TYPE_STR_FIELD (E_CONTACT_IM_MSN_WORK_2, EVC_X_MSN, "im_msn_work_2", N_("MSN Work Screen Name 2"), FALSE, "WORK", 1),
229 ATTR_TYPE_STR_FIELD (E_CONTACT_IM_MSN_WORK_3, EVC_X_MSN, "im_msn_work_3", N_("MSN Work Screen Name 3"), FALSE, "WORK", 2),
230 ATTR_TYPE_STR_FIELD (E_CONTACT_IM_ICQ_HOME_1, EVC_X_ICQ, "im_icq_home_1", N_("ICQ Home ID 1"), FALSE, "HOME", 0),
231 ATTR_TYPE_STR_FIELD (E_CONTACT_IM_ICQ_HOME_2, EVC_X_ICQ, "im_icq_home_2", N_("ICQ Home ID 2"), FALSE, "HOME", 1),
232 ATTR_TYPE_STR_FIELD (E_CONTACT_IM_ICQ_HOME_3, EVC_X_ICQ, "im_icq_home_3", N_("ICQ Home ID 3"), FALSE, "HOME", 2),
233 ATTR_TYPE_STR_FIELD (E_CONTACT_IM_ICQ_WORK_1, EVC_X_ICQ, "im_icq_work_1", N_("ICQ Work ID 1"), FALSE, "WORK", 0),
234 ATTR_TYPE_STR_FIELD (E_CONTACT_IM_ICQ_WORK_2, EVC_X_ICQ, "im_icq_work_2", N_("ICQ Work ID 2"), FALSE, "WORK", 1),
235 ATTR_TYPE_STR_FIELD (E_CONTACT_IM_ICQ_WORK_3, EVC_X_ICQ, "im_icq_work_3", N_("ICQ Work ID 3"), FALSE, "WORK", 2),
237 /* Last modified time */
238 STRING_FIELD (E_CONTACT_REV, EVC_REV, "Rev", N_("Last Revision"), FALSE),
239 /* Translators: This is an EContact field description, in this case it's a
240 * virtual field, which returns either name of the contact or the organization
241 * name, recognized by multiple other fields, where the first filled is used. */
242 SYNTH_STR_FIELD (E_CONTACT_NAME_OR_ORG, "name_or_org", N_("Name or Org"), TRUE),
245 MULTI_LIST_FIELD (E_CONTACT_ADDRESS, EVC_ADR, "address", N_("Address List"), FALSE),
246 ATTR_TYPE_STRUCT_FIELD (E_CONTACT_ADDRESS_HOME, EVC_ADR, "address_home", N_("Home Address"), FALSE, "HOME", adr_getter, adr_setter, e_contact_address_get_type),
247 ATTR_TYPE_STRUCT_FIELD (E_CONTACT_ADDRESS_WORK, EVC_ADR, "address_work", N_("Work Address"), FALSE, "WORK", adr_getter, adr_setter, e_contact_address_get_type),
248 ATTR_TYPE_STRUCT_FIELD (E_CONTACT_ADDRESS_OTHER, EVC_ADR, "address_other", N_("Other Address"), FALSE, "OTHER", adr_getter, adr_setter, e_contact_address_get_type),
250 /* Contact categories */
251 LIST_FIELD (E_CONTACT_CATEGORY_LIST, EVC_CATEGORIES, "category_list", N_("Category List"), FALSE),
254 STRUCT_FIELD (E_CONTACT_PHOTO, EVC_PHOTO, "photo", N_("Photo"), FALSE, photo_getter, photo_setter, e_contact_photo_get_type),
255 STRUCT_FIELD (E_CONTACT_LOGO, EVC_LOGO, "logo", N_("Logo"), FALSE, photo_getter, photo_setter, e_contact_photo_get_type),
257 /* Translators: This is an EContact field description, in this case it's a name
258 * of the contact, as specified in http://tools.ietf.org/html/rfc6350#section-6.2.2 */
259 STRUCT_FIELD (E_CONTACT_NAME, EVC_N, "name", N_("Name"), FALSE, n_getter, n_setter, e_contact_name_get_type),
260 MULTI_LIST_FIELD (E_CONTACT_EMAIL, EVC_EMAIL, "email", N_("Email List"), FALSE),
262 /* Instant messaging fields */
263 MULTI_LIST_FIELD (E_CONTACT_IM_AIM, EVC_X_AIM, "im_aim", N_("AIM Screen Name List"), FALSE),
264 MULTI_LIST_FIELD (E_CONTACT_IM_GROUPWISE, EVC_X_GROUPWISE, "im_groupwise", N_("GroupWise ID List"), FALSE),
265 MULTI_LIST_FIELD (E_CONTACT_IM_JABBER, EVC_X_JABBER, "im_jabber", N_("Jabber ID List"), FALSE),
266 MULTI_LIST_FIELD (E_CONTACT_IM_YAHOO, EVC_X_YAHOO, "im_yahoo", N_("Yahoo! Screen Name List"), FALSE),
267 MULTI_LIST_FIELD (E_CONTACT_IM_MSN, EVC_X_MSN, "im_msn", N_("MSN Screen Name List"), FALSE),
268 MULTI_LIST_FIELD (E_CONTACT_IM_ICQ, EVC_X_ICQ, "im_icq", N_("ICQ ID List"), FALSE),
270 BOOLEAN_FIELD (E_CONTACT_WANTS_HTML, EVC_X_WANTS_HTML, "wants_html", N_("Wants HTML Mail"), FALSE),
272 /* Translators: This is an EContact field description, in this case it's a
273 * field describing whether it's a Contact list (list of email addresses) or a
274 * regular contact for one person/organization/... */
275 BOOLEAN_FIELD (E_CONTACT_IS_LIST, EVC_X_LIST, "list", N_("List"), FALSE),
276 /* Translators: This is an EContact field description, in this case it's a flag
277 * used to determine whether when sending to Contact lists the addresses should be
278 * shown or not to other recipients - basically whether to use BCC field or CC
279 * message header when sending messages to this Contact list. */
280 BOOLEAN_FIELD (E_CONTACT_LIST_SHOW_ADDRESSES, EVC_X_LIST_SHOW_ADDRESSES, "list_show_addresses", N_("List Shows Addresses"), FALSE),
282 STRUCT_FIELD (E_CONTACT_BIRTH_DATE, EVC_BDAY, "birth_date", N_("Birth Date"), FALSE, date_getter, date_setter, e_contact_date_get_type),
283 STRUCT_FIELD (E_CONTACT_ANNIVERSARY, EVC_X_ANNIVERSARY, "anniversary", N_("Anniversary"), FALSE, date_getter, date_setter, e_contact_date_get_type),
285 /* Security fields */
286 ATTR_TYPE_STRUCT_FIELD (E_CONTACT_X509_CERT, EVC_KEY, "x509Cert", N_("X.509 Certificate"), FALSE, "X509", cert_getter, cert_setter, e_contact_cert_get_type),
288 ATTR_TYPE_STR_FIELD (E_CONTACT_IM_GADUGADU_HOME_1, EVC_X_GADUGADU, "im_gadugadu_home_1", N_("Gadu-Gadu Home ID 1"), FALSE, "HOME", 0),
289 ATTR_TYPE_STR_FIELD (E_CONTACT_IM_GADUGADU_HOME_2, EVC_X_GADUGADU, "im_gadugadu_home_2", N_("Gadu-Gadu Home ID 2"), FALSE, "HOME", 1),
290 ATTR_TYPE_STR_FIELD (E_CONTACT_IM_GADUGADU_HOME_3, EVC_X_GADUGADU, "im_gadugadu_home_3", N_("Gadu-Gadu Home ID 3"), FALSE, "HOME", 2),
291 ATTR_TYPE_STR_FIELD (E_CONTACT_IM_GADUGADU_WORK_1, EVC_X_GADUGADU, "im_gadugadu_work_1", N_("Gadu-Gadu Work ID 1"), FALSE, "WORK", 0),
292 ATTR_TYPE_STR_FIELD (E_CONTACT_IM_GADUGADU_WORK_2, EVC_X_GADUGADU, "im_gadugadu_work_2", N_("Gadu-Gadu Work ID 2"), FALSE, "WORK", 1),
293 ATTR_TYPE_STR_FIELD (E_CONTACT_IM_GADUGADU_WORK_3, EVC_X_GADUGADU, "im_gadugadu_work_3", N_("Gadu-Gadu Work ID 3"), FALSE, "WORK", 2),
294 MULTI_LIST_FIELD (E_CONTACT_IM_GADUGADU, EVC_X_GADUGADU, "im_gadugadu", N_("Gadu-Gadu ID List"), FALSE),
296 /* Geo information */
297 STRUCT_FIELD (E_CONTACT_GEO, EVC_GEO, "geo", N_("Geographic Information"), FALSE, geo_getter, geo_setter, e_contact_geo_get_type),
299 MULTI_LIST_FIELD (E_CONTACT_TEL, EVC_TEL, "phone", N_("Telephone"), FALSE),
301 ATTR_TYPE_STR_FIELD (E_CONTACT_IM_SKYPE_HOME_1, EVC_X_SKYPE, "im_skype_home_1", N_("Skype Home Name 1"), FALSE, "HOME", 0),
302 ATTR_TYPE_STR_FIELD (E_CONTACT_IM_SKYPE_HOME_2, EVC_X_SKYPE, "im_skype_home_2", N_("Skype Home Name 2"), FALSE, "HOME", 1),
303 ATTR_TYPE_STR_FIELD (E_CONTACT_IM_SKYPE_HOME_3, EVC_X_SKYPE, "im_skype_home_3", N_("Skype Home Name 3"), FALSE, "HOME", 2),
304 ATTR_TYPE_STR_FIELD (E_CONTACT_IM_SKYPE_WORK_1, EVC_X_SKYPE, "im_skype_work_1", N_("Skype Work Name 1"), FALSE, "WORK", 0),
305 ATTR_TYPE_STR_FIELD (E_CONTACT_IM_SKYPE_WORK_2, EVC_X_SKYPE, "im_skype_work_2", N_("Skype Work Name 2"), FALSE, "WORK", 1),
306 ATTR_TYPE_STR_FIELD (E_CONTACT_IM_SKYPE_WORK_3, EVC_X_SKYPE, "im_skype_work_3", N_("Skype Work Name 3"), FALSE, "WORK", 2),
307 MULTI_LIST_FIELD (E_CONTACT_IM_SKYPE, EVC_X_SKYPE, "im_skype", N_("Skype Name List"), FALSE),
309 MULTI_LIST_FIELD (E_CONTACT_SIP, EVC_X_SIP, "sip", N_("SIP address"), FALSE),
311 ATTR_TYPE_STR_FIELD (E_CONTACT_IM_GOOGLE_TALK_HOME_1, EVC_X_GOOGLE_TALK, "im_google_talk_home_1", N_("Google Talk Home Name 1"), FALSE, "HOME", 0),
312 ATTR_TYPE_STR_FIELD (E_CONTACT_IM_GOOGLE_TALK_HOME_2, EVC_X_GOOGLE_TALK, "im_google_talk_home_2", N_("Google Talk Home Name 2"), FALSE, "HOME", 1),
313 ATTR_TYPE_STR_FIELD (E_CONTACT_IM_GOOGLE_TALK_HOME_3, EVC_X_GOOGLE_TALK, "im_google_talk_home_3", N_("Google Talk Home Name 3"), FALSE, "HOME", 2),
314 ATTR_TYPE_STR_FIELD (E_CONTACT_IM_GOOGLE_TALK_WORK_1, EVC_X_GOOGLE_TALK, "im_google_talk_work_1", N_("Google Talk Work Name 1"), FALSE, "WORK", 0),
315 ATTR_TYPE_STR_FIELD (E_CONTACT_IM_GOOGLE_TALK_WORK_2, EVC_X_GOOGLE_TALK, "im_google_talk_work_2", N_("Google Talk Work Name 2"), FALSE, "WORK", 1),
316 ATTR_TYPE_STR_FIELD (E_CONTACT_IM_GOOGLE_TALK_WORK_3, EVC_X_GOOGLE_TALK, "im_google_talk_work_3", N_("Google Talk Work Name 3"), FALSE, "WORK", 2),
317 MULTI_LIST_FIELD (E_CONTACT_IM_GOOGLE_TALK, EVC_X_GOOGLE_TALK, "im_google_talk", N_("Google Talk Name List"), FALSE),
319 MULTI_LIST_FIELD (E_CONTACT_IM_TWITTER, EVC_X_TWITTER, "im_twitter", N_("Twitter Name List"), FALSE)
322 #undef LIST_ELEM_STR_FIELD
324 #undef SYNTH_STR_FIELD
328 static void e_contact_set_property (GObject *object, guint prop_id, const GValue *value, GParamSpec *pspec);
329 static void e_contact_get_property (GObject *object, guint prop_id, GValue *value, GParamSpec *pspec);
332 e_contact_finalize (GObject *object)
334 EContactPrivate *priv;
337 priv = E_CONTACT_GET_PRIVATE (object);
339 for (ii = E_CONTACT_FIELD_FIRST; ii < E_CONTACT_FIELD_LAST; ii++)
340 g_free (priv->cached_strings[ii]);
342 /* Chain up to parent's finalize() method. */
343 G_OBJECT_CLASS (e_contact_parent_class)->finalize (object);
347 e_contact_class_init (EContactClass *class)
349 GObjectClass *object_class;
352 g_type_class_add_private (class, sizeof (EContactPrivate));
354 object_class = G_OBJECT_CLASS (class);
355 object_class->set_property = e_contact_set_property;
356 object_class->get_property = e_contact_get_property;
357 object_class->finalize = e_contact_finalize;
359 for (ii = E_CONTACT_FIELD_FIRST; ii < E_CONTACT_FIELD_LAST; ii++) {
360 GParamSpec *pspec = NULL;
363 /* Verify the table is correctly ordered */
364 g_assert (ii == field_info[ii].field_id);
366 flags = G_PARAM_READABLE |
367 G_PARAM_STATIC_NICK |
368 G_PARAM_STATIC_BLURB;
370 if (!field_info[ii].read_only)
371 flags |= G_PARAM_WRITABLE;
373 if (field_info[ii].t & E_CONTACT_FIELD_TYPE_STRING)
374 pspec = g_param_spec_string (
375 field_info[ii].field_name,
376 _(field_info[ii].pretty_name),
377 field_info[ii].pretty_name,
380 else if (field_info[ii].t & E_CONTACT_FIELD_TYPE_BOOLEAN)
381 pspec = g_param_spec_boolean (
382 field_info[ii].field_name,
383 _(field_info[ii].pretty_name),
384 field_info[ii].pretty_name,
387 else if (field_info[ii].t & E_CONTACT_FIELD_TYPE_STRUCT)
388 pspec = g_param_spec_boxed (
389 field_info[ii].field_name,
390 _(field_info[ii].pretty_name),
391 field_info[ii].pretty_name,
392 field_info[ii].boxed_type_getter (),
394 else if (field_info[ii].t & E_CONTACT_FIELD_TYPE_MULTI)
395 pspec = g_param_spec_boxed (
396 field_info[ii].field_name,
397 _(field_info[ii].pretty_name),
398 field_info[ii].pretty_name,
399 E_TYPE_CONTACT_ATTR_LIST,
402 pspec = g_param_spec_pointer (
403 field_info[ii].field_name,
404 _(field_info[ii].pretty_name),
405 field_info[ii].pretty_name,
408 g_object_class_install_property (
409 object_class, field_info[ii].field_id, pspec);
414 e_contact_init (EContact *ec)
416 ec->priv = E_CONTACT_GET_PRIVATE (ec);
420 geo_getter (EContact *contact,
421 EVCardAttribute *attr)
424 GList *p = e_vcard_attribute_get_values (attr);
425 EContactGeo *geo = g_new0 (EContactGeo, 1);
427 geo->latitude = (p && p->data ? g_ascii_strtod (p->data, NULL) : 0); if (p) p = p->next;
428 geo->longitude = (p && p->data ? g_ascii_strtod (p->data, NULL) : 0);
437 geo_setter (EContact *contact,
438 EVCardAttribute *attr,
441 EContactGeo *geo = data;
442 gchar buf[G_ASCII_DTOSTR_BUF_SIZE];
444 e_vcard_attribute_add_value
445 (attr, g_ascii_dtostr (buf, sizeof (buf), geo->latitude));
447 e_vcard_attribute_add_value
448 (attr, g_ascii_dtostr (buf, sizeof (buf), geo->longitude));
452 photo_getter (EContact *contact,
453 EVCardAttribute *attr)
460 values = e_vcard_attribute_get_param (attr, EVC_ENCODING);
461 if (values && (g_ascii_strcasecmp (values->data, "b") == 0 ||
462 /* second for photo vCard 2.1 support */
463 g_ascii_strcasecmp (values->data, "base64") == 0)) {
464 values = e_vcard_attribute_get_values_decoded (attr);
465 if (values && values->data) {
466 GString *s = values->data;
467 EContactPhoto *photo;
472 photo = g_new0 (EContactPhoto, 1);
473 photo->type = E_CONTACT_PHOTO_TYPE_INLINED;
474 photo->data.inlined.length = s->len;
475 photo->data.inlined.data = g_malloc (photo->data.inlined.length);
476 memcpy (photo->data.inlined.data, s->str, photo->data.inlined.length);
478 values = e_vcard_attribute_get_param (attr, EVC_TYPE);
479 if (values && values->data)
480 photo->data.inlined.mime_type = g_strdup_printf ("image/%s", (gchar *) values->data);
485 values = e_vcard_attribute_get_param (attr, EVC_VALUE);
486 if (values && g_ascii_strcasecmp (values->data, "uri") == 0) {
487 EContactPhoto *photo;
488 photo = g_new0 (EContactPhoto, 1);
489 photo->type = E_CONTACT_PHOTO_TYPE_URI;
490 photo->data.uri = e_vcard_attribute_get_value (attr);
497 photo_setter (EContact *contact,
498 EVCardAttribute *attr,
501 EContactPhoto *photo = data;
502 const gchar *image_type, *p;
504 switch (photo->type) {
505 case E_CONTACT_PHOTO_TYPE_INLINED:
506 g_return_if_fail (photo->data.inlined.length > 0);
508 e_vcard_attribute_add_param_with_value (
510 e_vcard_attribute_param_new (EVC_ENCODING),
512 if (photo->data.inlined.mime_type && (p = strchr (photo->data.inlined.mime_type, '/'))) {
515 image_type = "X-EVOLUTION-UNKNOWN";
517 e_vcard_attribute_add_param_with_value (
519 e_vcard_attribute_param_new (EVC_TYPE),
522 e_vcard_attribute_add_value_decoded (attr, (gchar *) photo->data.inlined.data, photo->data.inlined.length);
524 case E_CONTACT_PHOTO_TYPE_URI:
525 e_vcard_attribute_add_param_with_value (
527 e_vcard_attribute_param_new (EVC_VALUE),
529 e_vcard_attribute_add_value (attr, photo->data.uri);
532 g_warning ("Unknown EContactPhotoType %d", photo->type);
539 fn_getter (EContact *contact,
540 EVCardAttribute *attr)
542 /* this fills FN, if not there yet */
546 name = e_contact_get (contact, E_CONTACT_NAME);
548 e_contact_name_free (name);
550 attr = e_vcard_get_attribute (E_VCARD (contact), EVC_FN);
554 GList *p = e_vcard_attribute_get_values (attr);
556 return p && p->data ? p->data : (gpointer) "";
562 fn_setter (EContact *contact,
563 EVCardAttribute *attr,
566 gchar *name_str = data;
568 e_vcard_attribute_add_value (attr, name_str);
570 attr = e_vcard_get_attribute (E_VCARD (contact), EVC_N);
572 EContactName *name = e_contact_name_from_string ((gchar *) data);
574 attr = e_vcard_attribute_new (NULL, EVC_N);
575 e_vcard_append_attribute (E_VCARD (contact), attr);
577 /* call the setter directly */
578 n_setter (contact, attr, name);
580 e_contact_name_free (name);
585 fileas_getter (EContact *contact,
586 EVCardAttribute *attr)
591 p = e_vcard_attribute_get_values (attr);
592 if (!p || !p->data || !*((const gchar *) p->data))
597 /* Generate a FILE_AS field */
599 gchar *new_file_as = NULL;
601 name = e_contact_get (contact, E_CONTACT_NAME);
603 /* Use name if available */
605 gchar *strings[3], **stringptr;
608 if (name->family && *name->family)
609 *(stringptr++) = name->family;
610 if (name->given && *name->given)
611 *(stringptr++) = name->given;
612 if (stringptr != strings) {
614 new_file_as = g_strjoinv (", ", strings);
617 e_contact_name_free (name);
620 /* Use org as fallback */
622 const gchar *org = e_contact_get_const (contact, E_CONTACT_ORG);
625 new_file_as = g_strdup (org);
629 /* Add the FILE_AS attribute to the vcard */
631 attr = e_vcard_attribute_new (NULL, EVC_X_FILE_AS);
632 e_vcard_add_attribute_with_value (E_VCARD (contact), attr, new_file_as);
634 g_free (new_file_as);
639 p = e_vcard_attribute_get_values (attr);
641 return p && p->data ? p->data : (gpointer) "";
648 fileas_setter (EContact *contact,
649 EVCardAttribute *attr,
652 /* Default implementation */
653 const gchar *file_as = data;
654 e_vcard_attribute_add_value (attr, file_as ? : "");
660 n_getter (EContact *contact,
661 EVCardAttribute *attr)
663 EContactName *name = g_new0 (EContactName, 1);
664 EVCardAttribute *new_attr;
668 GList *p = e_vcard_attribute_get_values (attr);
670 name->family = g_strdup (p && p->data ? p->data : ""); if (p) p = p->next;
671 name->given = g_strdup (p && p->data ? p->data : ""); if (p) p = p->next;
672 name->additional = g_strdup (p && p->data ? p->data : ""); if (p) p = p->next;
673 name->prefixes = g_strdup (p && p->data ? p->data : ""); if (p) p = p->next;
674 name->suffixes = g_strdup (p && p->data ? p->data : "");
677 new_attr = e_vcard_get_attribute (E_VCARD (contact), EVC_FN);
679 new_attr = e_vcard_attribute_new (NULL, EVC_FN);
680 e_vcard_append_attribute (E_VCARD (contact), new_attr);
681 name_str = e_contact_name_to_string (name);
682 e_vcard_attribute_add_value (new_attr, name_str);
690 n_setter (EContact *contact,
691 EVCardAttribute *attr,
694 EContactName *name = data;
696 e_vcard_attribute_add_value (attr, name->family ? name->family : "");
697 e_vcard_attribute_add_value (attr, name->given ? name->given : "");
698 e_vcard_attribute_add_value (attr, name->additional ? name->additional : "");
699 e_vcard_attribute_add_value (attr, name->prefixes ? name->prefixes : "");
700 e_vcard_attribute_add_value (attr, name->suffixes ? name->suffixes : "");
702 /* now find the attribute for FileAs. if it's not present, fill it in */
703 attr = e_vcard_get_attribute (E_VCARD (contact), EVC_X_FILE_AS);
705 gchar *strings[3], **stringptr;
707 attr = e_vcard_attribute_new (NULL, EVC_X_FILE_AS);
708 e_vcard_append_attribute (E_VCARD (contact), attr);
711 if (name->family && *name->family)
712 *(stringptr++) = name->family;
713 if (name->given && *name->given)
714 *(stringptr++) = name->given;
716 string = g_strjoinv (", ", strings);
718 e_vcard_attribute_add_value (attr, string);
727 adr_getter (EContact *contact,
728 EVCardAttribute *attr)
731 GList *p = e_vcard_attribute_get_values (attr);
732 EContactAddress *addr = g_new0 (EContactAddress, 1);
734 addr->address_format = g_strdup ("");
735 addr->po = g_strdup (p && p->data ? p->data : ""); if (p) p = p->next;
736 addr->ext = g_strdup (p && p->data ? p->data : ""); if (p) p = p->next;
737 addr->street = g_strdup (p && p->data ? p->data : ""); if (p) p = p->next;
738 addr->locality = g_strdup (p && p->data ? p->data : ""); if (p) p = p->next;
739 addr->region = g_strdup (p && p->data ? p->data : ""); if (p) p = p->next;
740 addr->code = g_strdup (p && p->data ? p->data : ""); if (p) p = p->next;
741 addr->country = g_strdup (p && p->data ? p->data : ""); if (p) p = p->next;
750 adr_setter (EContact *contact,
751 EVCardAttribute *attr,
754 EContactAddress *addr = data;
756 e_vcard_attribute_add_value (attr, addr->po);
757 e_vcard_attribute_add_value (attr, addr->ext);
758 e_vcard_attribute_add_value (attr, addr->street);
759 e_vcard_attribute_add_value (attr, addr->locality);
760 e_vcard_attribute_add_value (attr, addr->region);
761 e_vcard_attribute_add_value (attr, addr->code);
762 e_vcard_attribute_add_value (attr, addr->country);
768 date_getter (EContact *contact,
769 EVCardAttribute *attr)
772 GList *p = e_vcard_attribute_get_values (attr);
775 if (p && p->data && ((gchar *) p->data)[0])
776 date = e_contact_date_from_string ((gchar *) p->data);
787 date_setter (EContact *contact,
788 EVCardAttribute *attr,
791 EContactDate *date = data;
794 str = e_contact_date_to_string (date);
796 e_vcard_attribute_add_value (attr, str);
803 cert_getter (EContact *contact,
804 EVCardAttribute *attr)
807 /* the certificate is stored in this vcard. just
809 GList *values = e_vcard_attribute_get_values_decoded (attr);
811 if (values && values->data) {
812 GString *s = values->data;
813 EContactCert *cert = g_new0 (EContactCert, 1);
815 cert->length = s->len;
816 cert->data = g_malloc (cert->length);
817 memcpy (cert->data, s->str, cert->length);
823 /* XXX if we stored a fingerprint in the cert we could look it
824 * up via NSS, but that would require the additional NSS dep
825 * here, and we'd have more than one process opening the
826 * certdb, which is bad. *sigh * */
832 cert_setter (EContact *contact,
833 EVCardAttribute *attr,
836 EContactCert *cert = data;
838 e_vcard_attribute_add_param_with_value (
840 e_vcard_attribute_param_new (EVC_ENCODING),
843 e_vcard_attribute_add_value_decoded (attr, cert->data, cert->length);
848 /* Set_arg handler for the contact */
850 e_contact_set_property (GObject *object,
855 EContact *contact = E_CONTACT (object);
856 const EContactFieldInfo *info = NULL;
858 if (prop_id < 1 || prop_id >= E_CONTACT_FIELD_LAST) {
859 G_OBJECT_WARN_INVALID_PROPERTY_ID (object, prop_id, pspec);
863 info = &field_info[prop_id];
865 if (info->t & E_CONTACT_FIELD_TYPE_MULTI) {
866 GList *new_values = g_value_get_boxed (value);
869 /* first we remove all attributes of the type we're
870 * adding, then add new ones based on the values that
872 e_vcard_remove_attributes (E_VCARD (contact), NULL, info->vcard_field_name);
874 for (l = new_values; l; l = l->next)
875 e_vcard_append_attribute_with_value (
877 e_vcard_attribute_new (NULL, info->vcard_field_name),
880 else if (info->t & E_CONTACT_FIELD_TYPE_SYNTHETIC) {
881 if (info->t & E_CONTACT_FIELD_TYPE_MULTI_ELEM) {
882 /* XXX this is kinda broken - we don't insert
883 * insert padding elements if, e.g. the user
884 * sets email 3 when email 1 and 2 don't
885 * exist. But, if we *did* pad the lists we'd
886 * end up with empty items in the vcard. I
887 * dunno which is worse. */
888 EVCardAttribute *attr = NULL;
889 gboolean found = FALSE;
890 gint num_left = info->list_elem;
891 GList *attrs = e_vcard_get_attributes (E_VCARD (contact));
895 for (l = attrs; l; l = l->next) {
899 name = e_vcard_attribute_get_name (attr);
901 if (!g_ascii_strcasecmp (name, info->vcard_field_name)) {
902 if (num_left-- == 0) {
909 sval = g_value_get_string (value);
912 /* we found it, overwrite it */
913 e_vcard_attribute_remove_values (attr);
916 /* we didn't find it - add a new attribute */
917 attr = e_vcard_attribute_new (NULL, info->vcard_field_name);
918 if (!g_ascii_strcasecmp (info->vcard_field_name, "EMAIL") &&
921 /* Add default type */
922 e_vcard_attribute_add_param_with_value (
924 e_vcard_attribute_param_new (EVC_TYPE),
927 e_vcard_append_attribute (E_VCARD (contact), attr);
930 e_vcard_attribute_add_value (attr, sval);
934 e_vcard_remove_attribute (E_VCARD (contact), attr);
937 else if (info->t & E_CONTACT_FIELD_TYPE_ATTR_TYPE) {
938 /* XXX this is kinda broken - we don't insert
939 * insert padding elements if, e.g. the user
940 * sets email 3 when email 1 and 2 don't
941 * exist. But, if we *did* pad the lists we'd
942 * end up with empty items in the vcard. I
943 * dunno which is worse. */
944 EVCardAttribute *attr = NULL;
945 gboolean found = FALSE;
946 gint num_left = info->list_elem;
947 GList *attrs = e_vcard_get_attributes (E_VCARD (contact));
950 for (l = attrs; l && !found; l = l->next) {
952 gboolean found_needed1, found_needed2;
954 found_needed1 = (info->attr_type1 == NULL);
955 found_needed2 = (info->attr_type2 == NULL);
958 name = e_vcard_attribute_get_name (attr);
960 if (!g_ascii_strcasecmp (name, info->vcard_field_name)) {
963 for (params = e_vcard_attribute_get_params (attr); params; params = params->next) {
964 EVCardAttributeParam *param = params->data;
965 const gchar *param_name = e_vcard_attribute_param_get_name (param);
967 if (!g_ascii_strcasecmp (param_name, EVC_TYPE)) {
968 gboolean matches = FALSE;
969 GList *values = e_vcard_attribute_param_get_values (param);
971 while (values && values->data) {
972 if (!found_needed1 && !g_ascii_strcasecmp ((gchar *) values->data, info->attr_type1)) {
973 found_needed1 = TRUE;
976 else if (!found_needed2 && !g_ascii_strcasecmp ((gchar *) values->data, info->attr_type2)) {
977 found_needed2 = TRUE;
979 } else if (found_needed1) {
980 if (!matches || !found_needed2)
985 values = values->next;
989 /* this is to enforce that we find an attribute
990 * with *only* the TYPE='s we need. This may seem like
991 * an odd restriction but it's the only way at present to
992 * implement the Other Fax and Other Phone attributes. */
993 found_needed1 = FALSE;
998 if (found_needed1 && found_needed2) {
999 if (num_left-- == 0) {
1009 /* we found it, overwrite it */
1010 e_vcard_attribute_remove_values (attr);
1013 /* we didn't find it - add a new attribute */
1014 attr = e_vcard_attribute_new (NULL, info->vcard_field_name);
1015 e_vcard_append_attribute (E_VCARD (contact), attr);
1016 if (info->attr_type1)
1017 e_vcard_attribute_add_param_with_value (
1018 attr, e_vcard_attribute_param_new (EVC_TYPE),
1020 if (info->attr_type2)
1021 e_vcard_attribute_add_param_with_value (
1022 attr, e_vcard_attribute_param_new (EVC_TYPE),
1026 if (info->t & E_CONTACT_FIELD_TYPE_STRUCT || info->t & E_CONTACT_FIELD_TYPE_GETSET) {
1027 gpointer data = info->t & E_CONTACT_FIELD_TYPE_STRUCT ? g_value_get_boxed (value) : (gchar *) g_value_get_string (value);
1029 if ((info->t & E_CONTACT_FIELD_TYPE_STRUCT && data)
1030 || (data && *(gchar *) data))
1031 info->struct_setter (contact, attr, data);
1033 e_vcard_remove_attribute (E_VCARD (contact), attr);
1036 const gchar *sval = g_value_get_string (value);
1039 e_vcard_attribute_add_value (attr, sval);
1041 e_vcard_remove_attribute (E_VCARD (contact), attr);
1044 else if (info->t & E_CONTACT_FIELD_TYPE_LIST_ELEM) {
1045 EVCardAttribute *attr = e_vcard_get_attribute (E_VCARD (contact), info->vcard_field_name);
1048 const gchar *sval = g_value_get_string (value);
1051 if (!sval || !*sval)
1054 d (printf ("adding new %s\n", info->vcard_field_name));
1056 attr = e_vcard_attribute_new (NULL, info->vcard_field_name);
1057 e_vcard_append_attribute (E_VCARD (contact), attr);
1060 values = e_vcard_attribute_get_values (attr);
1061 p = g_list_nth (values, info->list_elem);
1065 p->data = g_strdup (g_value_get_string (value));
1068 /* there weren't enough elements in the list, pad it */
1069 gint count = info->list_elem - g_list_length (values);
1072 e_vcard_attribute_add_value (attr, "");
1074 e_vcard_attribute_add_value (attr, g_value_get_string (value));
1078 switch (info->field_id) {
1079 case E_CONTACT_CATEGORIES: {
1080 EVCardAttribute *attr = e_vcard_get_attribute (E_VCARD (contact), EVC_CATEGORIES);
1085 e_vcard_attribute_remove_values (attr);
1087 /* we didn't find it - add a new attribute */
1088 attr = e_vcard_attribute_new (NULL, EVC_CATEGORIES);
1089 e_vcard_append_attribute (E_VCARD (contact), attr);
1092 str = g_value_get_string (value);
1094 split = g_strsplit (str, ",", 0);
1096 for (s = split; *s; s++) {
1097 e_vcard_attribute_add_value (attr, g_strstrip (*s));
1101 e_vcard_attribute_add_value (attr, str);
1104 d (printf ("removing %s\n", info->vcard_field_name));
1106 e_vcard_remove_attribute (E_VCARD (contact), attr);
1111 g_warning ("unhandled synthetic field 0x%02x", info->field_id);
1116 else if (info->t & E_CONTACT_FIELD_TYPE_STRUCT || info->t & E_CONTACT_FIELD_TYPE_GETSET) {
1117 EVCardAttribute *attr = e_vcard_get_attribute (E_VCARD (contact), info->vcard_field_name);
1118 gpointer data = info->t & E_CONTACT_FIELD_TYPE_STRUCT ? g_value_get_boxed (value) : (gchar *) g_value_get_string (value);
1121 if ((info->t & E_CONTACT_FIELD_TYPE_STRUCT && data)
1122 || (data && *(gchar *) data)) {
1123 d (printf ("overwriting existing %s\n", info->vcard_field_name));
1124 /* remove all existing values and parameters.
1125 * the setter will add the correct ones */
1126 e_vcard_attribute_remove_values (attr);
1127 e_vcard_attribute_remove_params (attr);
1129 info->struct_setter (contact, attr, data);
1132 d (printf ("removing %s\n", info->vcard_field_name));
1134 e_vcard_remove_attribute (E_VCARD (contact), attr);
1137 else if ((info->t & E_CONTACT_FIELD_TYPE_STRUCT && data)
1138 || (data && *(gchar *) data)) {
1139 d (printf ("adding new %s\n", info->vcard_field_name));
1140 attr = e_vcard_attribute_new (NULL, info->vcard_field_name);
1142 e_vcard_append_attribute (E_VCARD (contact), attr);
1144 info->struct_setter (contact, attr, data);
1147 else if (info->t & E_CONTACT_FIELD_TYPE_BOOLEAN) {
1148 EVCardAttribute *attr;
1150 /* first we search for an attribute we can overwrite */
1151 attr = e_vcard_get_attribute (E_VCARD (contact), info->vcard_field_name);
1153 d (printf ("setting %s to `%s'\n", info->vcard_field_name, g_value_get_string (value)));
1154 e_vcard_attribute_remove_values (attr);
1155 e_vcard_attribute_add_value (attr, g_value_get_boolean (value) ? "TRUE" : "FALSE");
1158 /* and if we don't find one we create a new attribute */
1159 e_vcard_append_attribute_with_value (
1161 e_vcard_attribute_new (NULL, info->vcard_field_name),
1162 g_value_get_boolean (value) ? "TRUE" : "FALSE");
1165 else if (info->t & E_CONTACT_FIELD_TYPE_STRING) {
1166 EVCardAttribute *attr;
1167 const gchar *sval = g_value_get_string (value);
1169 /* first we search for an attribute we can overwrite */
1170 if (sval == NULL || g_ascii_strcasecmp (info->vcard_field_name, EVC_UID) != 0) {
1171 attr = e_vcard_get_attribute (E_VCARD (contact), info->vcard_field_name);
1173 /* Avoid useless vcard parsing when trying to set a new non-empty UID.
1174 * Parsing the vcard is pointless in this particular case because even
1175 * if there is a UID in the unparsed vcard, it is going to be ignored
1176 * upon parsing if we already have a UID for the vcard */
1177 attr = e_vcard_get_attribute_if_parsed (E_VCARD (contact), EVC_UID);
1181 d (printf ("setting %s to `%s'\n", info->vcard_field_name, sval));
1182 e_vcard_attribute_remove_values (attr);
1184 e_vcard_attribute_add_value (attr, sval);
1187 d (printf ("removing %s\n", info->vcard_field_name));
1189 e_vcard_remove_attribute (E_VCARD (contact), attr);
1194 /* and if we don't find one we create a new attribute */
1195 e_vcard_append_attribute_with_value (
1197 e_vcard_attribute_new (NULL, info->vcard_field_name),
1201 else if (info->t & E_CONTACT_FIELD_TYPE_LIST) {
1202 EVCardAttribute *attr;
1205 values = g_value_get_pointer (value);
1207 attr = e_vcard_get_attribute (E_VCARD (contact), info->vcard_field_name);
1210 e_vcard_attribute_remove_values (attr);
1213 e_vcard_remove_attribute (E_VCARD (contact), attr);
1216 attr = e_vcard_attribute_new (NULL, info->vcard_field_name);
1217 e_vcard_append_attribute (E_VCARD (contact), attr);
1220 for (l = values; l != NULL; l = l->next)
1221 e_vcard_attribute_add_value (attr, l->data);
1224 g_warning ("unhandled attribute `%s'", info->vcard_field_name);
1228 static EVCardAttribute *
1229 e_contact_find_attribute_with_types (EContact *contact,
1230 const gchar *attr_name,
1231 const gchar *type_needed1,
1232 const gchar *type_needed2,
1236 gboolean found_needed1, found_needed2;
1238 attrs = e_vcard_get_attributes (E_VCARD (contact));
1240 for (l = attrs; l; l = l->next) {
1241 EVCardAttribute *attr = l->data;
1244 found_needed1 = (type_needed1 == NULL);
1245 found_needed2 = (type_needed2 == NULL);
1247 name = e_vcard_attribute_get_name (attr);
1249 if (!g_ascii_strcasecmp (name, attr_name)) {
1252 for (params = e_vcard_attribute_get_params (attr); params; params = params->next) {
1253 EVCardAttributeParam *param = params->data;
1254 const gchar *param_name = e_vcard_attribute_param_get_name (param);
1256 if (!g_ascii_strcasecmp (param_name, EVC_TYPE)) {
1257 gboolean matches = FALSE;
1258 GList *values = e_vcard_attribute_param_get_values (param);
1260 while (values && values->data) {
1261 if (!found_needed1 && !g_ascii_strcasecmp ((gchar *) values->data, type_needed1)) {
1262 found_needed1 = TRUE;
1265 else if (!found_needed2 && !g_ascii_strcasecmp ((gchar *) values->data, type_needed2)) {
1266 found_needed2 = TRUE;
1268 } else if (found_needed1) {
1269 if (!matches || !found_needed2)
1273 values = values->next;
1277 /* this is to enforce that we find an attribute
1278 * with *only* the TYPE='s we need. This may seem like
1279 * an odd restriction but it's the only way at present to
1280 * implement the Other Fax and Other Phone attributes. */
1281 found_needed1 = FALSE;
1286 if (found_needed1 && found_needed2) {
1300 e_contact_get_property (GObject *object,
1305 const EContactFieldInfo *info = NULL;
1308 if (prop_id < 1 || prop_id >= E_CONTACT_FIELD_LAST) {
1309 G_OBJECT_WARN_INVALID_PROPERTY_ID (object, prop_id, pspec);
1310 g_value_reset (value);
1314 info = &field_info[prop_id];
1315 data = e_contact_get (E_CONTACT (object), prop_id);
1317 if (info->t & E_CONTACT_FIELD_TYPE_MULTI) {
1318 g_value_take_boxed (value, data);
1319 } else if (info->t & E_CONTACT_FIELD_TYPE_BOOLEAN) {
1320 g_value_set_boolean (value, data != NULL);
1321 } else if (info->t & E_CONTACT_FIELD_TYPE_LIST) {
1322 g_value_set_pointer (value, data);
1323 } else if (info->t & E_CONTACT_FIELD_TYPE_STRUCT) {
1324 g_value_take_boxed (value, data);
1325 } else if (info->t & E_CONTACT_FIELD_TYPE_GETSET) {
1326 if (info->t & E_CONTACT_FIELD_TYPE_STRUCT) {
1327 g_value_set_boxed (value, data);
1329 g_value_set_string (value, data);
1331 } else if (info->t & E_CONTACT_FIELD_TYPE_STRING) {
1332 g_value_set_string (value, data);
1334 g_value_set_pointer (value, data);
1343 * Creates a new, blank #EContact.
1345 * Returns: A new #EContact.
1348 e_contact_new (void)
1350 return e_contact_new_from_vcard ("");
1354 * e_contact_new_from_vcard:
1355 * @vcard: a string representing a vcard
1357 * Creates a new #EContact based on a vcard.
1359 * Returns: A new #EContact.
1362 e_contact_new_from_vcard (const gchar *vcard)
1365 g_return_val_if_fail (vcard != NULL, NULL);
1367 contact = g_object_new (E_TYPE_CONTACT, NULL);
1368 e_vcard_construct (E_VCARD (contact), vcard);
1374 * e_contact_new_from_vcard_with_uid:
1375 * @vcard: a string representing a vcard
1376 * @uid: a contact UID
1378 * Creates a new #EContact based on a vcard and a predefined UID.
1380 * Returns: A new #EContact.
1385 e_contact_new_from_vcard_with_uid (const gchar *vcard,
1389 g_return_val_if_fail (vcard != NULL, NULL);
1391 contact = g_object_new (E_TYPE_CONTACT, NULL);
1392 e_vcard_construct_with_uid (E_VCARD (contact), vcard, uid);
1398 * e_contact_duplicate:
1399 * @contact: an #EContact
1401 * Creates a copy of @contact.
1403 * Returns: (transfer full): A new #EContact identical to @contact.
1406 e_contact_duplicate (EContact *contact)
1411 g_return_val_if_fail (E_IS_CONTACT (contact), NULL);
1413 vcard = e_vcard_to_string (E_VCARD (contact), EVC_FORMAT_VCARD_30);
1414 c = e_contact_new_from_vcard (vcard);
1421 * e_contact_field_type:
1422 * @field_id: an #EContactField
1424 * Gets the #GType used for this contact field, this indicates
1425 * what kind of value can be passed to e_contact_set().
1427 * Returns: The #GType used for @field_id, or %G_TYPE_INVALID if it doesn't exist.
1432 e_contact_field_type (EContactField field_id)
1436 const gchar *field_name;
1437 GType type = G_TYPE_INVALID;
1439 g_return_val_if_fail (field_id >= 1 && field_id < E_CONTACT_FIELD_LAST, G_TYPE_INVALID);
1441 field_name = e_contact_field_name (field_id);
1442 class = g_type_class_ref (E_TYPE_CONTACT);
1443 pspec = g_object_class_find_property (G_OBJECT_CLASS (class), field_name);
1446 type = G_PARAM_SPEC_VALUE_TYPE (pspec);
1448 g_type_class_unref (class);
1454 * e_contact_field_name:
1455 * @field_id: an #EContactField
1457 * Gets the string representation of @field_id.
1459 * Returns: The string representation of @field_id, or %NULL if it doesn't exist.
1462 e_contact_field_name (EContactField field_id)
1464 g_return_val_if_fail (field_id >= 1 && field_id < E_CONTACT_FIELD_LAST, "");
1466 return field_info[field_id].field_name;
1470 * e_contact_pretty_name:
1471 * @field_id: an #EContactField
1473 * Gets a human-readable, translated string representation
1476 * Returns: The human-readable representation of @field_id, or %NULL if it doesn't exist.
1479 e_contact_pretty_name (EContactField field_id)
1481 g_return_val_if_fail (field_id >= 1 && field_id < E_CONTACT_FIELD_LAST, "");
1484 bindtextdomain (GETTEXT_PACKAGE, LOCALEDIR);
1485 bind_textdomain_codeset (GETTEXT_PACKAGE, "UTF-8");
1488 return _(field_info[field_id].pretty_name);
1492 * e_contact_vcard_attribute:
1493 * @field_id: an #EContactField
1495 * Gets the vcard attribute corresponding to @field_id, as a string.
1497 * Returns: The vcard attribute corresponding to @field_id, or %NULL if it doesn't exist.
1500 e_contact_vcard_attribute (EContactField field_id)
1502 g_return_val_if_fail (field_id >= 1 && field_id < E_CONTACT_FIELD_LAST, "");
1504 return field_info[field_id].vcard_field_name;
1508 * e_contact_field_id:
1509 * @field_name: a string representing a contact field
1511 * Gets the #EContactField corresponding to the @field_name.
1513 * Returns: An #EContactField corresponding to @field_name, or %0 if it doesn't exist.
1516 e_contact_field_id (const gchar *field_name)
1520 for (ii = E_CONTACT_FIELD_FIRST; ii < E_CONTACT_FIELD_LAST; ii++) {
1521 if (!g_ascii_strcasecmp (field_info[ii].field_name, field_name))
1522 return field_info[ii].field_id;
1529 * e_contact_field_id_from_vcard:
1530 * @vcard_field: a string representing a vCard field
1532 * Gets the #EContactField corresponding to the @vcard_field.
1534 * Returns: An #EContactField corresponding to @vcard_field, or %0 if it doesn't exist.
1539 e_contact_field_id_from_vcard (const gchar *vcard_field)
1543 for (ii = E_CONTACT_FIELD_FIRST; ii < E_CONTACT_FIELD_LAST; ii++) {
1544 if (field_info[ii].vcard_field_name == NULL)
1546 if (field_info[ii].t & E_CONTACT_FIELD_TYPE_SYNTHETIC)
1548 if (!strcmp (field_info[ii].vcard_field_name, vcard_field))
1549 return field_info[ii].field_id;
1552 g_warning ("unknown vCard field `%s'", vcard_field);
1558 * @contact: an #EContact
1559 * @field_id: an #EContactField
1561 * Gets the value of @contact's field specified by @field_id.
1563 * Returns: (transfer full) (allow-none): Depends on the field's type, owned by the caller. This may be %NULL if the field isn't set.
1566 e_contact_get (EContact *contact,
1567 EContactField field_id)
1569 const EContactFieldInfo *info = NULL;
1571 g_return_val_if_fail (contact && E_IS_CONTACT (contact), NULL);
1572 g_return_val_if_fail (field_id >= 1 && field_id < E_CONTACT_FIELD_LAST, NULL);
1574 info = &field_info[field_id];
1576 if (info->t & E_CONTACT_FIELD_TYPE_BOOLEAN) {
1577 EVCardAttribute *attr = e_vcard_get_attribute (E_VCARD (contact), info->vcard_field_name);
1578 gboolean rv = FALSE;
1581 GList *v = e_vcard_attribute_get_values (attr);
1582 rv = v && v->data && !g_ascii_strcasecmp ((gchar *) v->data, "true");
1583 return rv ? (gpointer) "1" : NULL;
1586 else if (info->t & E_CONTACT_FIELD_TYPE_LIST) {
1587 EVCardAttribute *attr = e_vcard_get_attribute (E_VCARD (contact), info->vcard_field_name);
1590 GList *list = g_list_copy (e_vcard_attribute_get_values (attr));
1592 for (l = list; l; l = l->next)
1593 l->data = l->data ? g_strstrip (g_strdup (l->data)) : NULL;
1597 else if (info->t & E_CONTACT_FIELD_TYPE_LIST_ELEM) {
1598 if (info->t & E_CONTACT_FIELD_TYPE_STRING) {
1599 EVCardAttribute *attr = e_vcard_get_attribute (E_VCARD (contact), info->vcard_field_name);
1604 v = e_vcard_attribute_get_values (attr);
1605 v = g_list_nth (v, info->list_elem);
1607 return (v && v->data) ? g_strstrip (g_strdup (v->data)) : NULL;
1611 else if (info->t & E_CONTACT_FIELD_TYPE_MULTI_ELEM) {
1612 if (info->t & E_CONTACT_FIELD_TYPE_STRING) {
1614 gint num_left = info->list_elem;
1616 attrs = e_vcard_get_attributes (E_VCARD (contact));
1618 for (l = attrs; l; l = l->next) {
1619 EVCardAttribute *attr = l->data;
1622 name = e_vcard_attribute_get_name (attr);
1624 if (!g_ascii_strcasecmp (name, info->vcard_field_name)) {
1625 if (num_left-- == 0) {
1626 GList *v = e_vcard_attribute_get_values (attr);
1628 return (v && v->data) ? g_strstrip (g_strdup (v->data)) : NULL;
1634 else if (info->t & E_CONTACT_FIELD_TYPE_ATTR_TYPE) {
1635 EVCardAttribute *attr = e_contact_find_attribute_with_types (contact, info->vcard_field_name, info->attr_type1, info->attr_type2, info->list_elem);
1637 if (info->t & E_CONTACT_FIELD_TYPE_STRING) {
1639 GList *p = e_vcard_attribute_get_values (attr);
1640 return (p && p->data) ? g_strstrip (g_strdup (p->data)) : NULL;
1647 return info->struct_getter (contact, attr);
1651 else if (info->t & E_CONTACT_FIELD_TYPE_STRUCT) {
1652 EVCardAttribute *attr = e_vcard_get_attribute (E_VCARD (contact), info->vcard_field_name);
1654 return info->struct_getter (contact, attr);
1656 else if (info->t & E_CONTACT_FIELD_TYPE_GETSET) {
1657 EVCardAttribute *attr = e_vcard_get_attribute (E_VCARD (contact), info->vcard_field_name);
1660 rv = info->struct_getter (contact, attr);
1662 if (info->t & E_CONTACT_FIELD_TYPE_STRUCT)
1663 return (gpointer) info->boxed_type_getter ();
1667 return g_strstrip (g_strdup (rv));
1669 else if (info->t & E_CONTACT_FIELD_TYPE_SYNTHETIC) {
1670 switch (info->field_id) {
1671 case E_CONTACT_NAME_OR_ORG: {
1674 str = e_contact_get_const (contact, E_CONTACT_FILE_AS);
1676 str = e_contact_get_const (contact, E_CONTACT_FULL_NAME);
1678 str = e_contact_get_const (contact, E_CONTACT_ORG);
1680 gboolean is_list = GPOINTER_TO_INT (e_contact_get (contact, E_CONTACT_IS_LIST));
1683 str = _("Unnamed List");
1685 str = e_contact_get_const (contact, E_CONTACT_EMAIL_1);
1688 return str ? g_strstrip (g_strdup (str)) : NULL;
1690 case E_CONTACT_CATEGORIES: {
1691 EVCardAttribute *attr = e_vcard_get_attribute (E_VCARD (contact), EVC_CATEGORIES);
1695 GString *str = g_string_new ("");
1696 GList *v = e_vcard_attribute_get_values (attr);
1698 g_string_append (str, (gchar *) v->data);
1701 g_string_append_c (str, ',');
1704 rv = g_string_free (str, FALSE);
1709 g_warning ("unhandled synthetic field 0x%02x", info->field_id);
1713 else if (info->t & E_CONTACT_FIELD_TYPE_STRING) {
1714 EVCardAttribute *attr;
1715 const gchar *cv = NULL;
1718 /* Do our best to avoid vcard parsing by not calling
1719 * e_vcard_get_attributes */
1721 cv = contact->priv->cached_strings[field_id];
1723 return g_strdup (cv);
1725 attr = e_vcard_get_attribute (E_VCARD (contact), info->vcard_field_name);
1728 v = e_vcard_attribute_get_values (attr);
1730 return ((v && v->data) ? g_strstrip (g_strdup (v->data)) : NULL);
1734 GList *rv = NULL; /* used for multi attribute lists */
1736 attrs = e_vcard_get_attributes (E_VCARD (contact));
1738 for (l = attrs; l; l = l->next) {
1739 EVCardAttribute *attr = l->data;
1742 name = e_vcard_attribute_get_name (attr);
1744 if (!g_ascii_strcasecmp (name, info->vcard_field_name)) {
1746 v = e_vcard_attribute_get_values (attr);
1748 rv = g_list_append (rv, (v && v->data) ? g_strstrip (g_strdup (v->data)) : NULL);
1757 * e_contact_get_const:
1758 * @contact: an #EContact
1759 * @field_id: an #EContactField
1761 * Gets the value of @contact's field specified by @field_id, caching
1762 * the result so it can be freed later.
1764 * Returns: (transfer none): Depends on the field's type, owned by the
1768 e_contact_get_const (EContact *contact,
1769 EContactField field_id)
1771 gpointer value = NULL;
1773 g_return_val_if_fail (E_IS_CONTACT (contact), NULL);
1774 g_return_val_if_fail (field_id >= 1 && field_id < E_CONTACT_FIELD_LAST, NULL);
1775 g_return_val_if_fail (field_info[field_id].t & E_CONTACT_FIELD_TYPE_STRING, NULL);
1777 value = contact->priv->cached_strings[field_id];
1780 value = e_contact_get (contact, field_id);
1782 contact->priv->cached_strings[field_id] = value;
1790 * @contact: an #EContact
1791 * @field_id: an #EContactField
1792 * @value: a value whose type depends on the @field_id
1794 * Sets the value of @contact's field specified by @field_id to @value.
1797 e_contact_set (EContact *contact,
1798 EContactField field_id,
1799 gconstpointer value)
1801 d (printf ("e_contact_set (%p, %d, %p)\n", contact, field_id, value));
1803 g_return_if_fail (contact && E_IS_CONTACT (contact));
1804 g_return_if_fail (field_id >= 1 && field_id < E_CONTACT_FIELD_LAST);
1806 /* set the cached slot to NULL so we'll re-get the new string
1807 * if e_contact_get_const is called again */
1808 g_free (contact->priv->cached_strings[field_id]);
1809 contact->priv->cached_strings[field_id] = NULL;
1813 e_contact_field_name (field_id), value,
1818 * e_contact_get_attributes:
1819 * @contact: an #EContact
1820 * @field_id: an #EContactField
1822 * Gets a list of the vcard attributes for @contact's @field_id.
1824 * Returns: (transfer full) (element-type EVCardAttribute): A #GList of pointers
1825 * to #EVCardAttribute, owned by the caller.
1828 e_contact_get_attributes (EContact *contact,
1829 EContactField field_id)
1833 const EContactFieldInfo *info = NULL;
1835 g_return_val_if_fail (contact && E_IS_CONTACT (contact), NULL);
1836 g_return_val_if_fail (field_id >= 1 && field_id < E_CONTACT_FIELD_LAST, NULL);
1838 info = &field_info[field_id];
1840 attrs = e_vcard_get_attributes (E_VCARD (contact));
1842 for (a = attrs; a; a = a->next) {
1843 EVCardAttribute *attr = a->data;
1846 name = e_vcard_attribute_get_name (attr);
1848 if (!g_ascii_strcasecmp (name, info->vcard_field_name)) {
1849 l = g_list_prepend (l, e_vcard_attribute_copy (attr));
1853 return g_list_reverse (l);
1857 * e_contact_set_attributes:
1858 * @contact: an #EContact
1859 * @field_id: an #EContactField
1860 * @attributes: (element-type EVCardAttribute): a #GList of pointers to #EVCardAttribute
1862 * Sets the vcard attributes for @contact's @field_id.
1863 * Attributes are added to the contact in the same order as they are in @attributes.
1866 e_contact_set_attributes (EContact *contact,
1867 EContactField field_id,
1870 const EContactFieldInfo *info = NULL;
1873 g_return_if_fail (contact && E_IS_CONTACT (contact));
1874 g_return_if_fail (field_id >= 1 && field_id < E_CONTACT_FIELD_LAST);
1876 info = &field_info[field_id];
1878 e_vcard_remove_attributes (E_VCARD (contact), NULL, info->vcard_field_name);
1880 for (l = attributes; l; l = l->next)
1881 e_vcard_append_attribute (
1883 e_vcard_attribute_copy ((EVCardAttribute *) l->data));
1887 * e_contact_name_new:
1889 * Creates a new #EContactName struct.
1891 * Returns: A new #EContactName struct.
1894 e_contact_name_new (void)
1896 return g_new0 (EContactName, 1);
1900 * e_contact_name_to_string:
1901 * @name: an #EContactName
1903 * Generates a string representation of @name.
1905 * Returns: The string representation of @name.
1908 e_contact_name_to_string (const EContactName *name)
1910 gchar *strings[6], **stringptr = strings;
1912 g_return_val_if_fail (name != NULL, NULL);
1914 if (name->prefixes && *name->prefixes)
1915 *(stringptr++) = name->prefixes;
1916 if (name->given && *name->given)
1917 *(stringptr++) = name->given;
1918 if (name->additional && *name->additional)
1919 *(stringptr++) = name->additional;
1920 if (name->family && *name->family)
1921 *(stringptr++) = name->family;
1922 if (name->suffixes && *name->suffixes)
1923 *(stringptr++) = name->suffixes;
1925 return g_strjoinv (" ", strings);
1929 * e_contact_name_from_string:
1930 * @name_str: a string representing a contact's full name
1932 * Creates a new #EContactName based on the parsed @name_str.
1934 * Returns: A new #EContactName struct.
1937 e_contact_name_from_string (const gchar *name_str)
1940 ENameWestern *western;
1942 g_return_val_if_fail (name_str != NULL, NULL);
1944 name = e_contact_name_new ();
1945 western = e_name_western_parse (name_str);
1947 name->prefixes = g_strdup (western->prefix);
1948 name->given = g_strdup (western->first );
1949 name->additional = g_strdup (western->middle);
1950 name->family = g_strdup (western->last );
1951 name->suffixes = g_strdup (western->suffix);
1953 e_name_western_free (western);
1959 * e_contact_name_copy:
1960 * @n: an #EContactName
1962 * Creates a copy of @n.
1964 * Returns: A new #EContactName identical to @n.
1967 e_contact_name_copy (EContactName *n)
1971 g_return_val_if_fail (n != NULL, NULL);
1973 name = e_contact_name_new ();
1975 name->prefixes = g_strdup (n->prefixes);
1976 name->given = g_strdup (n->given);
1977 name->additional = g_strdup (n->additional);
1978 name->family = g_strdup (n->family);
1979 name->suffixes = g_strdup (n->suffixes);
1985 * e_contact_name_free:
1986 * @name: an #EContactName
1988 * Frees @name and its contents.
1991 e_contact_name_free (EContactName *name)
1996 g_free (name->family);
1997 g_free (name->given);
1998 g_free (name->additional);
1999 g_free (name->prefixes);
2000 g_free (name->suffixes);
2005 #define E_CONTACT_DEFINE_BOXED_TYPE(_tp,_nm) \
2007 _tp ## _get_type (void) \
2009 static volatile gsize type_id__volatile = 0; \
2011 if (g_once_init_enter (&type_id__volatile)) { \
2014 type_id = g_boxed_type_register_static (_nm, \
2015 (GBoxedCopyFunc) _tp ## _copy, \
2016 (GBoxedFreeFunc) _tp ## _free); \
2018 g_once_init_leave (&type_id__volatile, type_id);\
2021 return type_id__volatile; \
2024 E_CONTACT_DEFINE_BOXED_TYPE (e_contact_name, "EContactName")
2027 * e_contact_date_from_string:
2028 * @str: a date string in the format YYYY-MM-DD or YYYYMMDD
2030 * Creates a new #EContactDate based on @str.
2032 * Returns: A new #EContactDate struct.
2035 e_contact_date_from_string (const gchar *str)
2037 EContactDate * date;
2041 g_return_val_if_fail (str != NULL, NULL);
2043 date = e_contact_date_new ();
2044 /* ignore time part */
2045 if ((t = strchr (str, 'T')) != NULL)
2048 length = strlen (str);
2050 if (length == 10 ) {
2051 date->year = str[0] * 1000 + str[1] * 100 + str[2] * 10 + str[3] - '0' * 1111;
2052 date->month = str[5] * 10 + str[6] - '0' * 11;
2053 date->day = str[8] * 10 + str[9] - '0' * 11;
2054 } else if (length == 8) {
2055 date->year = str[0] * 1000 + str[1] * 100 + str[2] * 10 + str[3] - '0' * 1111;
2056 date->month = str[4] * 10 + str[5] - '0' * 11;
2057 date->day = str[6] * 10 + str[7] - '0' * 11;
2064 * e_contact_date_to_string:
2065 * @dt: an #EContactDate
2067 * Generates a date string in the format YYYY-MM-DD based
2068 * on the values of @dt.
2070 * Returns: A date string, owned by the caller.
2073 e_contact_date_to_string (EContactDate *dt)
2076 return g_strdup_printf (
2078 CLAMP (dt->year, 1000, 9999),
2079 CLAMP (dt->month, 1, 12),
2080 CLAMP (dt->day, 1, 31));
2086 * e_contact_date_equal:
2087 * @dt1: an #EContactDate
2088 * @dt2: an #EContactDate
2090 * Checks if @dt1 and @dt2 are the same date.
2092 * Returns: %TRUE if @dt1 and @dt2 are equal, %FALSE otherwise.
2095 e_contact_date_equal (EContactDate *dt1,
2099 return (dt1->year == dt2->year &&
2100 dt1->month == dt2->month &&
2101 dt1->day == dt2->day);
2103 return (!!dt1 == !!dt2);
2107 * e_contact_date_copy:
2108 * @dt: an #EContactDate
2110 * Creates a copy of @dt.
2112 * Returns: A new #EContactDate struct identical to @dt.
2114 static EContactDate *
2115 e_contact_date_copy (EContactDate *dt)
2117 EContactDate *dt2 = e_contact_date_new ();
2118 dt2->year = dt->year;
2119 dt2->month = dt->month;
2126 * e_contact_date_free:
2127 * @date: an #EContactDate
2129 * Frees the @date struct and its contents.
2132 e_contact_date_free (EContactDate *date)
2137 E_CONTACT_DEFINE_BOXED_TYPE (e_contact_date, "EContactDate")
2140 * e_contact_date_new:
2142 * Creates a new #EContactDate struct.
2144 * Returns: A new #EContactDate struct.
2147 e_contact_date_new (void)
2149 return g_new0 (EContactDate, 1);
2153 * e_contact_photo_new:
2155 * Creates a new #EContactPhoto struct.
2157 * Returns: (transfer full): A new #EContactPhoto struct.
2162 e_contact_photo_new (void)
2164 return g_new0 (EContactPhoto, 1);
2168 * e_contact_photo_free:
2169 * @photo: an #EContactPhoto struct
2171 * Frees the @photo struct and its contents.
2174 e_contact_photo_free (EContactPhoto *photo)
2179 switch (photo->type) {
2180 case E_CONTACT_PHOTO_TYPE_INLINED:
2181 g_free (photo->data.inlined.mime_type);
2182 g_free (photo->data.inlined.data);
2184 case E_CONTACT_PHOTO_TYPE_URI:
2185 g_free (photo->data.uri);
2188 g_warning ("Unknown EContactPhotoType %d", photo->type);
2196 * e_contact_photo_copy:
2197 * @photo: an #EContactPhoto
2199 * Creates a copy of @photo.
2201 * Returns: A new #EContactPhoto struct identical to @photo.
2203 static EContactPhoto *
2204 e_contact_photo_copy (EContactPhoto *photo)
2206 EContactPhoto *photo2 = g_new0 (EContactPhoto, 1);
2207 switch (photo->type) {
2208 case E_CONTACT_PHOTO_TYPE_INLINED:
2209 photo2->type = E_CONTACT_PHOTO_TYPE_INLINED;
2210 photo2->data.inlined.mime_type = g_strdup (photo->data.inlined.mime_type);
2211 photo2->data.inlined.length = photo->data.inlined.length;
2212 photo2->data.inlined.data = g_malloc (photo2->data.inlined.length);
2213 memcpy (photo2->data.inlined.data, photo->data.inlined.data, photo->data.inlined.length);
2215 case E_CONTACT_PHOTO_TYPE_URI:
2216 photo2->type = E_CONTACT_PHOTO_TYPE_URI;
2217 photo2->data.uri = g_strdup (photo->data.uri);
2220 g_warning ("Unknown EContactPhotoType %d", photo->type);
2227 * e_contact_photo_get_inlined:
2228 * @photo: an #EContactPhoto
2229 * @len: (out caller-allocates) (transfer none): the length of the inlined data
2231 * Gets the @photo's data.
2233 * Returns: (transfer none) (array length=len) (allow-none): the inlined image in the
2234 * #EContactPhoto, or %NULL if it has not been set.
2239 e_contact_photo_get_inlined (EContactPhoto *photo,
2242 g_return_val_if_fail (photo != NULL, NULL);
2243 g_return_val_if_fail (photo->type == E_CONTACT_PHOTO_TYPE_INLINED, NULL);
2245 *len = photo->data.inlined.length;
2246 return photo->data.inlined.data;
2250 * e_contact_photo_set_inlined:
2251 * @photo: an #EContactPhoto
2252 * @data: (transfer none) (array length=len): the inlined image data
2253 * @len: the length of @data
2255 * Sets the @photo's inlined data.
2260 e_contact_photo_set_inlined (EContactPhoto *photo,
2264 g_return_if_fail (photo != NULL);
2265 g_return_if_fail (photo->type == E_CONTACT_PHOTO_TYPE_INLINED);
2267 photo->data.inlined.data = g_malloc (len);
2268 memcpy (photo->data.inlined.data, data, len);
2269 photo->data.inlined.length = len;
2273 * e_contact_photo_get_mime_type:
2274 * @photo: an #EContactPhoto
2276 * Gets the @photo's mime type.
2278 * Returns: (transfer none) (allow-none): the MIME type of the image, or %NULL if it has not been set.
2283 e_contact_photo_get_mime_type (EContactPhoto *photo)
2285 g_return_val_if_fail (photo != NULL, NULL);
2286 g_return_val_if_fail (photo->type == E_CONTACT_PHOTO_TYPE_INLINED, NULL);
2288 return photo->data.inlined.mime_type;
2292 * e_contact_photo_set_mime_type:
2293 * @photo: an #EContactPhoto
2294 * @mime_type: the mime type
2296 * Sets the @photo's mime type.
2301 e_contact_photo_set_mime_type (EContactPhoto *photo,
2302 const gchar *mime_type)
2304 g_return_if_fail (photo != NULL);
2305 g_return_if_fail (photo->type == E_CONTACT_PHOTO_TYPE_INLINED);
2307 g_free (photo->data.inlined.mime_type);
2308 photo->data.inlined.mime_type = g_strdup (mime_type);
2312 * e_contact_photo_get_uri:
2313 * @photo: an #EContactPhoto
2315 * Gets the @photo's URI.
2317 * Returns: (transfer none) (allow-none): the URI of the image, or %NULL if it has not been set
2322 e_contact_photo_get_uri (EContactPhoto *photo)
2324 g_return_val_if_fail (photo != NULL, NULL);
2325 g_return_val_if_fail (photo->type == E_CONTACT_PHOTO_TYPE_URI, NULL);
2327 return photo->data.uri;
2331 * e_contact_photo_set_uri:
2332 * @photo: an #EContactPhoto
2333 * @uri: the @photo's URI
2335 * Sets the @photo's URI.
2340 e_contact_photo_set_uri (EContactPhoto *photo,
2343 g_return_if_fail (photo != NULL);
2344 g_return_if_fail (photo->type == E_CONTACT_PHOTO_TYPE_URI);
2346 g_free (photo->data.uri);
2347 photo->data.uri = g_strdup (uri);
2350 /* Try to unescape a mime type which was encoded into
2351 * the filename, return the mime type if g_content_type_from_mime_type()
2352 * returns something for the decoded filename extension.
2355 mime_type_from_filename (const gchar *filename)
2359 gchar *content_type;
2361 extension = strrchr (filename, '.');
2368 mime_type = g_uri_unescape_string (extension, NULL);
2369 content_type = g_content_type_from_mime_type (mime_type);
2371 if (!content_type) {
2376 g_free (content_type);
2382 e_contact_photo_make_inline (EContactPhoto *photo,
2386 gchar *contents = NULL;
2388 gboolean success = FALSE;
2390 /* Just a sanity check, this wont happen but return TRUE anyway */
2391 if (photo->type != E_CONTACT_PHOTO_TYPE_URI)
2394 filename = g_filename_from_uri (photo->data.uri, NULL, error);
2398 if (g_file_get_contents (filename, &contents, &length, error)) {
2401 mime_type = mime_type_from_filename (filename);
2403 gchar *content_type =
2404 g_content_type_guess (NULL, (const guchar *) contents, length, NULL);
2407 mime_type = g_content_type_get_mime_type (content_type);
2409 g_free (content_type);
2412 g_free (photo->data.uri);
2414 photo->type = E_CONTACT_PHOTO_TYPE_INLINED;
2415 photo->data.inlined.data = (guchar *) contents;
2416 photo->data.inlined.length = length;
2417 photo->data.inlined.mime_type = mime_type;
2427 e_contact_inline_photo_field (EContact *contact,
2428 EContactField field,
2431 EContactPhoto *photo;
2432 gboolean success = TRUE;
2434 photo = e_contact_get (contact, field);
2437 if (photo->type == E_CONTACT_PHOTO_TYPE_URI &&
2438 g_str_has_prefix (photo->data.uri, "file://")) {
2439 success = e_contact_photo_make_inline (photo, error);
2442 e_contact_set (contact, field, photo);
2444 e_contact_photo_free (photo);
2451 * e_contact_inline_local_photos:
2452 * @contact: an #EContact
2453 * @error: the location to store any #GError which might occur
2455 * Tries to modify any #EContactPhoto fields which are
2456 * stored on the local file system as type %E_CONTACT_PHOTO_TYPE_URI
2457 * to be inlined and stored as %E_CONTACT_PHOTO_TYPE_INLINED instead.
2459 * Returns: %TRUE if there were no errors, upon error %FALSE is returned
2460 * and @error is set.
2465 e_contact_inline_local_photos (EContact *contact,
2468 gboolean success = TRUE;
2470 g_return_val_if_fail (E_IS_CONTACT (contact), FALSE);
2472 success = e_contact_inline_photo_field (contact, E_CONTACT_PHOTO, error);
2474 success = e_contact_inline_photo_field (contact, E_CONTACT_LOGO, error);
2479 E_CONTACT_DEFINE_BOXED_TYPE (e_contact_photo, "EContactPhoto")
2482 * e_contact_geo_new:
2484 * Creates an #EContactGeo struct with all coordinates set to 0.
2486 * Returns: (transfer full): A new #EContactGeo struct.
2491 e_contact_geo_new (void)
2494 geo = g_new0 (EContactGeo, 1);
2501 * e_contact_geo_free:
2502 * @geo: an #EContactGeo
2504 * Frees the @geo struct and its contents.
2509 e_contact_geo_free (EContactGeo *geo)
2514 static EContactGeo *
2515 e_contact_geo_copy (EContactGeo *geo)
2517 EContactGeo *geo2 = g_new0 (EContactGeo, 1);
2518 geo2->latitude = geo->latitude;
2519 geo2->longitude = geo->longitude;
2524 E_CONTACT_DEFINE_BOXED_TYPE (e_contact_geo, "EContactGeo")
2527 * e_contact_address_new:
2529 * Creates a new #EContactAddress struct.
2531 * Returns: (transfer full): A new #EContactAddress struct.
2536 e_contact_address_new (void)
2538 return g_new0 (EContactAddress, 1);
2542 * e_contact_address_free:
2543 * @address: an #EContactAddress
2545 * Frees the @address struct and its contents.
2548 e_contact_address_free (EContactAddress *address)
2553 g_free (address->address_format);
2554 g_free (address->po);
2555 g_free (address->ext);
2556 g_free (address->street);
2557 g_free (address->locality);
2558 g_free (address->region);
2559 g_free (address->code);
2560 g_free (address->country);
2565 static EContactAddress *
2566 e_contact_address_copy (EContactAddress *address)
2568 EContactAddress *address2 = g_new0 (EContactAddress, 1);
2570 address2->address_format = g_strdup (address->address_format);
2571 address2->po = g_strdup (address->po);
2572 address2->ext = g_strdup (address->ext);
2573 address2->street = g_strdup (address->street);
2574 address2->locality = g_strdup (address->locality);
2575 address2->region = g_strdup (address->region);
2576 address2->code = g_strdup (address->code);
2577 address2->country = g_strdup (address->country);
2582 E_CONTACT_DEFINE_BOXED_TYPE (e_contact_address, "EContactAddress")
2585 * e_contact_cert_new:
2587 * Creates an #EContactCert struct with all values set to 0.
2589 * Returns: (transfer full): A new #EContactCert struct.
2594 e_contact_cert_new (void)
2597 cert = g_new0 (EContactCert, 1);
2602 * e_contact_cert_free:
2603 * @cert: an #EContactCert
2605 * Frees the @cert struct and its contents.
2608 e_contact_cert_free (EContactCert *cert)
2613 g_free (cert->data);
2617 static EContactCert *
2618 e_contact_cert_copy (EContactCert *cert)
2620 EContactCert *cert2 = g_new0 (EContactCert, 1);
2621 cert2->length = cert->length;
2622 cert2->data = g_malloc (cert2->length);
2623 memcpy (cert2->data, cert->data, cert->length);
2628 E_CONTACT_DEFINE_BOXED_TYPE (e_contact_cert, "EContactCert")
2631 * e_contact_attr_list_copy:
2632 * @list: A #GList of strings
2634 * Copies a list of allocated strings, specifically
2635 * for the #EContactAttrList boxed type used for multi valued
2638 * Returns: (transfer full): A copy of @list
2643 e_contact_attr_list_copy (GList *list)
2645 return g_list_copy_deep (list, (GCopyFunc) g_strdup, NULL);
2649 * e_contact_attr_list_free:
2650 * @list: A #GList of strings
2652 * Frees a list of allocated strings, specifically
2653 * for the #EContactAttrList boxed type used for multi valued
2659 e_contact_attr_list_free (GList *list)
2661 g_list_free_full (list, (GDestroyNotify) g_free);
2664 typedef GList EContactAttrList;
2665 G_DEFINE_BOXED_TYPE (EContactAttrList, e_contact_attr_list, e_contact_attr_list_copy, e_contact_attr_list_free);