]> git.datanom.net - vcard-parser.git/commitdiff
Complete version 0.1
authorMichael Rasmussen <mir@datanom.net>
Mon, 30 Dec 2019 21:55:59 +0000 (22:55 +0100)
committerMichael Rasmussen <mir@datanom.net>
Mon, 30 Dec 2019 21:55:59 +0000 (22:55 +0100)
Signed-off-by: Michael Rasmussen <mir@datanom.net>
src/globals.h [new file with mode: 0644]
src/vcard-parser.c [new file with mode: 0644]

diff --git a/src/globals.h b/src/globals.h
new file mode 100644 (file)
index 0000000..b500d8c
--- /dev/null
@@ -0,0 +1,41 @@
+/*
+ * globals.h
+ *
+ * Copyright 2019 Michael Rasmussen <mir@datanom.net>
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 2 of the License, or
+ * (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software
+ * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston,
+ * MA 02110-1301, USA.
+ */
+
+#ifndef __GLOBALS_H__
+#define __GLOBALS_H__
+
+#include <glib.h>
+
+G_BEGIN_DECLS
+
+typedef enum {
+       VCARD_VERSION_UNSUPPORTED,
+       VCARD_VERSION_DETECT,
+       VCARD_VERSION_2_1,
+       VCARD_VERSION_3_0,
+       VCARD_VERSION_4_0,
+} VCardVersion;
+
+typedef struct _VCard VCard;
+
+G_END_DECLS
+
+#endif
diff --git a/src/vcard-parser.c b/src/vcard-parser.c
new file mode 100644 (file)
index 0000000..056adbd
--- /dev/null
@@ -0,0 +1,335 @@
+/*
+ * vcard-parser.c
+ *
+ * Copyright 2019 Michael Rasmussen <mir@datanom.net>
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 2 of the License, or
+ * (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software
+ * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston,
+ * MA 02110-1301, USA.
+ */
+
+#include <vcard-parser.h>
+
+#ifdef HAVE_CONFIG_H
+#  include "config.h"
+#endif
+
+static gchar* Properties[VCARD_PROPERTIES+1] = {
+       "N",
+       "FN",
+       "PHOTO",
+       "BDAY",
+       "ADR",
+       "LABEL",
+       "TEL",
+       "EMAIL",
+       "MAILER",
+       "GEO",
+       "TITLE",
+       "ROLE",
+       "LOGO",
+       "ORG",
+       "NOTE",
+       "REV",
+       "SOUND",
+       "URL",
+       "UID",
+       "VERSION",
+       "KEY",
+       "TZ",
+       "SOURCE",
+       "AGENT",
+       "PROFILE",
+       "CATEGORIES",
+       "SORT-STRING",
+       "PRODID",
+       "NICKNAME",
+       "NAME",
+       "CLASS",
+       "FBURL",
+       "CAPURI",
+       "CALURI",
+       "CALADRURI",
+       "IMPP",
+       "XML",
+       "ANNIVERSARY",
+       "CLIENTPIDMAP",
+       "LANG",
+       "GENDER",
+       "KIND",
+       "MEMBER",
+       "RELATED",
+       "BIRTHPLACE",
+       "DEATHPLACE",
+       "DEATHDATE",
+       "EXPERTISE",
+       "HOBBY",
+       "INTEREST",
+       "ORG-DIRECTORY",
+       NULL
+};
+
+static void vcard_property_free(gpointer data) {
+       if (!data) return;
+       VCardProperty* vp = (VCardProperty*) data;
+       g_free(vp->name);
+       g_free(vp->value);
+       g_free(vp);
+}
+
+static Property get_property(const gchar* property) {
+/*     Property p = VCARD_PROPERTIES;
+       gchar* lookup = NULL;
+
+       if (g_strcmp0("ORG-DIRECTORY", property) == 0)
+               lookup = g_strdup("ORG_DIRECTORY");
+       else if (g_strcmp0("SORT-STRING", property) == 0)
+               lookup = g_strdup("SORT_STRING");
+       else
+               lookup = g_strdup(property);
+
+       for (int i = 0; i < VCARD_PROPERTIES; i++) {
+               if (g_strcmp0(Properties[i], lookup) == 0) {
+                       p = i;
+                       break;
+               }
+       }
+       g_free(lookup);*/
+       for (Property i = N; i < VCARD_PROPERTIES; i++) {
+               if (g_strcmp0(Properties[i], property) == 0) return i;
+       }
+
+       return VCARD_PROPERTIES;
+}
+
+static gboolean check_support(Property property, VCardVersion version) {
+       gboolean support = FALSE;
+
+       if (version == VCARD_VERSION_2_1) {
+               support = property <= TZ ? TRUE : FALSE;
+       } else if (version == VCARD_VERSION_3_0) {
+               support = property <= IMPP ? TRUE : FALSE;
+       } else if (version == VCARD_VERSION_4_0) {
+               support = property <= VCARD_PROPERTIES ? TRUE : FALSE;
+       };
+
+       return support;
+}
+
+static VCardParserResponse has_required_version(const gchar** buf, VCardVersion* version) {
+       const gchar** tmp = buf;
+       gboolean found = FALSE;
+       VCardParserResponse result = VCARD_PARSER_VERSION_MISSING;
+
+       while (*tmp && !found) {
+               if (!g_str_has_prefix(*tmp, "VERSION")) {
+                       tmp++;
+               } else {
+                       found = TRUE;
+                       gchar** b = g_strsplit(*tmp, ":", 0);
+                       if (g_strv_length(b) == 2) {
+                               if (*version == VCARD_VERSION_DETECT) {
+                                       *version = str_2_vcard_version(b[1]);
+                                       result = VCARD_PARSER_OK;
+                               } else {
+                                       if (*version == str_2_vcard_version(b[1])) {
+                                               result = VCARD_PARSER_OK;
+                                       } else
+                                               result = VCARD_PARSER_VERSION_MISMATCH;
+                               }
+                       }
+                       g_strfreev(b);
+               }
+       }
+
+       return result;
+}
+
+static gchar** vcard_text_normalize(const gchar** text) {
+       gchar** new = NULL;
+       const gchar** tmp = text;
+       int count = 0;
+       gchar* str = NULL;
+
+       if (text == NULL) return NULL;
+
+       new = g_new0(gchar*, g_strv_length((gchar **) text) + 1);
+       while (*tmp && g_strcmp0(*tmp, "") != 0) {
+               if (*tmp[0] == ' ') {
+                       // Multi line value
+                       if (str) {
+                               gchar* s = g_strdup(str);
+                               g_free(str);
+                               str = g_strconcat(s, "\n", *tmp+1, NULL);
+                       } else
+                               str = g_strdup(*tmp+1);
+               } else {
+                       if (str) {
+                               new[count++] = g_strdup(str);
+                               g_free(str);
+                               str = NULL;
+                       }
+                       str = g_strdup(*tmp);
+               }
+               tmp++;
+       }
+       if (str) {
+               new[count++] = g_strdup(str);
+               g_free(str);
+       }
+
+       return new;
+}
+
+Property vcard_max_property(VCardVersion version) {
+       Property p = VCARD_PROPERTIES;
+
+       if (version == VCARD_VERSION_2_1) {
+               p = TZ;
+       } else if (version == VCARD_VERSION_3_0) {
+               p = IMPP;
+       } else if (version == VCARD_VERSION_4_0) {
+               p = VCARD_PROPERTIES - 1;
+       }
+
+       return p;
+}
+
+void destroy_hash_table(GHashTable* ht) {
+       GHashTableIter iter;
+       gpointer key, value;
+
+       g_hash_table_iter_init (&iter, ht);
+       while (g_hash_table_iter_next(&iter, &key, &value)) {
+               if (value)
+                       g_slist_free_full((GSList *) value, vcard_property_free);
+       }
+
+       g_hash_table_destroy(ht);
+}
+
+static void init_g_hash_table(GHashTable** ht, VCardVersion version) {
+       if (!ht) return;
+
+       Property property = vcard_max_property(version);
+       if (property < VCARD_PROPERTIES) {
+               *ht = g_hash_table_new_full(
+                       g_str_hash, g_str_equal, (GDestroyNotify) g_free, NULL);
+               for (Property i = N; i <= property; i++) {
+                       g_hash_table_insert(*ht, g_strdup(Properties[i]), NULL);
+               }
+       }
+}
+
+static VCardParserResponse vcard_parse(gchar** text, VCardVersion version, GHashTable** vcard) {
+       VCardParserResponse r = VCARD_PARSER_ERROR;
+       guint l = g_strv_length(text);
+
+       for (guint i = 0; i < l; i++) {
+               gchar** b = g_strsplit(text[i], ":", 2);
+               if (g_strv_length(b) != 2 || !b[0] || !b[1]) {
+                       destroy_hash_table(*vcard);
+                       *vcard = NULL;
+                       g_strfreev(b);
+                       return VCARD_PARSER_ERROR;
+               }
+               gchar** c = g_strsplit(b[0], ";", 0);
+               if (g_strcmp0(c[0], "BEGIN") == 0 ||
+                       g_strcmp0(c[0], "VERSION") == 0 ||
+                       g_strcmp0(c[0], "END") == 0) {
+                               g_strfreev(c);
+                               g_strfreev(b);
+                               continue;
+               }
+               Property property = get_property(c[0]);
+               if (check_support(property, version)) {
+                       GSList* value = g_hash_table_lookup(*vcard, c[0]);
+                       VCardProperty* vp = g_new(VCardProperty, 1);
+                       vp->name = g_strdup(b[0]);
+                       vp->value = g_strdup(b[1]);
+                       value = g_slist_append(value, vp);
+                       g_hash_table_insert(*vcard, g_strdup(c[0]), value);
+               } else {
+                       destroy_hash_table(*vcard);
+                       *vcard = NULL;
+                       g_strfreev(c);
+                       g_strfreev(b);
+                       return VCARD_PARSER_ATTRIBUTE_VERSION_MISMATCH;
+               }
+               g_strfreev(c);
+               g_strfreev(b);
+       }
+       r = VCARD_PARSER_OK;
+
+       return r;
+}
+
+VCardVersion str_2_vcard_version(const gchar* version) {
+       VCardVersion vv = VCARD_VERSION_DETECT;
+
+       if (version) {
+               if (g_strcmp0("2.1", version) == 0)
+                       vv = VCARD_VERSION_2_1;
+               else if (g_strcmp0("3.0", version) == 0)
+                       vv = VCARD_VERSION_3_0;
+               else if (g_strcmp0("4.0", version) == 0)
+                       vv = VCARD_VERSION_4_0;
+               else
+                       vv = VCARD_VERSION_UNSUPPORTED;
+       }
+
+       return vv;
+}
+
+gchar* vcard_version_2_str(VCardVersion version) {
+       gchar* vv = NULL;
+
+       if (VCARD_VERSION_2_1 == version)
+               vv = g_strdup("2.1");
+       else if (VCARD_VERSION_3_0 == version)
+               vv = g_strdup("3.0");
+       else if (VCARD_VERSION_4_0 == version)
+               vv = g_strdup("4.0");
+
+       return vv;
+}
+
+VCardParserResponse vcard_parse_text(const gchar* text, VCardVersion* version, GHashTable** vcard) {
+       VCardParserResponse r = VCARD_PARSER_ERROR;
+
+       if (text == NULL || strlen(text) < 1) return VCARD_PARSER_OK;
+
+       gchar** b = g_strsplit(text, "\n", 0);
+       gchar** buf = vcard_text_normalize((const gchar**)b);
+       g_strfreev(b);
+       guint l = g_strv_length(buf);
+       //begin, end and version line is required
+       r = VCARD_PARSER_BAD_FORMAT;
+       if (buf && l > 3) {
+               if (g_strcmp0(buf[0], "BEGIN:VCARD") == 0 && g_strcmp0(buf[l-1], "END:VCARD") == 0) {
+                       r = has_required_version((const gchar **)buf, version);
+                       if (r == VCARD_PARSER_OK) {
+                               init_g_hash_table(vcard, *version);
+                               if (vcard && *vcard) {
+                                       r = vcard_parse(buf, *version, vcard);
+                               } else {
+                                       r = VCARD_PARSER_ERROR;
+                               }
+                       }
+               }
+       }
+       g_strfreev(buf);
+
+       return r;
+}
This page took 0.044219 seconds and 5 git commands to generate.