]> git.deb.at Git - pkg/abook.git/blobdiff - filter.c
Merge remote-tracking branch 'upstream/master' into upstream
[pkg/abook.git] / filter.c
index c8b3d9ea6d82b02f59fb9030919dbaad70b0379c..2ea5a2dd77bfa3acafa196f5f324a7b47cee2ed6 100644 (file)
--- a/filter.c
+++ b/filter.c
@@ -35,6 +35,8 @@
 
 extern abook_field_list *fields_list;
 extern int fields_count;
+// see also enum field_types @database.h
+extern abook_field standard_fields[];
 
 /*
  * function declarations
@@ -503,8 +505,6 @@ export_file(char filtname[FILTNAME_LEN], char *filename)
 
 #include "ldif.h"
 
-static void    ldif_fix_string(char *str);
-
 /* During LDIF import we need more fields than the
    ITEM_FIELDS of a *list_item. Eg: "objectclass"
    to test valid records, ...
@@ -789,8 +789,6 @@ ldif_parse_file(FILE *handle)
                        continue; /* just skip the errors */
                }
 
-               ldif_fix_string(value);
-
                ldif_convert(item, type, value);
 
                xfree(line);
@@ -802,19 +800,6 @@ ldif_parse_file(FILE *handle)
        return 0;
 }
 
-static void
-ldif_fix_string(char *str)
-{
-       int i, j;
-
-       for(i = 0, j = 0; j < (int)strlen(str); i++, j++)
-               str[i] = ( str[j] == (char)0xc3 ?
-                               (char) str[++j] + (char) 0x40 :
-                               str[j] );
-
-       str[i] = 0;
-}
-
 /*
  * end of ldif import
  */
@@ -1084,23 +1069,26 @@ html_export_database(FILE *out, struct db_enumerator e)
        html_export_write_head(out);
 
        db_enumerate_items(e) {
-               fprintf(out, "<tr>");
+               fprintf(out, " <tr>\n");
                for(cur = index_elements; cur; cur = cur->next) {
                        if(cur->type != INDEX_FIELD)
                                continue;
-
+                       
                        get_list_field(e.item, cur, &f);
 
+                       fprintf(out, "  <td>");
+
                        if(f.type == FIELD_EMAILS) {
-                               fprintf(out, "<td>");
                                html_print_emails(out, &f);
-                               fprintf(out, "</td>");
-                               continue;
                        } else {
-                               fprintf(out, "<td>%s</td>", safe_str(f.data));
+                               if (strcmp(safe_str(f.data), "") == 0)
+                                       fprintf(out, "&nbsp;");
+                               else
+                                       fprintf(out, "%s", safe_str(f.data));
                        }
+                       fprintf(out, "</td>\n");
                }
-               fprintf(out, "</tr>\n");
+               fprintf(out, " </tr>\n");
        }
 
        html_export_write_tail(out);
@@ -1114,22 +1102,45 @@ html_export_write_head(FILE *out)
        char *realname = get_real_name(), *str;
        struct index_elem *cur;
 
-       fprintf(out, "<!DOCTYPE HTML PUBLIC \"-//W3C//DTD HTML 4.01 Transitional//EN\">\n");
-       fprintf(out, "<html>\n<head>\n <title>%s's addressbook</title>",
-                       realname );
-       fprintf(out, "\n</head>\n<body>\n");
-       fprintf(out, "\n<h2>%s's addressbook</h2>\n", realname );
-       fprintf(out, "<br><br>\n\n");
-
-       fprintf(out, "<table border=\"1\" align=\"center\">\n<tr>");
+       fprintf(out, "<!DOCTYPE html>\n");
+       fprintf(out, "<html>\n");
+       fprintf(out, "<head>\n");
+       fprintf(out, " <meta charset=\"utf-8\" />\n");
+       fprintf(out, " <title>");
+       fprintf(out, _("%s's addressbook"), realname );
+       fprintf(out, "</title>\n");
+       fprintf(out, " <style type=\"text/css\">\n");
+       fprintf(out, "  table {border-collapse: collapse ; border: 1px solid #000;}\n");
+       fprintf(out, "  table th, table td {text-align: left; border: 1px solid #000; padding: 0.33em;}\n");
+       fprintf(out, "  table th {border-bottom: 3px double #000; background-color: #ccc;}\n");
+       fprintf(out, " </style>\n");
+       fprintf(out, "</head>\n");
+       fprintf(out, "<body>\n");
+       fprintf(out, "<h1>");
+       fprintf(out, _("%s's addressbook"), realname);
+       fprintf(out, "</h1>\n");
+
+       fprintf(out, "<table>\n");
+       fprintf(out, "<thead>\n");
+       fprintf(out, " <tr>\n");
        for(cur = index_elements; cur; cur = cur->next) {
                if(cur->type != INDEX_FIELD)
                        continue;
 
                get_field_info(cur->d.field.id, NULL, &str, NULL);
-               fprintf(out, "<th>%s</th>", str);
+
+               fprintf(out, "  <th>");
+
+               if (strcmp(str, "") == 0)
+                       fprintf(out, "&nbsp;");
+               else
+                       fprintf(out, "%s", str);
+
+               fprintf(out, "</th>\n");
        }
