extern abook_field_list *fields_list;
extern int fields_count;
+// see also enum field_types @database.h
+extern abook_field standard_fields[];
/*
* function declarations
#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, ...
continue; /* just skip the errors */
}
- ldif_fix_string(value);
-
ldif_convert(item, type, value);
xfree(line);
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
*/
ldif_export_database(FILE *out, struct db_enumerator e)
{
char email[MAX_EMAILSTR_LEN];
+ abook_list *emails, *em;
fprintf(out, "version: 1\n");
for(j = 0; j < ITEM_FIELDS; j++) {
if(j == EMAIL) {
- if(*email) // don't dump an empty email field
- ldif_fput_type_and_value(out,
- ldif_field_names[j],
- email);
+ if(*email) {
+ tmp = db_email_get(e.item);
+ emails = csv_to_abook_list(tmp);
+ free(tmp);
+ for(em = emails; em; em = em->next)
+ ldif_fput_type_and_value(out,
+ ldif_field_names[EMAIL],
+ em->data);
+ }
}
else if(db_fget(e.item,j)) {
ldif_fput_type_and_value(out,
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, " ");
+ 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);
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, " ");
+ 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);
}
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>");
}
/*
* 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
"FN", /* FORMATTED NAME */
"EMAIL", /* EMAIL */
"ADR", /* ADDRESS */
- "ADR", /* ADDRESS2 - not used */
+ "ADR", /* ADDRESS2 */
"ADR", /* CITY */
"ADR", /* STATE */
"ADR", /* ZIP */
"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 {
}
}
+
+/*
+ * 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
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])]), '\"')) ?
* 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,
PHONE,
WORKPHONE,
FAX,
- MOBILEPHONE,
+ MOBILEPHONE, // spelt "mobile" in standard_fields
NICK,
URL,
NOTES,
};
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);
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",
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",
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);
}
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 */
-};
+/*
+ * end of BSD calendar export filter
+ */
+
+/*
+ * custom export filter
+ */
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
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);
}
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(ft);
+ exit(EXIT_FAILURE);
}
static int
custom_export_item(FILE *out, int item, char *s, enum field_types *ft);
-// used to store the format string from --outformatstr when "custom" format is used
-// default value overriden in export_file()
-extern char *parsed_custom_format;
-extern enum field_types *custom_format_fields;
+// stores the format string generated from --outformatstr {custom_format}
+// (when "custom" output format is requested)
+// overrides default value of custom_format set by from abook.c
+extern char custom_format[FORMAT_STRING_LEN];
+char parsed_custom_format[FORMAT_STRING_LEN];
+enum field_types *custom_format_fields = 0;
/* wrapper for custom_export_item:
1) avoid messing with extern pointer
// 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++;
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 {
return 0;
}
-// used to store the format string from --outformatstr when "custom" format is used
-// default value overriden from abook.c
-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*));
-
enum field_types *ft =
- (enum field_types *)malloc(FORMAT_STRING_MAX_FIELDS * sizeof(enum field_types *));
-
- parse_custom_format(custom_format, format_string, ft);
+ (enum field_types *)malloc(FORMAT_STRING_MAX_FIELDS * sizeof(enum field_types));
+ parse_custom_format(custom_format, (char*)&parsed_custom_format, ft);
db_enumerate_items(e) {
- if(custom_export_item(out, e.item, format_string, ft) == 0)
+ if(custom_export_item(out, e.item, (char*)&parsed_custom_format, ft) == 0)
fprintf(out, "\n");
}
return 0;
}
/*
- * end of BSD calendar export filter
+ * end of custom export filter
*/
-