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