-       fprintf(out, "</tr>\n\n");
+       fprintf(out, " </tr>\n");
+       fprintf(out, "<thead>\n");
+       fprintf(out, "<tbody>\n");
 
        free(realname);
 }
@@ -1137,8 +1148,9 @@ html_export_write_head(FILE *out)
 static void
 html_export_write_tail(FILE *out)
 {
-       fprintf(out, "\n</table>\n");
-       fprintf(out, "\n</body>\n</html>\n");
+       fprintf(out, "</table>\n");
+       fprintf(out, "</body>\n");
+       fprintf(out, "</html>");
 }
 
 /*
@@ -1302,6 +1314,11 @@ pine_export_database(FILE *out, struct db_enumerator e)
  * csv import filter
  */
 
+/* This is used by both allcsv_export_database() and csv_export_common()
+   to distinguish between standard and defined fields.
+   To avoid confusions this should stay > ITEM_FIELDS  */
+#define CUSTOM_FIELD_START_INDEX       (ITEM_FIELDS + 10)
+
 /* FIXME
  * these files should be parsed according to a certain
  * lay out, or the default if layout is not given, at
@@ -1555,7 +1572,7 @@ static char *vcard_fields[] = {
        "FN",                   /* FORMATTED NAME */
        "EMAIL",                /* EMAIL */
        "ADR",                  /* ADDRESS */
-       "ADR",                  /* ADDRESS2 - not used */
+       "ADR",                  /* ADDRESS2 */
        "ADR",                  /* CITY */
        "ADR",                  /* STATE */
        "ADR",                  /* ZIP */
@@ -1567,22 +1584,9 @@ static char *vcard_fields[] = {
        "NICKNAME",             /* NICK */
        "URL",                  /* URL */
        "NOTE",                 /* NOTES */
+       "BDAY",                 /* ANNIVERSARY */
        "N",                    /* NAME: special case/mapping in vcard_parse_line() */
-       NULL                    /* not implemented: ANNIVERSARY, ITEM_FIELDS */
-};
-
-/*
- * mappings between vCard ADR field and abook's ADDRESS
- * see rfc2426 section 3.2.1
- */
-static int vcard_address_fields[] = {
-       -1,                     /* vCard(post office box) - not used */
-       -1,                     /* vCard(the extended address) - not used */
-       2,                      /* vCard(the street address) - ADDRESS */
-       4,                      /* vCard(the locality) - CITY */
-       5,                      /* vCard(the region) - STATE */
-       6,                      /* vCard(the postal code) - ZIP */
-       7                       /* vCard(the country name) - COUNTRY */
+       NULL                    /* ITEM_FIELDS */
 };
 
 enum {
@@ -1668,33 +1672,49 @@ vcard_parse_email(list_item item, char *line)
        }
 }
 
