4 * Copyright 2019 Michael Rasmussen <mir@datanom.net>
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.
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.
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,
22 #include <vcard-parser.h>
28 static gchar
* Properties
[VCARD_PROPERTIES
+1] = {
83 static void vcard_property_free(gpointer data
) {
85 VCardProperty
* vp
= (VCardProperty
*) data
;
91 static Property
get_property(const gchar
* property
) {
92 /* Property p = VCARD_PROPERTIES;
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");
100 lookup = g_strdup(property);
102 for (int i = 0; i < VCARD_PROPERTIES; i++) {
103 if (g_strcmp0(Properties[i], lookup) == 0) {
109 for (Property i
= N
; i
< VCARD_PROPERTIES
; i
++) {
110 if (g_strcmp0(Properties
[i
], property
) == 0) return i
;
113 return VCARD_PROPERTIES
;
116 static gboolean
check_support(Property property
, VCardVersion version
) {
117 gboolean support
= FALSE
;
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
;
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
;
135 while (*tmp
&& !found
) {
136 if (!g_str_has_prefix(*tmp
, "VERSION")) {
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
;
146 if (*version
== str_2_vcard_version(b
[1])) {
147 result
= VCARD_PARSER_OK
;
149 result
= VCARD_PARSER_VERSION_MISMATCH
;
159 static gchar
** vcard_text_normalize(const gchar
** text
) {
161 const gchar
** tmp
= text
;
165 if (text
== NULL
) return NULL
;
167 new = g_new0(gchar
*, g_strv_length((gchar
**) text
) + 1);
168 while (*tmp
&& g_strcmp0(*tmp
, "") != 0) {
169 if (*tmp
[0] == ' ') {
172 gchar
* s
= g_strdup(str
);
174 str
= g_strconcat(s
, "\n", *tmp
+1, NULL
);
176 str
= g_strdup(*tmp
+1);
179 new[count
++] = g_strdup(str
);
183 str
= g_strdup(*tmp
);
188 new[count
++] = g_strdup(str
);
195 Property
vcard_max_property(VCardVersion version
) {
196 Property p
= VCARD_PROPERTIES
;
198 if (version
== VCARD_VERSION_2_1
) {
200 } else if (version
== VCARD_VERSION_3_0
) {
202 } else if (version
== VCARD_VERSION_4_0
) {
203 p
= VCARD_PROPERTIES
- 1;
209 void destroy_hash_table(GHashTable
* ht
) {
213 g_hash_table_iter_init (&iter
, ht
);
214 while (g_hash_table_iter_next(&iter
, &key
, &value
)) {
216 g_slist_free_full((GSList
*) value
, vcard_property_free
);
219 g_hash_table_destroy(ht
);
222 static void init_g_hash_table(GHashTable
** ht
, VCardVersion version
) {
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
);
235 static VCardParserResponse
vcard_parse(gchar
** text
, VCardVersion version
, GHashTable
** vcard
) {
236 VCardParserResponse r
= VCARD_PARSER_ERROR
;
237 guint l
= g_strv_length(text
);
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
);
245 return VCARD_PARSER_ERROR
;
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) {
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
);
264 destroy_hash_table(*vcard
);
268 return VCARD_PARSER_ATTRIBUTE_VERSION_MISMATCH
;
278 VCardVersion
str_2_vcard_version(const gchar
* version
) {
279 VCardVersion vv
= VCARD_VERSION_DETECT
;
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
;
289 vv
= VCARD_VERSION_UNSUPPORTED
;
295 gchar
* vcard_version_2_str(VCardVersion version
) {
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");
308 VCardParserResponse
vcard_parse_text(const gchar
* text
, VCardVersion
* version
, GHashTable
** vcard
) {
309 VCardParserResponse r
= VCARD_PARSER_ERROR
;
311 if (text
== NULL
|| strlen(text
) < 1) return VCARD_PARSER_OK
;
313 gchar
** b
= g_strsplit(text
, "\n", 0);
314 gchar
** buf
= vcard_text_normalize((const gchar
**)b
);
316 guint l
= g_strv_length(buf
);
317 //begin, end and version line is required
318 r
= VCARD_PARSER_BAD_FORMAT
;
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
);
327 r
= VCARD_PARSER_ERROR
;