diff options
Diffstat (limited to 'addressbook/backend/ebook/e-vcard.c')
-rw-r--r-- | addressbook/backend/ebook/e-vcard.c | 488 |
1 files changed, 465 insertions, 23 deletions
diff --git a/addressbook/backend/ebook/e-vcard.c b/addressbook/backend/ebook/e-vcard.c index 62ebda9349..7ea1d9da84 100644 --- a/addressbook/backend/ebook/e-vcard.c +++ b/addressbook/backend/ebook/e-vcard.c @@ -1,5 +1,5 @@ /* -*- Mode: C; tab-width: 8; indent-tabs-mode: t; c-basic-offset: 8 -*- */ -/* evcard.h +/* e-vcard.c * * Copyright (C) 2003 Ximian, Inc. * @@ -22,11 +22,18 @@ #include <glib.h> #include <stdio.h> +#include <string.h> #include <ctype.h> #include "e-vcard.h" #define CRLF "\r\n" +typedef enum { + EVC_ENCODING_RAW, /* no encoding */ + EVC_ENCODING_BASE64, /* base64 */ + EVC_ENCODING_QP /* quoted-printable */ +} EVCardEncoding; + struct _EVCardPrivate { GList *attributes; }; @@ -36,6 +43,9 @@ struct _EVCardAttribute { char *name; GList *params; /* EVCardParam */ GList *values; + GList *decoded_values; + EVCardEncoding encoding; + gboolean encoding_set; }; struct _EVCardAttributeParam { @@ -45,6 +55,12 @@ struct _EVCardAttributeParam { static GObjectClass *parent_class; +static void _evc_base64_init(void); +static size_t _evc_base64_encode_step(unsigned char *in, size_t len, gboolean break_lines, unsigned char *out, int *state, int *save); +static size_t _evc_base64_decode_step(unsigned char *in, size_t len, unsigned char *out, int *state, unsigned int *save); +size_t _evc_base64_decode_simple (char *data, size_t len); +char *_evc_base64_encode_simple (const char *data, size_t len); + static void e_vcard_dispose (GObject *object) { @@ -73,6 +89,8 @@ e_vcard_class_init (EVCardClass *klass) parent_class = g_type_class_ref (G_TYPE_OBJECT); object_class->dispose = e_vcard_dispose; + + _evc_base64_init(); } static void @@ -229,9 +247,9 @@ read_attribute_value (EVCardAttribute *attr, char **p, gboolean quoted_printable g_warning ("invalid escape, passing it through"); str = g_string_append_c (str, '\\'); str = g_string_append_unichar (str, g_utf8_get_char(lp)); - lp = g_utf8_next_char(lp); break; } + lp = g_utf8_next_char(lp); } else if (*lp == ';') { e_vcard_attribute_add_value (attr, g_string_free (str, FALSE)); @@ -480,14 +498,16 @@ parse (EVCard *evc, const char *str) *end = '\0'; } +#if DEBUG_FOLDING printf ("BEFORE FOLDING:\n"); printf (str); - +#endif buf = fold_lines (buf); +#if DEBUG_FOLDING printf ("\n\nAFTER FOLDING:\n"); printf (buf); - +#endif p = buf; attr = read_attribute (&p); @@ -508,6 +528,8 @@ parse (EVCard *evc, const char *str) if (!attr || attr->group || g_ascii_strcasecmp (attr->name, "end")) { g_warning ("vcard ended without END:VCARD\n"); } + + g_free (buf); } static char* @@ -517,7 +539,7 @@ escape_string (const char *s) const char *p; /* Escape a string as described in RFC2426, section 5 */ - for (p = s; *p; p++) { + for (p = s; p && *p; p++) { switch (*p) { case '\n': str = g_string_append (str, "\\n"); @@ -579,24 +601,42 @@ unescape_string (const char *s) } #endif +void +e_vcard_construct (EVCard *evc, const char *str) +{ + if (*str) + parse (evc, str); +} + EVCard * e_vcard_new () { - return g_object_new (E_TYPE_VCARD, NULL); + return e_vcard_new_from_string (""); } EVCard * e_vcard_new_from_string (const char *str) { - EVCard *evc = e_vcard_new (); + EVCard *evc; - parse (evc, str); + g_return_val_if_fail (str, NULL); + + evc = g_object_new (E_TYPE_VCARD, NULL); + + e_vcard_construct (evc, str); return evc; } -char* -e_vcard_to_string (EVCard *evc) +static char* +e_vcard_to_string_vcard_21 (EVCard *evc) +{ + g_warning ("need to implement e_vcard_to_string_vcard_21"); + return g_strdup (""); +} + +static char* +e_vcard_to_string_vcard_30 (EVCard *evc) { GList *l; GList *v; @@ -680,6 +720,20 @@ e_vcard_to_string (EVCard *evc) return g_string_free (str, FALSE); } +char* +e_vcard_to_string (EVCard *evc, EVCardFormat format) +{ + switch (format) { + case EVC_FORMAT_VCARD_21: + return e_vcard_to_string_vcard_21 (evc); + case EVC_FORMAT_VCARD_30: + return e_vcard_to_string_vcard_30 (evc); + default: + g_warning ("invalid format specifier passed to e_vcard_to_string"); + return g_strdup (""); + } +} + void e_vcard_dump_structure (EVCard *evc) { @@ -732,24 +786,62 @@ e_vcard_attribute_new (const char *attr_group, const char *attr_name) void e_vcard_attribute_free (EVCardAttribute *attr) { - GList *p; - g_free (attr->group); g_free (attr->name); - g_list_foreach (attr->values, (GFunc)g_free, NULL); - g_list_free (attr->values); + e_vcard_attribute_remove_values (attr); + + e_vcard_attribute_remove_params (attr); + + g_free (attr); +} + +EVCardAttribute* +e_vcard_attribute_copy (EVCardAttribute *attr) +{ + EVCardAttribute *a = e_vcard_attribute_new (e_vcard_attribute_get_group (attr), + e_vcard_attribute_get_name (attr)); + GList *p; + + for (p = attr->values; p; p = p->next) + e_vcard_attribute_add_value (a, p->data); + + for (p = attr->params; p; p = p->next) + e_vcard_attribute_add_param (a, e_vcard_attribute_param_copy (p->data)); + + return a; +} + +void +e_vcard_remove_attributes (EVCard *evc, const char *attr_group, const char *attr_name) +{ + GList *attr; + + attr = evc->priv->attributes; + while (attr) { + GList *next_attr; + EVCardAttribute *a = attr->data; + + next_attr = attr->next; + + if (((!attr_group && !a->group) || !g_ascii_strcasecmp (attr_group, a->group)) && + ((!attr_name && !a->name) || !g_ascii_strcasecmp (attr_name, a->name))) { + + /* matches, remove/delete the attribute */ + evc->priv->attributes = g_list_remove_link (evc->priv->attributes, attr); - for (p = attr->params; p; p = p->next) { - EVCardAttributeParam *param = p->data; + e_vcard_attribute_free (a); + } - g_free (param->name); - g_list_foreach (param->values, (GFunc)g_free, NULL); - g_list_free (param->values); - g_free (param); + attr = next_attr; } +} - g_free (attr); +void +e_vcard_remove_attribute (EVCard *evc, EVCardAttribute *attr) +{ + evc->priv->attributes = g_list_remove (evc->priv->attributes, attr); + e_vcard_attribute_free (attr); } void @@ -791,6 +883,33 @@ e_vcard_attribute_add_value (EVCardAttribute *attr, const char *value) } void +e_vcard_attribute_add_value_decoded (EVCardAttribute *attr, const char *value, int len) +{ + switch (attr->encoding) { + case EVC_ENCODING_RAW: + g_warning ("can't add_value_decoded with an attribute using RAW encoding. you must set the ENCODING parameter first"); + break; + case EVC_ENCODING_BASE64: { + char *b64_data = _evc_base64_encode_simple (value, len); + GString *decoded = g_string_new_len (value, len); + + /* make sure the decoded list is up to date */ + e_vcard_attribute_get_values_decoded (attr); + + printf ("base64 encoded value: %s\n", b64_data); + printf ("original length: %d\n", len); + + attr->values = g_list_append (attr->values, b64_data); + attr->decoded_values = g_list_append (attr->decoded_values, decoded); + break; + } + case EVC_ENCODING_QP: + g_warning ("need to implement quoted printable decoding"); + break; + } +} + +void e_vcard_attribute_add_values (EVCardAttribute *attr, ...) { @@ -806,6 +925,21 @@ e_vcard_attribute_add_values (EVCardAttribute *attr, va_end (ap); } +void +e_vcard_attribute_remove_values (EVCardAttribute *attr) +{ + g_list_foreach (attr->values, (GFunc)g_free, NULL); + g_list_free (attr->values); + attr->values = NULL; +} + +void +e_vcard_attribute_remove_params (EVCardAttribute *attr) +{ + g_list_foreach (attr->params, (GFunc)e_vcard_attribute_param_free, NULL); + g_list_free (attr->params); + attr->params = NULL; +} EVCardAttributeParam* e_vcard_attribute_param_new (const char *name) @@ -820,16 +954,55 @@ void e_vcard_attribute_param_free (EVCardAttributeParam *param) { g_free (param->name); - g_list_foreach (param->values, (GFunc)g_free, NULL); - g_list_free (param->values); + + e_vcard_attribute_param_remove_values (param); + g_free (param); } +EVCardAttributeParam* +e_vcard_attribute_param_copy (EVCardAttributeParam *param) +{ + EVCardAttributeParam *p = e_vcard_attribute_param_new (e_vcard_attribute_param_get_name (param)); + GList *l; + + for (l = param->values; l; l = l->next) { + e_vcard_attribute_param_add_value (p, l->data); + } + + return p; +} + void e_vcard_attribute_add_param (EVCardAttribute *attr, EVCardAttributeParam *param) { attr->params = g_list_append (attr->params, param); + + /* we handle our special encoding stuff here */ + + if (!g_ascii_strcasecmp (param->name, EVC_ENCODING)) { + if (attr->encoding_set) { + g_warning ("ENCODING specified twice"); + return; + } + + if (param->values && param->values->data) { + if (!g_ascii_strcasecmp ((char*)param->values->data, "b")) + attr->encoding = EVC_ENCODING_BASE64; + else if (!g_ascii_strcasecmp ((char*)param->values->data, EVC_QUOTEDPRINTABLE)) + attr->encoding = EVC_ENCODING_QP; + else { + g_warning ("Unknown value `%s' for ENCODING parameter. values will be treated as raw", + (char*)param->values->data); + } + + attr->encoding_set = TRUE; + } + else { + g_warning ("ENCODING parameter added with no value"); + } + } } void @@ -882,6 +1055,14 @@ e_vcard_attribute_add_param_with_values (EVCardAttribute *attr, e_vcard_attribute_add_param (attr, param); } +void +e_vcard_attribute_param_remove_values (EVCardAttributeParam *param) +{ + g_list_foreach (param->values, (GFunc)g_free, NULL); + g_list_free (param->values); + param->values = NULL; +} + GList* e_vcard_get_attributes (EVCard *evcard) { @@ -907,6 +1088,33 @@ e_vcard_attribute_get_values (EVCardAttribute *attr) } GList* +e_vcard_attribute_get_values_decoded (EVCardAttribute *attr) +{ + if (!attr->decoded_values) { + GList *l; + switch (attr->encoding) { + case EVC_ENCODING_RAW: + for (l = attr->values; l; l = l->next) + attr->decoded_values = g_list_append (attr->decoded_values, g_string_new ((char*)l->data)); + break; + case EVC_ENCODING_BASE64: + for (l = attr->values; l; l = l->next) { + char *decoded = g_strdup ((char*)l->data); + int len = _evc_base64_decode_simple (decoded, strlen (decoded)); + attr->decoded_values = g_list_append (attr->decoded_values, g_string_new_len (decoded, len)); + g_free (decoded); + } + break; + case EVC_ENCODING_QP: + g_warning ("need to implement quoted printable decoding"); + break; + } + } + + return attr->decoded_values; +} + +GList* e_vcard_attribute_get_params (EVCardAttribute *attr) { return attr->params; @@ -923,3 +1131,237 @@ e_vcard_attribute_param_get_values (EVCardAttributeParam *param) { return param->values; } + + + +/* encoding/decoding stuff ripped from camel-mime-utils.c */ + +static char *_evc_base64_alphabet = +"ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789+/"; + +static unsigned char _evc_base64_rank[256]; + +static void +_evc_base64_init(void) +{ + int i; + + memset(_evc_base64_rank, 0xff, sizeof(_evc_base64_rank)); + for (i=0;i<64;i++) { + _evc_base64_rank[(unsigned int)_evc_base64_alphabet[i]] = i; + } + _evc_base64_rank['='] = 0; +} + +/* call this when finished encoding everything, to + flush off the last little bit */ +static size_t +_evc_base64_encode_close(unsigned char *in, size_t inlen, gboolean break_lines, unsigned char *out, int *state, int *save) +{ + int c1, c2; + unsigned char *outptr = out; + + if (inlen>0) + outptr += _evc_base64_encode_step(in, inlen, break_lines, outptr, state, save); + + c1 = ((unsigned char *)save)[1]; + c2 = ((unsigned char *)save)[2]; + +#if 0 + d(printf("mode = %d\nc1 = %c\nc2 = %c\n", + (int)((char *)save)[0], + (int)((char *)save)[1], + (int)((char *)save)[2])); +#endif + + switch (((char *)save)[0]) { + case 2: + outptr[2] = _evc_base64_alphabet[ ( (c2 &0x0f) << 2 ) ]; + g_assert(outptr[2] != 0); + goto skip; + case 1: + outptr[2] = '='; + skip: + outptr[0] = _evc_base64_alphabet[ c1 >> 2 ]; + outptr[1] = _evc_base64_alphabet[ c2 >> 4 | ( (c1&0x3) << 4 )]; + outptr[3] = '='; + outptr += 4; + break; + } + if (break_lines) + *outptr++ = '\n'; + + *save = 0; + *state = 0; + + return outptr-out; +} + +/* + performs an 'encode step', only encodes blocks of 3 characters to the + output at a time, saves left-over state in state and save (initialise to + 0 on first invocation). +*/ +static size_t +_evc_base64_encode_step(unsigned char *in, size_t len, gboolean break_lines, unsigned char *out, int *state, int *save) +{ + register unsigned char *inptr, *outptr; + + if (len<=0) + return 0; + + inptr = in; + outptr = out; + +#if 0 + d(printf("we have %d chars, and %d saved chars\n", len, ((char *)save)[0])); +#endif + + if (len + ((char *)save)[0] > 2) { + unsigned char *inend = in+len-2; + register int c1, c2, c3; + register int already; + + already = *state; + + switch (((char *)save)[0]) { + case 1: c1 = ((unsigned char *)save)[1]; goto skip1; + case 2: c1 = ((unsigned char *)save)[1]; + c2 = ((unsigned char *)save)[2]; goto skip2; + } + + /* yes, we jump into the loop, no i'm not going to change it, it's beautiful! */ + while (inptr < inend) { + c1 = *inptr++; + skip1: + c2 = *inptr++; + skip2: + c3 = *inptr++; + *outptr++ = _evc_base64_alphabet[ c1 >> 2 ]; + *outptr++ = _evc_base64_alphabet[ c2 >> 4 | ( (c1&0x3) << 4 ) ]; + *outptr++ = _evc_base64_alphabet[ ( (c2 &0x0f) << 2 ) | (c3 >> 6) ]; + *outptr++ = _evc_base64_alphabet[ c3 & 0x3f ]; + /* this is a bit ugly ... */ + if (break_lines && (++already)>=19) { + *outptr++='\n'; + already = 0; + } + } + + ((char *)save)[0] = 0; + len = 2-(inptr-inend); + *state = already; + } + +#if 0 + d(printf("state = %d, len = %d\n", + (int)((char *)save)[0], + len)); +#endif + + if (len>0) { + register char *saveout; + + /* points to the slot for the next char to save */ + saveout = & (((char *)save)[1]) + ((char *)save)[0]; + + /* len can only be 0 1 or 2 */ + switch(len) { + case 2: *saveout++ = *inptr++; + case 1: *saveout++ = *inptr++; + } + ((char *)save)[0]+=len; + } + +#if 0 + d(printf("mode = %d\nc1 = %c\nc2 = %c\n", + (int)((char *)save)[0], + (int)((char *)save)[1], + (int)((char *)save)[2])); +#endif + + return outptr-out; +} + + +/** + * base64_decode_step: decode a chunk of base64 encoded data + * @in: input stream + * @len: max length of data to decode + * @out: output stream + * @state: holds the number of bits that are stored in @save + * @save: leftover bits that have not yet been decoded + * + * Decodes a chunk of base64 encoded data + **/ +static size_t +_evc_base64_decode_step(unsigned char *in, size_t len, unsigned char *out, int *state, unsigned int *save) +{ + register unsigned char *inptr, *outptr; + unsigned char *inend, c; + register unsigned int v; + int i; + + inend = in+len; + outptr = out; + + /* convert 4 base64 bytes to 3 normal bytes */ + v=*save; + i=*state; + inptr = in; + while (inptr<inend) { + c = _evc_base64_rank[*inptr++]; + if (c != 0xff) { + v = (v<<6) | c; + i++; + if (i==4) { + *outptr++ = v>>16; + *outptr++ = v>>8; + *outptr++ = v; + i=0; + } + } + } + + *save = v; + *state = i; + + /* quick scan back for '=' on the end somewhere */ + /* fortunately we can drop 1 output char for each trailing = (upto 2) */ + i=2; + while (inptr>in && i) { + inptr--; + if (_evc_base64_rank[*inptr] != 0xff) { + if (*inptr == '=' && outptr>out) + outptr--; + i--; + } + } + + /* if i!= 0 then there is a truncation error! */ + return outptr-out; +} + +char * +_evc_base64_encode_simple (const char *data, size_t len) +{ + unsigned char *out; + int state = 0, outlen; + unsigned int save = 0; + + out = g_malloc (len * 4 / 3 + 5); + outlen = _evc_base64_encode_close ((unsigned char *)data, len, FALSE, + out, &state, &save); + out[outlen] = '\0'; + return (char *)out; +} + +size_t +_evc_base64_decode_simple (char *data, size_t len) +{ + int state = 0; + unsigned int save = 0; + + return _evc_base64_decode_step ((unsigned char *)data, len, + (unsigned char *)data, &state, &save); +} |