Merged from silc_1_0_branch.
[silc.git] / lib / silcutil / silcvcard.c
index ca26ae28c61458252be64e316de1138a8e322f9b..a6d6fd5bd655e33ccbdf99bf51241553e31a5b4e 100644 (file)
   GNU General Public License for more details.
 
 */
+/* $Id$ */
 /* Implementation of the VCard (RFC 2426) */
 
 #include "silcincludes.h"
 
 #define VCARD_HEADER "BEGIN:VCARD\n"
 #define VCARD_VERSION "VERSION:3.0\n"
-#define VCARD_FOOTER "END:VCARD"
-
-/* Free all data inside the card structure */
-
-static void silc_vcard_free_internal(SilcVCard vcard)
-{
-  int i;
-
-  silc_free(vcard->full_name);
-  silc_free(vcard->family_name);
-  silc_free(vcard->first_name);
-  silc_free(vcard->middle_names);
-  silc_free(vcard->prefix);
-  silc_free(vcard->suffix);
-  silc_free(vcard->nickname);
-  silc_free(vcard->bday);
-  silc_free(vcard->title);
-  silc_free(vcard->role);
-  silc_free(vcard->org_name);
-  silc_free(vcard->org_unit);
-  silc_free(vcard->categories);
-  silc_free(vcard->class);
-  silc_free(vcard->url);
-  silc_free(vcard->label);
-  for (i = 0; i < vcard->num_addrs; i++) {
-    silc_free(vcard->addrs[i].type);
-    silc_free(vcard->addrs[i].pbox);
-    silc_free(vcard->addrs[i].ext_addr);
-    silc_free(vcard->addrs[i].street_addr);
-    silc_free(vcard->addrs[i].city);
-    silc_free(vcard->addrs[i].state);
-    silc_free(vcard->addrs[i].code);
-    silc_free(vcard->addrs[i].country);
-  }
-  silc_free(vcard->addrs);
-  for (i = 0; i < vcard->num_tels; i++) {
-    silc_free(vcard->tels[i].type);
-    silc_free(vcard->tels[i].tel);
-  }
-  silc_free(vcard->tels);
-  for (i = 0; i < vcard->num_emails; i++) {
-    silc_free(vcard->emails[i].type);
-    silc_free(vcard->emails[i].address);
-  }
-  silc_free(vcard->emails);
-  silc_free(vcard->note);
-  silc_free(vcard->rev);
-}
+#define VCARD_FOOTER "END:VCARD\n"
 
 /* Encode VCard */
 
