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 for (Property i
= N
; i
< VCARD_PROPERTIES
; i
++) {
93 if (g_strcmp0(Properties
[i
], property
) == 0) return i
;
96 return VCARD_PROPERTIES
;
99 static gboolean
check_support(Property property
, VCardVersion version
) {
100 gboolean support
= FALSE
;
102 if (version
== VCARD_VERSION_2_1
) {
103 support
= property
<= TZ
? TRUE
: FALSE
;
104 } else if (version
== VCARD_VERSION_3_0
) {
105 support
= property
<= IMPP
? TRUE
: FALSE
;
106 } else if (version
== VCARD_VERSION_4_0
) {
107 support
= property
<= VCARD_PROPERTIES
? TRUE
: FALSE
;
113 static VCardParserResponse
has_required_version(const gchar
** buf
, VCardVersion
* version
) {
114 const gchar
** tmp
= buf
;
115 gboolean found
= FALSE
;
116 VCardParserResponse result
= VCARD_PARSER_VERSION_MISSING
;
118 while (*tmp
&& !found
) {
119 if (!g_str_has_prefix(*tmp
, "VERSION")) {
123 gchar
** b
= g_strsplit(*tmp
, ":", 0);
124 if (g_strv_length(b
) == 2) {
125 if (*version
== VCARD_VERSION_DETECT
) {
126 *version
= str_2_vcard_version(b
[1]);
127 result
= VCARD_PARSER_OK
;
129 if (*version
== str_2_vcard_version(b
[1])) {
130 result
= VCARD_PARSER_OK
;
132 result
= VCARD_PARSER_VERSION_MISMATCH
;
142 static gchar
** vcard_text_normalize(const gchar
** text
) {
144 const gchar
** tmp
= text
;
148 if (text
== NULL
) return NULL
;
150 new = g_new0(gchar
*, g_strv_length((gchar
**) text
) + 1);
151 while (*tmp
&& g_strcmp0(*tmp
, "") != 0) {
152 if (*tmp
[0] == ' ') {
155 gchar
* s
= g_strdup(str
);
157 str
= g_strconcat(s
, "\n", *tmp
+1, NULL
);
159 str
= g_strdup(*tmp
+1);
162 new[count
++] = g_strdup(str
);
166 str
= g_strdup(*tmp
);
171 new[count
++] = g_strdup(str
);
178 static VCardParserResponse
vcard_parse(gchar
** text
, VCardVersion version
, GHashTable
** vcard
) {
179 VCardParserResponse r
= VCARD_PARSER_ERROR
;
180 guint l
= g_strv_length(text
);
183 for (guint i
= 0; i
< l
; i
++) {
184 gchar
** b
= g_strsplit(text
[i
], ":", 2);
185 if (g_strv_length(b
) != 2 || !b
[0] || !b
[1]) {
186 destroy_hash_table(*vcard
);
189 return VCARD_PARSER_ERROR
;
191 gchar
** c
= g_strsplit(b
[0], ";", 0);
192 if (g_strcmp0(c
[0], "BEGIN") == 0 ||
193 g_strcmp0(c
[0], "VERSION") == 0 ||
194 g_strcmp0(c
[0], "END") == 0) {
199 if (g_str_has_prefix(c
[0], "X-")) {
202 Property property
= get_property(c
[0]);
203 support
= check_support(property
, version
);
206 GSList
* value
= g_hash_table_lookup(*vcard
, c
[0]);
207 VCardProperty
* vp
= g_new(VCardProperty
, 1);
208 vp
->name
= g_strdup(b
[0]);
209 vp
->value
= g_strdup(b
[1]);
210 value
= g_slist_append(value
, vp
);
211 g_hash_table_insert(*vcard
, g_strdup(c
[0]), value
);
213 destroy_hash_table(*vcard
);
217 return VCARD_PARSER_ATTRIBUTE_VERSION_MISMATCH
;
227 Property
vcard_max_property(VCardVersion version
) {
228 Property p
= VCARD_PROPERTIES
;
230 if (version
== VCARD_VERSION_2_1
) {
232 } else if (version
== VCARD_VERSION_3_0
) {
234 } else if (version
== VCARD_VERSION_4_0
) {
235 p
= VCARD_PROPERTIES
- 1;
241 void destroy_hash_table(GHashTable
* ht
) {
245 g_hash_table_iter_init (&iter
, ht
);
246 while (g_hash_table_iter_next(&iter
, &key
, &value
)) {
248 g_slist_free_full((GSList
*) value
, vcard_property_free
);
251 g_hash_table_destroy(ht
);
254 void init_g_hash_table(GHashTable
** ht
, VCardVersion version
) {
257 Property property
= vcard_max_property(version
);
258 if (property
< VCARD_PROPERTIES
) {
259 *ht
= g_hash_table_new_full(
260 g_str_hash
, g_str_equal
, (GDestroyNotify
) g_free
, NULL
);
261 for (Property i
= N
; i
<= property
; i
++) {
262 g_hash_table_insert(*ht
, g_strdup(Properties
[i
]), NULL
);
267 VCardVersion
str_2_vcard_version(const gchar
* version
) {
268 VCardVersion vv
= VCARD_VERSION_DETECT
;
271 if (g_strcmp0("2.1", version
) == 0)
272 vv
= VCARD_VERSION_2_1
;
273 else if (g_strcmp0("3.0", version
) == 0)
274 vv
= VCARD_VERSION_3_0
;
275 else if (g_strcmp0("4.0", version
) == 0)
276 vv
= VCARD_VERSION_4_0
;
278 vv
= VCARD_VERSION_UNSUPPORTED
;
284 gchar
* vcard_version_2_str(VCardVersion version
) {
287 if (VCARD_VERSION_2_1
== version
)
288 vv
= g_strdup("2.1");
289 else if (VCARD_VERSION_3_0
== version
)
290 vv
= g_strdup("3.0");
291 else if (VCARD_VERSION_4_0
== version
)
292 vv
= g_strdup("4.0");
297 VCardParserResponse
vcard_parse_text(const gchar
* text
, VCardVersion
* version
, GHashTable
** vcard
) {
298 VCardParserResponse r
= VCARD_PARSER_ERROR
;
300 if (text
== NULL
|| strlen(text
) < 1) return VCARD_PARSER_OK
;
302 gchar
** b
= g_strsplit(text
, "\n", 0);
303 gchar
** buf
= vcard_text_normalize((const gchar
**)b
);
305 guint l
= g_strv_length(buf
);
306 //begin, end and version line is required
307 r
= VCARD_PARSER_BAD_FORMAT
;
309 if (g_strcmp0(buf
[0], "BEGIN:VCARD") == 0 && g_strcmp0(buf
[l
-1], "END:VCARD") == 0) {
310 r
= has_required_version((const gchar
**)buf
, version
);
311 if (r
== VCARD_PARSER_OK
) {
312 init_g_hash_table(vcard
, *version
);
313 if (vcard
&& *vcard
) {
314 r
= vcard_parse(buf
, *version
, vcard
);
316 r
= VCARD_PARSER_ERROR
;