/* * vcard.c * * Copyright 2019 Michael Rasmussen * * 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 #include #include #include #include #ifdef HAVE_CONFIG_H # include "config.h" #endif struct _VCard { VCardParserResponse state; VCardVersion version; GHashTable* vcard; }; static char *replaceWord(const char *s, const char *oldW, const char *newW) { char *result; int i, cnt = 0; int newWlen = strlen(newW); int oldWlen = strlen(oldW); for (i = 0; s[i] != '\0'; i++) { if (strstr(&s[i], oldW) == &s[i]) { cnt++; i += oldWlen - 1; } } result = (char *) malloc(i + cnt * (newWlen - oldWlen) + 1); i = 0; while (*s) { if (strstr(s, oldW) == s) { strcpy(&result[i], newW); i += newWlen; s += oldWlen; } else result[i++] = *s++; } result[i] = '\0'; return result; } static void hash_table_cb(gpointer key, gpointer value, gpointer data) { GSList* list = (GSList*) value; gchar** str = (gchar**) data; gchar* s = NULL; if (!list) return; while (list) { VCardProperty* vp = (VCardProperty*) list->data; gchar* s0 = replaceWord(vp->value, "\n", "\n "); if (!*str) s = g_strconcat(vp->name, ":", s0, NULL); else { s = g_strconcat(*str, "\n", vp->name, ":", s0, NULL); g_free(*str); } g_free(s0); *str = g_strdup(s); g_free(s); list = list->next; } } static gchar** make_chunks(const gchar* text) { gchar** chunks = NULL; gchar* v_text = g_strdup(text); g_strstrip(v_text); gchar** tmp = g_strsplit(v_text, "END:VCARD", 0); g_free(v_text); guint lines = g_strv_length(tmp); if (lines < 2) return tmp; if (strlen(tmp[lines-1]) < 1) { chunks = g_new0(gchar*, lines); lines--; } else chunks = g_new0(gchar*, lines + 1); for(int i = 0; i < lines; i++) { g_strchug(tmp[i]); chunks[i] = g_strconcat(tmp[i], "END:VCARD", NULL); } g_strfreev(tmp); return chunks; } void VCard_free(VCard* object) { if (object == NULL) return; if (object->vcard) destroy_hash_table(object->vcard); g_free(object); } void VCard_list_free(GSList* list) { GSList* tmp = list; while(tmp) { VCard* object = (VCard*) tmp->data; VCard_free(object); tmp = tmp->next; } if(list) g_slist_free(list); } VCard* VCard_new(VCardVersion version) { VCard* vc = g_new0(VCard, 1); vc->version = version; return vc; } GSList* VCard_new_from_text(const gchar* text, VCardVersion version, gboolean skip_broken) { GSList* list = NULL; VCard* vc = NULL; if (!text || strlen(text) < 1) return NULL; gchar** chunks = make_chunks(text); for (int i = 0; i < g_strv_length(chunks); i++) { vc = g_new0(VCard, 1); vc->version = version; VCardParserResponse r = vcard_parse_text(chunks[i], &vc->version, &vc->vcard); if (r != VCARD_PARSER_OK && skip_broken) { VCard_free(vc); continue; } vc->state = r; list = g_slist_prepend(list, vc); } g_strfreev(chunks); return list; } GSList* VCard_new_from_file(FILE* file, VCardVersion version, gboolean skip_broken) { GSList* list = NULL; VCard* vc = g_new0(VCard, 1); vc->version = version; list = g_slist_prepend(list, vc); return list; } void VCard_print_list(GSList* list) { VCard_print_list_fd(list, STDOUT_FILENO); } void VCard_print_list_fd(GSList* list, int fd) { GSList* tmp = list; if (fd > 0) { while(tmp) { VCard* object = (VCard*) tmp->data; VCard_print_fd(object, fd); if (tmp->next) { ssize_t len = strlen("\n"); if (write(fd, "\n", len) < len) g_warning(strerror(errno)); } tmp = tmp->next; } } } void VCard_print(VCard* object){ VCard_print_fd(object, STDOUT_FILENO); } void VCard_print_fd(VCard* object, int fd) { gchar *body = NULL, *s = NULL; if (object && object->vcard && fd > 0) { gchar* version = vcard_version_2_str(object->version); s = g_strdup_printf("BEGIN:VCARD\nVERSION:%s", version); g_free(version); g_hash_table_foreach(object->vcard, hash_table_cb, &s); body = g_strdup_printf("%s\nEND:VCARD", s); g_free(s); ssize_t len = strlen(body); if (write(fd, body, len) < len) g_warning(strerror(errno)); g_free(body); } } gchar* VCard_get_state(VCard* object) { gchar* message = NULL; if (!object) return NULL; switch (object->state) { case VCARD_PARSER_OK: message = g_strdup("OK"); break; case VCARD_PARSER_VERSION_MISMATCH: message = g_strdup("Version mismatch"); break; case VCARD_PARSER_ATTRIBUTE_VERSION_MISMATCH: message = g_strdup("Attribute not supported in this version"); break; case VCARD_PARSER_BAD_FORMAT: message = g_strdup("Bad input format"); break; case VCARD_PARSER_ERROR: message = g_strdup("Unknown error"); break; default: message = g_strdup("Unhandled error"); break; } return message; } GSList* VCard_get_property(VCard* object, const gchar* property) { GSList *values = NULL, *tmp; GSList *iter; VCardProperty* vp = NULL; if (object && object->vcard && property) { tmp = g_hash_table_lookup(object->vcard, property); for (iter = tmp; iter; iter = g_slist_next(iter)) { vp = (VCardProperty*) iter->data; values = g_slist_prepend(values, g_strdup_printf("%s:%s", vp->name, vp->value)); } } return values; }