-char *silc_vcard_encode(SilcVCard vcard, SilcUInt32 *vcard_len)
+unsigned char *silc_vcard_encode(SilcVCard vcard, SilcUInt32 *vcard_len)
 {
   SilcBufferStruct buffer;
   int i;
@@ -115,9 +69,9 @@ char *silc_vcard_encode(SilcVCard vcard, SilcUInt32 *vcard_len)
     silc_buffer_strformat(&buffer,
                          "CATEGORIES:", vcard->categories, "\n",
                          SILC_STR_END);
-  if (vcard->class)
+  if (vcard->catclass)
     silc_buffer_strformat(&buffer,
-                         "CLASS:", vcard->class, "\n",
+                         "CLASS:", vcard->catclass, "\n",
                          SILC_STR_END);
   if (vcard->url)
     silc_buffer_strformat(&buffer,
@@ -144,7 +98,7 @@ char *silc_vcard_encode(SilcVCard vcard, SilcUInt32 *vcard_len)
     silc_buffer_strformat(&buffer,
                          "TEL;TYPE=",
                          vcard->tels[i].type, ":",
-                         vcard->tels[i].tel, "\n",
+                         vcard->tels[i].telnum, "\n",
                          SILC_STR_END);
   }
   for (i = 0; i < vcard->num_emails; i++) {
@@ -171,6 +125,7 @@ char *silc_vcard_encode(SilcVCard vcard, SilcUInt32 *vcard_len)
   return buffer.head;
 }
 
+/* Take one token */
 #define VCARD_TOKEN(x)                         \
   if (!(x)) {                                  \
     (x) = silc_memdup(val + off, i - off);     \
@@ -178,21 +133,39 @@ char *silc_vcard_encode(SilcVCard vcard, SilcUInt32 *vcard_len)
     continue;                                  \
   }
 
-#define VCARD_TYPETOKEN(x)                             \
-  if (!(x)) {                                          \
-    int tmpi;                                          \
-    (x) = silc_memdup(val + off + 5, i - off - 5 - 1); \
-    tmpi = off + 5 + strlen((x)) + 1;                  \
-    off = i;                                           \
-    i = tmpi;                                          \
+/* Take on TYPE= token and prepare for next token, accept the
+   type also without TYPE= as it is possible */
+#define VCARD_TYPETOKEN(x)                                     \
+  if (!(x)) {                                                  \
+    int tmpi = 0;                                              \
+    if (!strncasecmp(val + off, "TYPE=", 5))                   \
+      tmpi = 5;                                                        \
+    (x) = silc_memdup(val + off + tmpi, i - off - tmpi - 1);   \
+    tmpi = off + tmpi + strlen((x)) + 1;                       \
+    off = i;                                                   \
+    i = tmpi;                                                  \
   }
 
+/* Take last token */
 #define VCARD_LASTTOKEN(x)                     \
   if (!(x)) {                                  \
     if (off < len)                             \
       (x) = silc_memdup(val + off, len - off); \
   }                                            \
 
+/* Get one (single) field */
+#define VCARD_FIELD(val, c, x)                         \
+do {                                                   \
+  if (!strncasecmp(val, (c), strlen((c)))) {           \
+    int tmpl = strlen((c));                            \
+    if ((x))                                           \
+      break;                                           \
+    if (len - tmpl > 0)                                        \
+      (x) = silc_memdup(val + tmpl, len - tmpl);       \
+    goto next;                                         \
+  }                                                    \
+} while(0)
+
 /* Decode VCard */
 
 bool silc_vcard_decode(const unsigned char *data, SilcUInt32 data_len,
@@ -204,20 +177,42 @@ bool silc_vcard_decode(const unsigned char *data, SilcUInt32 data_len,
   
   val = (unsigned char *)data;
   while (val) {
-    if (!strchr(val, '\n'))
-      break;
-    len = strchr(val, '\n') - (char *)val;
-    if (len > data_len - (val - data))
+    len = 0;
+    for (i = (val - data); i < data_len; i++) {
+      if (data[i] == '\0' || data[i] == '\n') {
+       len = i - (val - data);
+       break;
+      }
+    }
+    if (!len || len > data_len - (val - data))
       break;
 
+    /* Check for mandatory header and footer */
     if (!strncasecmp(val, VCARD_HEADER, strlen(VCARD_HEADER))) {
       has_begin = TRUE;
-    } else if (!strncasecmp(val, "FN:", 3)) {
-      if (vcard->full_name)
-       break;
-      if (len - 3)
-       vcard->full_name = silc_memdup(val + 3, len - 3);
-    } else if (!strncasecmp(val, "N:", 2)) {
+      goto next;
+    }
+    if (!strncasecmp(val, VCARD_FOOTER, strlen(VCARD_FOOTER))) {
+      has_end = TRUE;
+      goto next;
+    }
+
+    /* Get single fields */
+    VCARD_FIELD(val, "FN:", vcard->full_name);
+    VCARD_FIELD(val, "NICKNAME:", vcard->nickname);
+    VCARD_FIELD(val, "BDAY:", vcard->bday);
+    VCARD_FIELD(val, "TITLE:", vcard->title);
+    VCARD_FIELD(val, "ROLE:", vcard->role);
+    VCARD_FIELD(val, "CATEGORIES:", vcard->categories);
+    VCARD_FIELD(val, "CLASS:", vcard->catclass);
+    VCARD_FIELD(val, "URL:", vcard->url);
+    VCARD_FIELD(val, "LABEL;", vcard->label);
+    VCARD_FIELD(val, "NOTE:", vcard->note);
+    VCARD_FIELD(val, "REV:", vcard->rev);
+
+    /* Get multi-column fields */
+
+    if (!strncasecmp(val, "N:", 2)) {
       if (vcard->family_name)
        break;
       if (len - 2) {
@@ -229,29 +224,20 @@ bool silc_vcard_decode(const unsigned char *data, SilcUInt32 data_len,
            VCARD_TOKEN(vcard->middle_names);
            VCARD_TOKEN(vcard->prefix);
          }
-       VCARD_LASTTOKEN(vcard->suffix);
+       if (!vcard->family_name && !vcard->first_name) {
+         VCARD_LASTTOKEN(vcard->family_name);
+         off += (len - off);
+       }
+       if (!vcard->first_name) {
+         VCARD_LASTTOKEN(vcard->first_name);
+       } else {
+         VCARD_LASTTOKEN(vcard->suffix);
+       }
       }
-    } else if (!strncasecmp(val, "NICKNAME:", 9)) {
-      if (vcard->nickname)
-       continue;
-      if (len - 9)
-       vcard->nickname = silc_memdup(val + 9, len - 9);
-    } else if (!strncasecmp(val, "BDAY:", 5)) {
-      if (vcard->bday)
-       continue;
-      if (len - 5)
-       vcard->bday = silc_memdup(val + 5, len - 5);
-    } else if (!strncasecmp(val, "TITLE:", 6)) {
-      if (vcard->title)
-       continue;
-      if (len - 6)
-       vcard->title = silc_memdup(val + 6, len - 6);
-    } else if (!strncasecmp(val, "ROLE:", 5)) {
-      if (vcard->role)
-       continue;
-      if (len - 5)
-       vcard->role = silc_memdup(val + 5, len - 5);
-    } else if (!strncasecmp(val, "ORG:", 4)) {
+      goto next;
+    }
+
+    if (!strncasecmp(val, "ORG:", 4)) {
       if (vcard->org_name)
        continue;
       if (len - 4) {
@@ -262,35 +248,20 @@ bool silc_vcard_decode(const unsigned char *data, SilcUInt32 data_len,
            break;
          }
        }
+       /* It's possible to have ORG without last ';', so check for it */
        if (!vcard->org_name) {
          VCARD_LASTTOKEN(vcard->org_name);
        } else {
          VCARD_LASTTOKEN(vcard->org_unit);
        }
       }
-    } else if (!strncasecmp(val, "CATEGORIES:", 11)) {
-      if (vcard->categories)
-       continue;
-      if (len - 11)
-       vcard->categories = silc_memdup(val + 11, len - 11);
-    } else if (!strncasecmp(val, "CLASS:", 6)) {
-      if (vcard->class)
-       continue;
-      if (len - 6)
-       vcard->class = silc_memdup(val + 6, len - 6);
-    } else if (!strncasecmp(val, "URL:", 4)) {
-      if (vcard->url)
-       continue;
-      if (len - 4)
-       vcard->url = silc_memdup(val + 4, len - 4);
-    } else if (!strncasecmp(val, "LABEL;", 6)) {
-      if (vcard->label)
-       continue;
-      if (len - 6)
-       vcard->label = silc_memdup(val + 6, len - 6);
-    } else if (!strncasecmp(val, "ADR;", 4)) {
+      goto next;
+    }
+
+    if (!strncasecmp(val, "ADR;", 4)) {
       vcard->addrs = silc_realloc(vcard->addrs, sizeof(*vcard->addrs) *
                                  (vcard->num_addrs + 1));
+      memset(&vcard->addrs[vcard->num_addrs], 0, sizeof(*vcard->addrs));
       if (len - 4) {
        off = 4;
        for (i = off; i < len; i++)
@@ -306,9 +277,13 @@ bool silc_vcard_decode(const unsigned char *data, SilcUInt32 data_len,
        VCARD_LASTTOKEN(vcard->addrs[vcard->num_addrs].country);
       }
       vcard->num_addrs++;
-    } else if (!strncasecmp(val, "TEL;", 4)) {
+      goto next;
+    }
+
+    if (!strncasecmp(val, "TEL;", 4)) {
       vcard->tels = silc_realloc(vcard->tels, sizeof(*vcard->tels) *
                                 (vcard->num_tels + 1));
+      memset(&vcard->tels[vcard->num_tels], 0, sizeof(*vcard->tels));
       if (len - 4) {
        off = 4;
        for (i = off; i < len; i++)
@@ -317,12 +292,16 @@ bool silc_vcard_decode(const unsigned char *data, SilcUInt32 data_len,
            VCARD_TYPETOKEN(vcard->tels[vcard->num_tels].type);
            break;
          }
-       VCARD_LASTTOKEN(vcard->tels[vcard->num_tels].tel);
+       VCARD_LASTTOKEN(vcard->tels[vcard->num_tels].telnum);
       }
       vcard->num_tels++;
-    } else if (!strncasecmp(val, "EMAIL;", 6)) {
+      goto next;
+    }
+
+    if (!strncasecmp(val, "EMAIL;", 6)) {
       vcard->emails = silc_realloc(vcard->emails, sizeof(*vcard->emails) *
                                   (vcard->num_emails + 1));
+      memset(&vcard->emails[vcard->num_emails], 0, sizeof(*vcard->emails));
       if (len - 6) {
        off = 6;
        for (i = off; i < len; i++)
@@ -334,44 +313,89 @@ bool silc_vcard_decode(const unsigned char *data, SilcUInt32 data_len,
        VCARD_LASTTOKEN(vcard->emails[vcard->num_emails].address);
       }
       vcard->num_emails++;
-    } else if (!strncasecmp(val, "NOTE:", 5)) {
-      if (vcard->note)
-       continue;
-      if (len - 5)
-       vcard->note = silc_memdup(val + 5, len - 5);
-    } else if (!strncasecmp(val, "REV:", 4)) {
-      if (vcard->rev)
-       continue;
-      if (len - 4)
-       vcard->rev = silc_memdup(val + 4, len - 4);
-    } else if (!strncasecmp(val, VCARD_FOOTER, strlen(VCARD_FOOTER))) {
-      has_end = TRUE;
-      break;
+      goto next;
     }
 
+  next:
     val = strchr(val, '\n');
-    if (!val || !(*val))
+    if (!val)
       break;
     val++;
     if (!val || !(*val))
       break;
   }
 
-  if (!has_begin || !has_end) {
-    silc_vcard_free_internal(vcard);
+  if (!has_begin || !has_end || !vcard->full_name) {
+    silc_vcard_free(vcard);
     return FALSE;
   }
 
   return TRUE;
 }
 
+/* Allocate vcard context */
+
+SilcVCard silc_vcard_alloc(void)
+{
+  SilcVCard vcard = silc_calloc(1, sizeof(*vcard));
+  if (!vcard)
+    return NULL;
+  vcard->dynamic = TRUE;
+  return vcard;
+}
+
 /* Free the vcard structure */
 
 void silc_vcard_free(SilcVCard vcard)
 {
-  silc_vcard_free_internal(vcard);
-  memset(vcard, 'F', sizeof(*vcard));
-  silc_free(vcard);
+  int i;
+
+  silc_free(vcard->full_name);
+  silc_free(vcard->family_name);
+  silc_free(vcard->first_name);
+  silc_free(vcard->middle_names);
+  silc_free(vcard->prefix);
+  silc_free(vcard->suffix);
+  silc_free(vcard->nickname);
+  silc_free(vcard->bday);
+  silc_free(vcard->title);
+  silc_free(vcard->role);
+  silc_free(vcard->org_name);
+  silc_free(vcard->org_unit);
+  silc_free(vcard->categories);
+  silc_free(vcard->catclass);
+  silc_free(vcard->url);
+  silc_free(vcard->label);
+  for (i = 0; i < vcard->num_addrs; i++) {
+    silc_free(vcard->addrs[i].type);
+    silc_free(vcard->addrs[i].pbox);
+    silc_free(vcard->addrs[i].ext_addr);
+    silc_free(vcard->addrs[i].street_addr);
+    silc_free(vcard->addrs[i].city);
+    silc_free(vcard->addrs[i].state);
+    silc_free(vcard->addrs[i].code);
+    silc_free(vcard->addrs[i].country);
+  }
+  silc_free(vcard->addrs);
+  for (i = 0; i < vcard->num_tels; i++) {
+    silc_free(vcard->tels[i].type);
+    silc_free(vcard->tels[i].telnum);
+  }
+  silc_free(vcard->tels);
+  for (i = 0; i < vcard->num_emails; i++) {
+    silc_free(vcard->emails[i].type);
+    silc_free(vcard->emails[i].address);
+  }
+  silc_free(vcard->emails);
+  silc_free(vcard->note);
+  silc_free(vcard->rev);
+  if (!vcard->dynamic)
+    memset(vcard, 0, sizeof(*vcard));
+
+  if (vcard->dynamic) {
+    memset(vcard, 0, sizeof(*vcard));
+    silc_free(vcard);
+  }
 }
 
 /* Print card to file stream */
@@ -403,8 +427,8 @@ void silc_vcard_fprintf(SilcVCard vcard, FILE *stream)
            vcard->org_unit ? vcard->org_unit : "");
   if (vcard->categories)
     fprintf(stream, "CATEGORIES:%s\n", vcard->categories);
-  if (vcard->class)
-    fprintf(stream, "CLASS:%s\n", vcard->class);
+  if (vcard->catclass)
+    fprintf(stream, "CLASS:%s\n", vcard->catclass);
   if (vcard->url)
     fprintf(stream, "URL:%s\n", vcard->url);
   if (vcard->label)
@@ -423,7 +447,7 @@ void silc_vcard_fprintf(SilcVCard vcard, FILE *stream)
   for (i = 0; i < vcard->num_tels; i++) {
     fprintf(stream, "TEL;TYPE=%s:%s\n",
            vcard->tels[i].type,
-           vcard->tels[i].tel ? vcard->tels[i].tel : "");
+           vcard->tels[i].telnum ? vcard->tels[i].telnum : "");
   }
   for (i = 0; i < vcard->num_emails; i++) {
     fprintf(stream, "EMAIL;TYPE=%s:%s\n",