+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;