/* * Copyright (C) 2000 Helix Code Inc. * * Authors: Michael Zucchi * * This program is free software; you can redistribute it and/or * modify it under the terms of the GNU Library General Public License * as published by the Free Software Foundation; either version 2 of * the License, or (at your option) any later version. * * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU Library General Public License for more details. * * You should have received a copy of the GNU Library General Public * License along with this program; if not, write to the Free Software * Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA. */ #include #include #include "camel-mime-utils.h" #include "camel-internet-address.h" #define d(x) static int internet_decode (CamelAddress *, const char *raw); static char * internet_encode (CamelAddress *); static int internet_unformat (CamelAddress *, const char *raw); static char * internet_format (CamelAddress *); static int internet_cat (CamelAddress *dest, const CamelAddress *source); static void internet_remove (CamelAddress *, int index); static void camel_internet_address_class_init (CamelInternetAddressClass *klass); static void camel_internet_address_init (CamelInternetAddress *obj); static CamelAddressClass *camel_internet_address_parent; struct _address { char *name; char *address; }; static void camel_internet_address_class_init(CamelInternetAddressClass *klass) { CamelAddressClass *address = (CamelAddressClass *) klass; camel_internet_address_parent = CAMEL_ADDRESS_CLASS(camel_type_get_global_classfuncs(camel_address_get_type())); address->decode = internet_decode; address->encode = internet_encode; address->unformat = internet_unformat; address->format = internet_format; address->remove = internet_remove; address->cat = internet_cat; } static void camel_internet_address_init(CamelInternetAddress *obj) { } CamelType camel_internet_address_get_type(void) { static CamelType type = CAMEL_INVALID_TYPE; if (type == CAMEL_INVALID_TYPE) { type = camel_type_register(camel_address_get_type(), "CamelInternetAddress", sizeof (CamelInternetAddress), sizeof (CamelInternetAddressClass), (CamelObjectClassInitFunc) camel_internet_address_class_init, NULL, (CamelObjectInitFunc) camel_internet_address_init, NULL); } return type; } static int internet_decode (CamelAddress *a, const char *raw) { struct _header_address *ha, *n; int count = a->addresses->len; /* Should probably use its own decoder or something */ ha = header_address_decode(raw); if (ha) { n = ha; while (n) { if (n->type == HEADER_ADDRESS_NAME) { camel_internet_address_add((CamelInternetAddress *)a, n->name, n->v.addr); } else if (n->type == HEADER_ADDRESS_GROUP) { struct _header_address *g = n->v.members; while (g) { if (g->type == HEADER_ADDRESS_NAME) camel_internet_address_add((CamelInternetAddress *)a, g->name, g->v.addr); /* otherwise, it's an error, infact */ g = g->next; } } n = n->next; } header_address_list_clear(&ha); } return a->addresses->len - count; } static char * internet_encode (CamelAddress *a) { int i; GString *out; char *ret; int len = 6; /* "From: ", assume longer of the address headers */ if (a->addresses->len == 0) return NULL; out = g_string_new(""); for (i = 0;i < a->addresses->len; i++) { struct _address *addr = g_ptr_array_index(a->addresses, i); char *enc; if (i != 0) g_string_append(out, ", "); enc = camel_internet_address_encode_address(&len, addr->name, addr->address); g_string_sprintfa(out, "%s", enc); g_free(enc); } ret = out->str; g_string_free(out, FALSE); return ret; } static int internet_unformat(CamelAddress *a, const char *raw) { char *buffer, *p, *name, *addr; int c; int count = a->addresses->len; if (raw == NULL) return 0; d(printf("unformatting address: %s\n", raw)); /* we copy, so we can modify as we go */ buffer = g_strdup(raw); /* this can be simpler than decode, since there are much fewer rules */ p = buffer; name = NULL; addr = p; do { c = (unsigned char)*p++; switch (c) { /* removes quotes, they should only be around the total name anyway */ case '"': p[-1] = ' '; while (*p) if (*p == '"') { *p++ = ' '; break; } else { p++; } break; case '<': if (name == NULL) name = addr; addr = p; addr[-1] = 0; while (*p && *p != '>') p++; if (*p == 0) break; p++; /* falls through */ case ',': p[-1] = 0; /* falls through */ case 0: if (name) name = g_strstrip(name); addr = g_strstrip(addr); if (addr[0]) { d(printf("found address: '%s' <%s>\n", name, addr)); camel_internet_address_add((CamelInternetAddress *)a, name, addr); } name = NULL; addr = p; break; } } while (c); g_free(buffer); return a->addresses->len - count; } static char * internet_format (CamelAddress *a) { int i; GString *out; char *ret; if (a->addresses->len == 0) return NULL; out = g_string_new(""); for (i = 0;i < a->addresses->len; i++) { struct _address *addr = g_ptr_array_index(a->addresses, i); char *enc; if (i != 0) g_string_append(out, ", "); enc = camel_internet_address_format_address(addr->name, addr->address); g_string_sprintfa(out, "%s", enc); g_free(enc); } ret = out->str; g_string_free(out, FALSE); return ret; } static int internet_cat (CamelAddress *dest, const CamelAddress *source) { int i; g_assert(CAMEL_IS_INTERNET_ADDRESS(source)); for (i=0;iaddresses->len;i++) { struct _address *addr = g_ptr_array_index(source->addresses, i); camel_internet_address_add((CamelInternetAddress *)dest, addr->name, addr->address); } return i; } static void internet_remove (CamelAddress *a, int index) { struct _address *addr; if (index < 0 || index >= a->addresses->len) return; addr = g_ptr_array_index(a->addresses, index); g_free(addr->name); g_free(addr->address); g_free(addr); g_ptr_array_remove_index(a->addresses, index); } /** * camel_internet_address_new: * * Create a new CamelInternetAddress object. * * Return value: A new CamelInternetAddress object. **/ CamelInternetAddress * camel_internet_address_new (void) { CamelInternetAddress *new = CAMEL_INTERNET_ADDRESS(camel_object_new(camel_internet_address_get_type())); return new; } /** * camel_internet_address_add: * @a: internet address object * @name: * @address: * * Add a new internet address to the address object. * * Return value: Index of added entry. **/ int camel_internet_address_add (CamelInternetAddress *a, const char *name, const char *address) { struct _address *new; int index; g_assert(CAMEL_IS_INTERNET_ADDRESS(a)); new = g_malloc(sizeof(*new)); new->name = g_strdup(name); new->address = g_strdup(address); index = ((CamelAddress *)a)->addresses->len; g_ptr_array_add(((CamelAddress *)a)->addresses, new); return index; } /** * camel_internet_address_get: * @a: internet address object * @index: address's array index * @namep: Holder for the returned name, or NULL, if not required. * @addressp: Holder for the returned address, or NULL, if not required. * * Get the address at @index. * * Return value: TRUE if such an address exists, or FALSE otherwise. **/ gboolean camel_internet_address_get (const CamelInternetAddress *a, int index, const char **namep, const char **addressp) { struct _address *addr; g_assert(CAMEL_IS_INTERNET_ADDRESS(a)); if (index < 0 || index >= ((CamelAddress *)a)->addresses->len) return FALSE; addr = g_ptr_array_index( ((CamelAddress *)a)->addresses, index); if (namep) *namep = addr->name; if (addressp) *addressp = addr->address; return TRUE; } /** * camel_internet_address_find_name: * @a: * @name: * @addressp: Holder for address part, or NULL, if not required. * * Find address by real name. * * Return value: The index of the address matching the name, or -1 * if no match was found. **/ int camel_internet_address_find_name(CamelInternetAddress *a, const char *name, const char **addressp) { struct _address *addr; int i, len; g_assert(CAMEL_IS_INTERNET_ADDRESS(a)); len = ((CamelAddress *)a)->addresses->len; for (i=0;iaddresses, i ); if (!strcmp(addr->name, name)) { if (addressp) *addressp = addr->address; return i; } } return -1; } /** * camel_internet_address_find_address: * @a: * @address: * @namep: Return for the matching name, or NULL, if not required. * * Find an address by address. * * Return value: The index of the address, or -1 if not found. **/ int camel_internet_address_find_address(CamelInternetAddress *a, const char *address, const char **namep) { struct _address *addr; int i, len; g_assert(CAMEL_IS_INTERNET_ADDRESS(a)); len = ((CamelAddress *)a)->addresses->len; for (i=0;iaddresses, i ); if (!strcmp(addr->address, address)) { if (namep) *namep = addr->name; return i; } } return -1; } /** * camel_internet_address_encode_address: * @len: The encoded length so far, of this line * @name: * @addr: * * Encode a single address ready for internet usage. Header folding * as per rfc 822 is also performed, based on the length in len. * * Return value: The encoded address. **/ char * camel_internet_address_encode_address(int *inlen, const char *real, const char *addr) { char *name = header_encode_phrase(real); char *ret = NULL, *addra = NULL; int len = *inlen; GString *out = g_string_new(""); g_assert(addr); if (name && name[0]) { if (strlen(name) + len > CAMEL_FOLD_SIZE) { char *folded = header_fold(name, len); char *last; g_string_append(out, folded); g_free(folded); last = strrchr(out->str, '\n'); if (last) len = last-(out->str+out->len); else len = out->len; } else { g_string_append(out, name); len += strlen(name); } addr = addra = g_strdup_printf(" <%s>", addr); } /* NOTE: Strictly speaking, we could and should split the * internal address up if we need to, on atom or specials * boundaries - however, to aid interoperability with mailers * that will probably not handle this case, we will just move * the whole address to its own line */ if (strlen(addr) + len > CAMEL_FOLD_SIZE) { g_string_append(out, "\n\t"); g_string_append(out, addr); len = strlen(addr)+1; } else { g_string_append(out, addr); len += strlen(addr); } *inlen = len; #if 0 if (name && name[0]) ret = g_strdup_printf("%s <%s>", name, addr); else ret = g_strdup_printf("%s", addr); #endif g_free(name); g_free(addra); ret = out->str; g_string_free(out, FALSE); return ret; } /** * camel_internet_address_format_address: * @name: A name, quotes may be stripped from it. * @addr: Assumes a valid rfc822 email address. * * Function to format a single address, suitable for display. * * Return value: **/ char * camel_internet_address_format_address(const char *name, const char *addr) { char *ret = NULL; g_assert(addr); if (name && name[0]) { const char *p = name; char *o, c; while ((c = *p++)) { if (c == '\"' || c == ',') { o = ret = g_malloc(strlen(name)+3+strlen(addr)+3 + 1); p = name; *o++ = '\"'; while ((c = *p++)) if (c != '\"') *o++ = c; *o++ = '\"'; sprintf(o, " <%s>", addr); d(printf("encoded '%s' => '%s'\n", name, ret)); return ret; } } ret = g_strdup_printf("%s <%s>", name, addr); } else ret = g_strdup(addr); return ret; }