]> git.datanom.net - vcard-parser.git/blob - src/vcard-parser.c
Complete version 0.1
[vcard-parser.git] / src / vcard-parser.c
1 /*
2 * vcard-parser.c
3 *
4 * Copyright 2019 Michael Rasmussen <mir@datanom.net>
5 *
6 * This program is free software; you can redistribute it and/or modify
7 * it under the terms of the GNU General Public License as published by
8 * the Free Software Foundation; either version 2 of the License, or
9 * (at your option) any later version.
10 *
11 * This program is distributed in the hope that it will be useful,
12 * but WITHOUT ANY WARRANTY; without even the implied warranty of
13 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
14 * GNU General Public License for more details.
15 *
16 * You should have received a copy of the GNU General Public License
17 * along with this program; if not, write to the Free Software
18 * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston,
19 * MA 02110-1301, USA.
20 */
21
22 #include <vcard-parser.h>
23
24 #ifdef HAVE_CONFIG_H
25 # include "config.h"
26 #endif
27
28 static gchar* Properties[VCARD_PROPERTIES+1] = {
29 "N",
30 "FN",
31 "PHOTO",
32 "BDAY",
33 "ADR",
34 "LABEL",
35 "TEL",
36 "EMAIL",
37 "MAILER",
38 "GEO",
39 "TITLE",
40 "ROLE",
41 "LOGO",
42 "ORG",
43 "NOTE",
44 "REV",
45 "SOUND",
46 "URL",
47 "UID",
48 "VERSION",
49 "KEY",
50 "TZ",
51 "SOURCE",
52 "AGENT",
53 "PROFILE",
54 "CATEGORIES",
55 "SORT-STRING",
56 "PRODID",
57 "NICKNAME",
58 "NAME",
59 "CLASS",
60 "FBURL",
61 "CAPURI",
62 "CALURI",
63 "CALADRURI",
64 "IMPP",
65 "XML",
66 "ANNIVERSARY",
67 "CLIENTPIDMAP",
68 "LANG",
69 "GENDER",
70 "KIND",
71 "MEMBER",
72 "RELATED",
73 "BIRTHPLACE",
74 "DEATHPLACE",
75 "DEATHDATE",
76 "EXPERTISE",
77 "HOBBY",
78 "INTEREST",
79 "ORG-DIRECTORY",
80 NULL
81 };
82
83 static void vcard_property_free(gpointer data) {
84 if (!data) return;
85 VCardProperty* vp = (VCardProperty*) data;
86 g_free(vp->name);
87 g_free(vp->value);
88 g_free(vp);
89 }
90
91 static Property get_property(const gchar* property) {
92 /* Property p = VCARD_PROPERTIES;
93 gchar* lookup = NULL;
94
95 if (g_strcmp0("ORG-DIRECTORY", property) == 0)
96 lookup = g_strdup("ORG_DIRECTORY");
97 else if (g_strcmp0("SORT-STRING", property) == 0)
98 lookup = g_strdup("SORT_STRING");
99 else
100 lookup = g_strdup(property);
101
102 for (int i = 0; i < VCARD_PROPERTIES; i++) {
103 if (g_strcmp0(Properties[i], lookup) == 0) {
104 p = i;
105 break;
106 }
107 }
108 g_free(lookup);*/
109 for (Property i = N; i < VCARD_PROPERTIES; i++) {
110 if (g_strcmp0(Properties[i], property) == 0) return i;
111 }
112
113 return VCARD_PROPERTIES;
114 }
115
116 static gboolean check_support(Property property, VCardVersion version) {
117 gboolean support = FALSE;
118
119 if (version == VCARD_VERSION_2_1) {
120 support = property <= TZ ? TRUE : FALSE;
121 } else if (version == VCARD_VERSION_3_0) {
122 support = property <= IMPP ? TRUE : FALSE;
123 } else if (version == VCARD_VERSION_4_0) {
124 support = property <= VCARD_PROPERTIES ? TRUE : FALSE;
125 };
126
127 return support;
128 }
129
130 static VCardParserResponse has_required_version(const gchar** buf, VCardVersion* version) {
131 const gchar** tmp = buf;
132 gboolean found = FALSE;
133 VCardParserResponse result = VCARD_PARSER_VERSION_MISSING;
134
135 while (*tmp && !found) {
136 if (!g_str_has_prefix(*tmp, "VERSION")) {
137 tmp++;
138 } else {
139 found = TRUE;
140 gchar** b = g_strsplit(*tmp, ":", 0);
141 if (g_strv_length(b) == 2) {
142 if (*version == VCARD_VERSION_DETECT) {
143 *version = str_2_vcard_version(b[1]);
144 result = VCARD_PARSER_OK;
145 } else {
146 if (*version == str_2_vcard_version(b[1])) {
147 result = VCARD_PARSER_OK;
148 } else
149 result = VCARD_PARSER_VERSION_MISMATCH;
150 }
151 }
152 g_strfreev(b);
153 }
154 }
155
156 return result;
157 }
158
159 static gchar** vcard_text_normalize(const gchar** text) {
160 gchar** new = NULL;
161 const gchar** tmp = text;
162 int count = 0;
163 gchar* str = NULL;
164
165 if (text == NULL) return NULL;
166
167 new = g_new0(gchar*, g_strv_length((gchar **) text) + 1);
168 while (*tmp && g_strcmp0(*tmp, "") != 0) {
169 if (*tmp[0] == ' ') {
170 // Multi line value
171 if (str) {
172 gchar* s = g_strdup(str);
173 g_free(str);
174 str = g_strconcat(s, "\n", *tmp+1, NULL);
175 } else
176 str = g_strdup(*tmp+1);
177 } else {
178 if (str) {
179 new[count++] = g_strdup(str);
180 g_free(str);
181 str = NULL;
182 }
183 str = g_strdup(*tmp);
184 }
185 tmp++;
186 }
187 if (str) {
188 new[count++] = g_strdup(str);
189 g_free(str);
190 }
191
192 return new;
193 }
194
195 Property vcard_max_property(VCardVersion version) {
196 Property p = VCARD_PROPERTIES;
197
198 if (version == VCARD_VERSION_2_1) {
199 p = TZ;
200 } else if (version == VCARD_VERSION_3_0) {
201 p = IMPP;
202 } else if (version == VCARD_VERSION_4_0) {
203 p = VCARD_PROPERTIES - 1;
204 }
205
206 return p;
207 }
208
209 void destroy_hash_table(GHashTable* ht) {
210 GHashTableIter iter;
211 gpointer key, value;
212
213 g_hash_table_iter_init (&iter, ht);
214 while (g_hash_table_iter_next(&iter, &key, &value)) {
215 if (value)
216 g_slist_free_full((GSList *) value, vcard_property_free);
217 }
218
219 g_hash_table_destroy(ht);
220 }
221
222 static void init_g_hash_table(GHashTable** ht, VCardVersion version) {
223 if (!ht) return;
224
225 Property property = vcard_max_property(version);
226 if (property < VCARD_PROPERTIES) {
227 *ht = g_hash_table_new_full(
228 g_str_hash, g_str_equal, (GDestroyNotify) g_free, NULL);
229 for (Property i = N; i <= property; i++) {
230 g_hash_table_insert(*ht, g_strdup(Properties[i]), NULL);
231 }
232 }
233 }
234
235 static VCardParserResponse vcard_parse(gchar** text, VCardVersion version, GHashTable** vcard) {
236 VCardParserResponse r = VCARD_PARSER_ERROR;
237 guint l = g_strv_length(text);
238
239 for (guint i = 0; i < l; i++) {
240 gchar** b = g_strsplit(text[i], ":", 2);
241 if (g_strv_length(b) != 2 || !b[0] || !b[1]) {
242 destroy_hash_table(*vcard);
243 *vcard = NULL;
244 g_strfreev(b);
245 return VCARD_PARSER_ERROR;
246 }
247 gchar** c = g_strsplit(b[0], ";", 0);
248 if (g_strcmp0(c[0], "BEGIN") == 0 ||
249 g_strcmp0(c[0], "VERSION") == 0 ||
250 g_strcmp0(c[0], "END") == 0) {
251 g_strfreev(c);
252 g_strfreev(b);
253 continue;
254 }
255 Property property = get_property(c[0]);
256 if (check_support(property, version)) {
257 GSList* value = g_hash_table_lookup(*vcard, c[0]);
258 VCardProperty* vp = g_new(VCardProperty, 1);
259 vp->name = g_strdup(b[0]);
260 vp->value = g_strdup(b[1]);
261 value = g_slist_append(value, vp);
262 g_hash_table_insert(*vcard, g_strdup(c[0]), value);
263 } else {
264 destroy_hash_table(*vcard);
265 *vcard = NULL;
266 g_strfreev(c);
267 g_strfreev(b);
268 return VCARD_PARSER_ATTRIBUTE_VERSION_MISMATCH;
269 }
270 g_strfreev(c);
271 g_strfreev(b);
272 }
273 r = VCARD_PARSER_OK;
274
275 return r;
276 }
277
278 VCardVersion str_2_vcard_version(const gchar* version) {
279 VCardVersion vv = VCARD_VERSION_DETECT;
280
281 if (version) {
282 if (g_strcmp0("2.1", version) == 0)
283 vv = VCARD_VERSION_2_1;
284 else if (g_strcmp0("3.0", version) == 0)
285 vv = VCARD_VERSION_3_0;
286 else if (g_strcmp0("4.0", version) == 0)
287 vv = VCARD_VERSION_4_0;
288 else
289 vv = VCARD_VERSION_UNSUPPORTED;
290 }
291
292 return vv;
293 }
294
295 gchar* vcard_version_2_str(VCardVersion version) {
296 gchar* vv = NULL;
297
298 if (VCARD_VERSION_2_1 == version)
299 vv = g_strdup("2.1");
300 else if (VCARD_VERSION_3_0 == version)
301 vv = g_strdup("3.0");
302 else if (VCARD_VERSION_4_0 == version)
303 vv = g_strdup("4.0");
304
305 return vv;
306 }
307
308 VCardParserResponse vcard_parse_text(const gchar* text, VCardVersion* version, GHashTable** vcard) {
309 VCardParserResponse r = VCARD_PARSER_ERROR;
310
311 if (text == NULL || strlen(text) < 1) return VCARD_PARSER_OK;
312
313 gchar** b = g_strsplit(text, "\n", 0);
314 gchar** buf = vcard_text_normalize((const gchar**)b);
315 g_strfreev(b);
316 guint l = g_strv_length(buf);
317 //begin, end and version line is required
318 r = VCARD_PARSER_BAD_FORMAT;
319 if (buf && l > 3) {
320 if (g_strcmp0(buf[0], "BEGIN:VCARD") == 0 && g_strcmp0(buf[l-1], "END:VCARD") == 0) {
321 r = has_required_version((const gchar **)buf, version);
322 if (r == VCARD_PARSER_OK) {
323 init_g_hash_table(vcard, *version);
324 if (vcard && *vcard) {
325 r = vcard_parse(buf, *version, vcard);
326 } else {
327 r = VCARD_PARSER_ERROR;
328 }
329 }
330 }
331 }
332 g_strfreev(buf);
333
334 return r;
335 }
This page took 0.081374 seconds and 6 git commands to generate.