+
+/*
+ * mappings between vCard ADR field and abook's ADDRESS
+ * see rfc2426 section 3.2.1
+ */
 static void
 vcard_parse_address(list_item item, char *line)
 {
-       int i;
-       int k;
        char *value;
-       char *address_field;
 
        value = vcard_get_line_element(line, VCARD_VALUE);
        if(!value)
                return;
 
-       address_field = value;
-       for(i=k=0; value[i]; i++) {
-               if(value[i] == ';') {
-                       value[i] = '\0';
-                       if(vcard_address_fields[k] >= 0) {
-                               item[vcard_address_fields[k]] = xstrdup(address_field);
-                       }
-                       address_field = &value[i+1];
-                       k++;
-                       if((k+1)==(sizeof(vcard_address_fields)/sizeof(*vcard_address_fields)))
-                               break;
-               }
-       }
-       item[vcard_address_fields[k]] = xstrdup(address_field);
-       xfree(value);
+       // vCard(post office box) - not used
+       strsep(&value, ";");
+       if(!value) return;
+
+       // vCard(the extended address)
+       item_fput(item, ADDRESS2, xstrdup(strsep(&value, ";")));
+       if(!value) return;
+
+       // vCard(the street address)
+       item_fput(item, ADDRESS, xstrdup(strsep(&value, ";")));
+       if(!value) return;
+
+       // vCard(the locality)
+       item_fput(item, CITY, xstrdup(strsep(&value, ";")));
+       if(!value) return;
+
+       // vCard(the region)
+       item_fput(item, STATE, xstrdup(strsep(&value, ";")));
+       if(!value) return;
+
+       // vCard(the postal code)
+       item_fput(item, ZIP, xstrdup(strsep(&value, ";")));
+       if(!value) return;
+
+       // vCard(the country name)
+       item_fput(item, COUNTRY, xstrdup(strsep(&value, ";")));
+
+       // support of optional trailing ";" to the ADR field
+       if(value && *value) xfree(value);
 }
 
 static void
@@ -1841,7 +1861,12 @@ csv_export_common(FILE *out, struct db_enumerator e,
                        else if(CSV_IS_SPECIAL(fields[i])) {
                                if(special_func)
                                        (*special_func)(out, e.item, fields[i]);
-                       } else
+                       }
+                       else if(fields[i] >= CUSTOM_FIELD_START_INDEX) {
+                               fprintf(out, "\"%s\"",
+                                       safe_str(db_fget_byid(e.item, fields[i] - CUSTOM_FIELD_START_INDEX)));
+                       }
+                       else
                                /*fprintf(out,(
                        strchr(safe_str(database[e.item][field_idx(fields[i])]), ',') ||
                        strchr(safe_str(database[e.item][field_idx(fields[i])]), '\"')) ?
@@ -1884,7 +1909,7 @@ allcsv_export_database(FILE *out, struct db_enumerator e)
         * TODO: Should get these atomatically from abook_fileds
         *  - JH
         */
-       int allcsv_export_fields[] = {
+       int allcsv_export_fields[ITEM_FIELDS + 6] = { // only the 5 custom fields are allowed so far
                NAME,
                EMAIL,
                ADDRESS,
@@ -1896,7 +1921,7 @@ allcsv_export_database(FILE *out, struct db_enumerator e)
                PHONE,
                WORKPHONE,
                FAX,
-               MOBILEPHONE,
+               MOBILEPHONE, // spelt "mobile" in standard_fields
                NICK,
                URL,
                NOTES,
@@ -1906,23 +1931,47 @@ allcsv_export_database(FILE *out, struct db_enumerator e)
        };
 
        fprintf(out, "#");
-       fprintf(out, "\"NAME\",");
-       fprintf(out, "\"EMAIL\",");
-       fprintf(out, "\"ADDRESS\",");
-       fprintf(out, "\"ADDRESS2\",");
-       fprintf(out, "\"CITY\",");
-       fprintf(out, "\"STATE\",");
-       fprintf(out, "\"ZIP\",");
-       fprintf(out, "\"COUNTRY\",");
-       fprintf(out, "\"PHONE\",");
-       fprintf(out, "\"WORKPHONE\",");
-       fprintf(out, "\"FAX\",");
-       fprintf(out, "\"MOBILEPHONE\",");
-       fprintf(out, "\"NICK\",");
-       fprintf(out, "\"URL\",");
-       fprintf(out, "\"NOTES\",");
-       fprintf(out, "\"ANNIVERSARY\",");
-       fprintf(out, "\"GROUPS\"\n");
+       int i = 0;
+       while(allcsv_export_fields[i+1] != CSV_LAST) {
+               fprintf(out, "\"%s\",", standard_fields[i++].key);
+       }
+       fprintf(out, "\"%s\"", standard_fields[i].key);
+
+       /*
+         Custom fields handling:
+         This loop appends custom fields' id at the end of allcsv_export_fields and shift
+         the CSV_LAST sentinel value each time one is found.
+         CUSTOM_FIELD_START_INDEX is added to these index values so csv_export_common()
+         can later recognize them and call db_fget_byid() instead of the traditional db_fget()
+
+         It only search for defined the [legacy?] "custom" fields.
+       */
+
+       // pointer to the end of the field list
+       int append_field = ITEM_FIELDS;
+       // custom field's trailing number (between 1 and 5)
+       int j;
+       // full custom field name, eg "custom4"
+       char custom_field_key[8];
+       // index used by custom_field_key
+       int field_no;
+       // name of the defined field <field_no> as chosen by the user
+       char *custom_field_name;
+
+       for (j = 1; j <= 5; j++) {
+               snprintf(custom_field_key, 8, "custom%d", j++);
+               if(find_declared_field(custom_field_key)) {
+                       find_field_number(custom_field_key, &field_no);
+                       get_field_info(field_no, NULL, &custom_field_name, NULL);
+                       // append the field to the list
+                       allcsv_export_fields[append_field] = field_no + CUSTOM_FIELD_START_INDEX;
+                       allcsv_export_fields[++append_field] = CSV_LAST;
+                       // print column name
+                       fprintf(out, ",\"%s\"", custom_field_name);
+               }
+       }
+       free(custom_field_name);
+       fprintf(out, "\n");
 
        csv_export_common(out, e, allcsv_export_fields, NULL);
 
@@ -2025,7 +2074,7 @@ vcard_export_database(FILE *out, struct db_enumerator e)
 void
 vcard_export_item(FILE *out, int item)
 {
-       int j;
+       int j, email_no;
        char *name, *tmp;
        abook_list *emails, *em;
        fprintf(out, "BEGIN:VCARD\r\nFN:%s\r\n",
@@ -2044,15 +2093,25 @@ vcard_export_item(FILE *out, int item)
 
        free(name);
 
-       if(db_fget(item, ADDRESS))
-         fprintf(out, "ADR:;;%s;%s;%s;%s;%s;%s\r\n",
-                 safe_str(db_fget(item, ADDRESS)),
-                 safe_str(db_fget(item, ADDRESS2)),
-                 safe_str(db_fget(item, CITY)),
-                 safe_str(db_fget(item, STATE)),
-                 safe_str(db_fget(item, ZIP)),
-                 safe_str(db_fget(item, COUNTRY))
-                 );
+       if(db_fget(item, NICK))
+         fprintf(out, "NICKNAME:%s\r\n",
+                 safe_str(db_fget(item, NICK)));
+       if(db_fget(item, ANNIVERSARY))
+         fprintf(out, "BDAY:%s\r\n",
+                 safe_str(db_fget(item, ANNIVERSARY)));
+
+       // see rfc6350 section 6.3.1
+       if(db_fget(item, ADDRESS)) {
+               fprintf(out, "ADR:;%s;%s;%s;%s;%s;%s\r\n",
+                       // pobox (unsupported)
+                       safe_str(db_fget(item, ADDRESS2)), // ext (n°, ...)
+                       safe_str(db_fget(item, ADDRESS)), // street
+                       safe_str(db_fget(item, CITY)), // locality
+                       safe_str(db_fget(item, STATE)), // region
+                       safe_str(db_fget(item, ZIP)), // code (postal)
+                       safe_str(db_fget(item, COUNTRY)) // country
+                       );
+       }
 
        if(db_fget(item, PHONE))
          fprintf(out, "TEL;HOME:%s\r\n",
@@ -2070,9 +2129,10 @@ vcard_export_item(FILE *out, int item)
        tmp = db_email_get(item);
        if(*tmp) {
          emails = csv_to_abook_list(tmp);
-
-         for(em = emails; em; em = em->next)
-           fprintf(out, "EMAIL;INTERNET:%s\r\n", em->data);
+         fprintf(out, "EMAIL;PREF;INTERNET:%s\r\n", emails->data);
+         email_no = 1;
+         for(em = emails->next; em; em = em->next, email_no++ )
+                 fprintf(out, "EMAIL;%d;INTERNET:%s\r\n", email_no, em->data);
 
          abook_list_free(&emails);
        }
@@ -2502,37 +2562,13 @@ bsdcal_export_database(FILE *out, struct db_enumerator e)
        return 0;
 }
 
-// see enum field_types @database.h
-static char *conv_table[] = {
-  "name",
-  "email",
-  "address",
-  "address2",
-  "city",
-  "state",
-  "zip",
-  "country",
-  "phone",
-  "workphone",
-  "fax",
-  "mobile",
-  "nick",
-  "url",
-  "notes",
-  "anniversary",
-  0 /* ITEM_FIELDS */
-};
-
 static int
 find_field_enum(char *s) {
-  int i = 0;
-  while (conv_table[i]) {
-    if(!safe_strcmp(conv_table[i], s))
-      return i;
-    i++;
-  }
-  // failed
-  return -1;
+       int i = -1;
+       while(standard_fields[++i].key)
+               if(!strcmp(standard_fields[i].key, s))
+                       return i;
+       return -1;
 }
 
 /* Convert a string with named placeholders to
@@ -2546,17 +2582,17 @@ parse_custom_format(char *s, char *fmt_string, enum field_types *ft)
          exit(EXIT_FAILURE);
        }
 
+       char tmp[1] = { 0 };
        char *p, *start, *field_name = NULL;
        p = start = s;
 
        while(*p) {
                if(*p == '{') {
                  start = ++p;
+
+                 if(! *start) goto cannotparse;
                  p = strchr(start, '}');
-                 if(! *p) {
-                   fprintf(stderr, _("parse_custom_format: invalid format\n"));
-                   exit(EXIT_FAILURE);
-                 }
+                 if(! p) goto cannotparse;
                  strcat(fmt_string, "%s");
                  field_name = strndup(start, (size_t)(p-start));
                  *ft = find_field_enum(field_name);
@@ -2566,23 +2602,53 @@ parse_custom_format(char *s, char *fmt_string, enum field_types *ft)
                  }
 
                  ft++;
-                 p++;
-                 start = p;
-               } else {
+                 start = ++p;
+               }
+
+               else if(*p == '\\') {
+                       ++p;
+                       if(! *p) tmp[0] = '\\'; // last char is a '\' ?
+                       else if(*p == 'n') *tmp = '\n';
+                       else if(*p == 't') *tmp = '\t';
+                       else if(*p == 'r') *tmp = '\r';
+                       else if(*p == 'v') *tmp = '\v';
+                       else if(*p == 'b') *tmp = '\b';
+                       else if(*p == 'a') *tmp = '\a';
+                       else *tmp = *p;
+                       strncat(fmt_string, tmp, 1);
+                       start = ++p;
+               }
+
+               // if no '\' following: quick mode using strchr/strncat
+               else if(! strchr(start, '\\')) {
                  p = strchr(start, '{');
-                 if(p && *p) {
+                 if(p) { // copy until the next placeholder
                    strncat(fmt_string, start, (size_t)(p-start));
                    start = p;
                  }
-                 else {
+                 else { // copy till the end
                    strncat( fmt_string,
                             start,
                             FORMAT_STRING_LEN - strlen(fmt_string) - 1 );
                    break;
                  }
                }
+
+               // otherwise character by character
+               else {
+                       strncat(fmt_string, p, 1);
+                       start = ++p;
+               }
        }
-       *ft = 66;
+
+       *ft = ITEM_FIELDS;
+       return;
+
+ cannotparse:
+       fprintf(stderr, _("%s: invalid format, index %ld\n"), __FUNCTION__, (start - s));
+       free(fmt_string);
+       free(ft);
+       exit(EXIT_FAILURE);
 }
 
 static int
@@ -2615,7 +2681,7 @@ custom_export_item(FILE *out, int item, char *fmt, enum field_types *ft)
   // we first check that all fields exist before continuing
   if(*fmt == '!') {
     enum field_types *ftp = ft;
-    while(*ft != 66) {
+    while(*ft != ITEM_FIELDS) {
       if(! db_fget(item, *ft) )
        return 1;
       ft++;
@@ -2629,7 +2695,7 @@ custom_export_item(FILE *out, int item, char *fmt, enum field_types *ft)
       fprintf(out, "%s", safe_str(db_fget(item, *ft)));
       ft++;
       fmt+=2;
-    } else if (*ft == 66) {
+    } else if (*ft == ITEM_FIELDS) {
       fprintf(out, "%s", fmt);
       return 0;
     } else {
@@ -2657,11 +2723,10 @@ extern char custom_format[FORMAT_STRING_LEN];
 static int
 custom_export_database(FILE *out, struct db_enumerator e)
 {
-       char *format_string =
-         (char *)malloc(FORMAT_STRING_LEN * sizeof(char*));
+       char *format_string = (char *)malloc(FORMAT_STRING_LEN);
 
        enum field_types *ft =
-         (enum field_types *)malloc(FORMAT_STRING_MAX_FIELDS * sizeof(enum field_types *));
+         (enum field_types *)malloc(FORMAT_STRING_MAX_FIELDS * sizeof(enum field_types));
 
        parse_custom_format(custom_format, format_string, ft);