[balsa] Add files required by last commit
- From: Peter Bloomfield <peterb src gnome org>
- To: commits-list gnome org
- Cc:
- Subject: [balsa] Add files required by last commit
- Date: Thu, 10 Nov 2016 04:23:58 +0000 (UTC)
commit 2b205aafa518661488b3772591cd37e333053e83
Author: Peter Bloomfield <PeterBloomfield bellsouth net>
Date: Wed Nov 9 23:22:42 2016 -0500
Add files required by last commit
new file: libbalsa/address-book-osmo.c
new file: libbalsa/address-book-osmo.h
new file: libbalsa/rfc6350.c
new file: libbalsa/rfc6350.h
libbalsa/address-book-osmo.c | 355 ++++++++++++++++++++++++++++++++++++++++++
libbalsa/address-book-osmo.h | 58 +++++++
libbalsa/rfc6350.c | 353 +++++++++++++++++++++++++++++++++++++++++
libbalsa/rfc6350.h | 45 ++++++
4 files changed, 811 insertions(+), 0 deletions(-)
---
diff --git a/libbalsa/address-book-osmo.c b/libbalsa/address-book-osmo.c
new file mode 100644
index 0000000..e5028cf
--- /dev/null
+++ b/libbalsa/address-book-osmo.c
@@ -0,0 +1,355 @@
+/* -*-mode:c; c-style:k&r; c-basic-offset:4; -*- */
+/* Balsa E-Mail Client
+ *
+ * Copyright (C) 1997-2016 Stuart Parmenter and others,
+ * See the file AUTHORS for a list.
+ *
+ * Osmo address book support has been written by Copyright (C) 2016
+ * Albrecht Dreß <albrecht dress arcor de>.
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 2, 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 General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, see <http://www.gnu.org/licenses/>.
+ */
+
+/*
+ * Osmo address book
+ *
+ * Add remarks here...
+ */
+#if defined(HAVE_CONFIG_H) && HAVE_CONFIG_H
+# include "config.h"
+#endif /* HAVE_CONFIG_H */
+
+#if defined(HAVE_OSMO)
+
+#include <string.h>
+#include <glib.h>
+#include <gio/gio.h>
+#include <glib/gi18n.h>
+
+#include "address-book.h"
+#include "rfc6350.h"
+#include "address-book-osmo.h"
+
+
+/* for the time being, osmo svn rev. 1099 accepts only reading via DBus, not writing new or modified records
*/
+#undef OSMO_CAN_WRITE
+
+
+#define LOOKUP_MIN_LEN 2U
+
+
+static void libbalsa_address_book_osmo_finalize(GObject *object);
+static LibBalsaABErr libbalsa_address_book_osmo_load(LibBalsaAddressBook *ab,
+ const gchar *filter,
+ LibBalsaAddressBookLoadFunc callback,
+ gpointer closure);
+static GList *libbalsa_address_book_osmo_alias_complete(LibBalsaAddressBook *ab,
+ const gchar *prefix);
+static GList *osmo_read_addresses(LibBalsaAddressBookOsmo *osmo,
+ const gchar *filter,
+ GError
**error);
+
+
+G_DEFINE_TYPE(LibBalsaAddressBookOsmo, libbalsa_address_book_osmo, LIBBALSA_TYPE_ADDRESS_BOOK);
+
+
+static void
+libbalsa_address_book_osmo_class_init(LibBalsaAddressBookOsmoClass *klass)
+{
+ LibBalsaAddressBookClass *address_book_class;
+ GObjectClass *object_class;
+
+ object_class = G_OBJECT_CLASS(klass);
+ address_book_class = LIBBALSA_ADDRESS_BOOK_CLASS(klass);
+
+ object_class->finalize = libbalsa_address_book_osmo_finalize;
+
+ address_book_class->load = libbalsa_address_book_osmo_load;
+#if defined(OSMO_CAN_WRITE)
+ address_book_class->add_address = libbalsa_address_book_osmo_add_address;
+ address_book_class->remove_address = libbalsa_address_book_osmo_remove_address;
+ address_book_class->modify_address = libbalsa_address_book_osmo_modify_address;
+#endif
+
+ address_book_class->alias_complete = libbalsa_address_book_osmo_alias_complete;
+}
+
+
+static void
+libbalsa_address_book_osmo_init(LibBalsaAddressBookOsmo *ab)
+{
+ LIBBALSA_ADDRESS_BOOK(ab)->is_expensive = FALSE;
+}
+
+
+static void
+libbalsa_address_book_osmo_finalize(GObject *object)
+{
+ LibBalsaAddressBookOsmo *osmo;
+
+ osmo = LIBBALSA_ADDRESS_BOOK_OSMO(object);
+ if (osmo->proxy != NULL) {
+ g_object_unref(osmo->proxy);
+ osmo->proxy = NULL;
+ }
+
+ G_OBJECT_CLASS(libbalsa_address_book_osmo_parent_class)->finalize(object);
+}
+
+
+LibBalsaAddressBook *
+libbalsa_address_book_osmo_new(const gchar *name)
+{
+ LibBalsaAddressBook *ab = NULL;
+ LibBalsaAddressBookOsmo *osmo;
+
+ osmo = LIBBALSA_ADDRESS_BOOK_OSMO(g_object_new(LIBBALSA_TYPE_ADDRESS_BOOK_OSMO, NULL));
+ ab = LIBBALSA_ADDRESS_BOOK(osmo);
+ ab->name = g_strdup(name);
+
+ return ab;
+}
+
+
+static LibBalsaABErr
+libbalsa_address_book_osmo_load(LibBalsaAddressBook *ab,
+ const gchar *filter,
+ LibBalsaAddressBookLoadFunc callback,
+ gpointer closure)
+{
+ LibBalsaABErr result;
+
+ g_return_val_if_fail (LIBBALSA_IS_ADDRESS_BOOK_OSMO(ab), LBABERR_OK);
+
+ if (callback == NULL) {
+ result = LBABERR_OK;
+ } else {
+ LibBalsaAddressBookOsmo *osmo;
+ GError *error = NULL;
+ GList *addresses;
+
+ osmo = LIBBALSA_ADDRESS_BOOK_OSMO(ab);
+
+ addresses = osmo_read_addresses(osmo, filter, &error);
+ if (error != NULL) {
+ libbalsa_address_book_set_status(ab, g_strdup_printf(_("Reading Osmo contacts failed:
%s"), error->message));
+ g_error_free(error);
+ result = LBABERR_CANNOT_SEARCH;
+ } else {
+ GList *this_addr;
+
+ for (this_addr = addresses; this_addr != NULL; this_addr = this_addr->next) {
+ callback(ab, LIBBALSA_ADDRESS(this_addr->data), closure);
+ }
+ callback(ab, NULL, closure);
+ g_list_free_full(addresses, g_object_unref);
+ libbalsa_address_book_set_status(ab, NULL);
+ result = LBABERR_OK;
+ }
+ }
+
+ return result;
+}
+
+
+/** \brief Utf8-safe strstr
+ *
+ * \param haystack utf8 "haystack" string
+ * \param utf8_needle utf8 "needle" string as returned by g_utf8_casefold()
+ * \return TRUE if needle is found anywhere in haystack
+ */
+static gboolean
+utf8_strstr(const gchar *haystack, const gchar *utf8_needle)
+{
+ gboolean result;
+
+ if (haystack != NULL) {
+ gchar *test;
+
+ test = g_utf8_casefold(haystack, -1);
+ result = (strstr(test, utf8_needle) != NULL);
+ g_free(test);
+ } else {
+ result = FALSE;
+ }
+ return result;
+}
+
+
+/** \brief Check for a pattern in a LibBalsaAddress
+ *
+ * \param address address object
+ * \param utf8_needle utf8 "needle" string as returned by g_utf8_casefold()
+ * \return TRUE if any address string field contains needle
+ *
+ * The fields checked are LibBalsaAddress::full_name, LibBalsaAddress::first_name,
LibBalsaAddress::last_name,
+ * LibBalsaAddress::nick_name and LibBalsaAddress::organization.
+ */
+static inline gboolean
+utf8_lba_strstr(const LibBalsaAddress *address, const gchar *utf8_needle)
+{
+ return utf8_strstr(address->full_name, utf8_needle) ||
+ utf8_strstr(address->first_name, utf8_needle) ||
+ utf8_strstr(address->last_name, utf8_needle) ||
+ utf8_strstr(address->nick_name, utf8_needle) ||
+ utf8_strstr(address->organization, utf8_needle);
+}
+
+
+static GList *
+libbalsa_address_book_osmo_alias_complete(LibBalsaAddressBook *ab,
+ const gchar *prefix)
+{
+ LibBalsaAddressBookOsmo *osmo;
+ GError *error = NULL;
+ GList *addresses;
+ GList *result = NULL;
+
+ g_return_val_if_fail(LIBBALSA_ADDRESS_BOOK_OSMO(ab), NULL);
+
+ osmo = LIBBALSA_ADDRESS_BOOK_OSMO(ab);
+
+ if (!ab->expand_aliases || strlen(prefix) < LOOKUP_MIN_LEN) {
+ return NULL;
+ }
+
+ g_debug("%s: filter for %s", __func__, prefix);
+ addresses = osmo_read_addresses(osmo, prefix, &error);
+ if (error != NULL) {
+ g_warning("%s: cannot read contacts from Osmo: %s", __func__, error->message);
+ g_error_free(error);
+ } else {
+ GList *p;
+ gchar *utf8_filter;
+
+ utf8_filter = g_utf8_casefold(prefix, -1);
+ for (p = addresses; p != NULL; p = p->next) {
+ LibBalsaAddress *this_addr = LIBBALSA_ADDRESS(p->data);
+ GList *this_mail;
+ gboolean names_match;
+
+ names_match = utf8_lba_strstr(this_addr, utf8_filter);
+ for (this_mail = this_addr->address_list; this_mail != NULL; this_mail =
this_mail->next) {
+ const gchar *mail_addr = (gchar *) this_mail->data;
+
+ if (names_match || (strstr(mail_addr, prefix) != NULL)) {
+ InternetAddress *addr;
+
+ g_debug("%s: found %s <%s>", __func__, this_addr->full_name,
mail_addr);
+ addr = internet_address_mailbox_new(this_addr->full_name,
g_strdup(mail_addr));
+ result = g_list_prepend(result, g_object_ref(addr));
+ }
+ }
+ }
+ g_free(utf8_filter);
+ g_list_free_full(addresses, g_object_unref);
+
+ if (result != NULL) {
+ result = g_list_reverse(result);
+ }
+ }
+
+ return result;
+}
+
+
+/** \brief Read filtered addresses from Osmo via DBus
+ *
+ * \param osmo Osmo address book object
+ * \param filter search filter, NULL or "" for all entries
+ * \param error filled with error information on error
+ * \return a list \ref LibBalsaAddress items on success or NULL on error or if no item matches the search
filter
+ *
+ * Create the proxy LibBalsaAddressBookOsmo::proxy if required, and ask Osmo for addresses. Only items with
any mail address are
+ * added to the returned list. The caller can distinguish between an error and an empty query result by
checking if error is not
+ * NULL.
+ *
+ * \note The caller must free the returned list.
+ */
+static GList *
+osmo_read_addresses(LibBalsaAddressBookOsmo *osmo,
+ const gchar *filter,
+ GError **error)
+{
+ GList *addresses = NULL;
+
+ /* connect to DBus unless we already have a proxy */
+ if (osmo->proxy == NULL) {
+ osmo->proxy =
+ g_dbus_proxy_new_for_bus_sync(G_BUS_TYPE_SESSION, G_DBUS_PROXY_FLAGS_NONE, NULL,
"org.clayo.osmo.Contacts",
+ "/org/clayo/osmo/Contacts",
"org.clayo.osmo.Contacts", NULL, error);
+ }
+
+ /* proceed only if we have the proxy */
+ if (osmo->proxy != NULL) {
+ GVariant *request;
+ GVariant *reply;
+
+ if (filter != NULL) {
+ request = g_variant_new("(s)", filter);
+ } else {
+ request = g_variant_new("(s)", "");
+ }
+ reply = g_dbus_proxy_call_sync(osmo->proxy, "Find", request, G_DBUS_CALL_FLAGS_NONE, -1,
NULL, error);
+
+ /* proceed only if we got a reply */
+ if (reply != NULL) {
+ gchar *vcards;
+ GInputStream *stream;
+ GDataInputStream *data;
+ gboolean eos;
+
+ /* create a stream from the VCards */
+ g_variant_get(reply, "(s)", &vcards);
+ stream = g_memory_input_stream_new_from_data(vcards, -1, NULL);
+ data = g_data_input_stream_new(stream);
+ g_data_input_stream_set_newline_type(data, G_DATA_STREAM_NEWLINE_TYPE_CR_LF);
+
+ /* decode all returned VCard's, skip those without email addresses */
+ eos = FALSE;
+ do {
+ LibBalsaAddress *this_addr;
+
+ this_addr = rfc6350_parse_from_stream(data, &eos, error);
+ if (this_addr != NULL) {
+ if (this_addr->address_list != NULL) {
+ addresses = g_list_prepend(addresses, this_addr);
+ } else {
+ g_object_unref(G_OBJECT(this_addr));
+ }
+ }
+ } while (!eos && (*error == NULL));
+
+ /* clean up */
+ g_object_unref(G_OBJECT(data));
+ g_object_unref(G_OBJECT(stream));
+ g_free(vcards);
+ g_variant_unref(reply);
+
+ /* drop list on error, reverse order otherwise */
+ if (addresses != NULL) {
+ if (*error != NULL) {
+ g_list_free_full(addresses, g_object_unref);
+ addresses = NULL;
+ } else {
+ addresses = g_list_reverse(addresses);
+ }
+ }
+ }
+ }
+
+ return addresses;
+}
+
+#endif /* HAVE_OSMO */
diff --git a/libbalsa/address-book-osmo.h b/libbalsa/address-book-osmo.h
new file mode 100644
index 0000000..0a020b2
--- /dev/null
+++ b/libbalsa/address-book-osmo.h
@@ -0,0 +1,58 @@
+/* -*-mode:c; c-style:k&r; c-basic-offset:4; -*- */
+/* Balsa E-Mail Client
+ *
+ * Copyright (C) 1997-2016 Stuart Parmenter and others,
+ * See the file AUTHORS for a list.
+ *
+ * Osmo address book support has been written by Copyright (C) 2016
+ * Albrecht Dreß <albrecht dress arcor de>.
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 2, 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 General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, see <http://www.gnu.org/licenses/>.
+ */
+
+/*
+ * Access osmo (http://clayo.org/osmo/) address book through DBus
+ */
+
+#ifndef LIBBALSA_ADDRESS_BOOK_OSMO_H__
+#define LIBBALSA_ADDRESS_BOOK_OSMO_H__
+
+#include <gio/gio.h>
+#include "address-book.h"
+
+#define LIBBALSA_TYPE_ADDRESS_BOOK_OSMO (libbalsa_address_book_osmo_get_type())
+#define LIBBALSA_ADDRESS_BOOK_OSMO(obj) (G_TYPE_CHECK_INSTANCE_CAST(obj,
LIBBALSA_TYPE_ADDRESS_BOOK_OSMO, LibBalsaAddressBookOsmo))
+#define LIBBALSA_ADDRESS_BOOK_OSMO_CLASS(klass) (G_TYPE_CHECK_CLASS_CAST(klass,
LIBBALSA_TYPE_ADDRESS_BOOK_OSMO, LibBalsaAddressBookOsmoClass))
+#define LIBBALSA_IS_ADDRESS_BOOK_OSMO(obj) (G_TYPE_CHECK_INSTANCE_TYPE(obj,
LIBBALSA_TYPE_ADDRESS_BOOK_OSMO))
+#define LIBBALSA_IS_ADDRESS_BOOK_OSMO_CLASS(klass) (G_TYPE_CHECK_CLASS_TYPE(klass,
LIBBALSA_TYPE_ADDRESS_BOOK_OSMO))
+
+typedef struct _LibBalsaAddressBookOsmo LibBalsaAddressBookOsmo;
+typedef struct _LibBalsaAddressBookOsmoClass LibBalsaAddressBookOsmoClass;
+
+struct _LibBalsaAddressBookOsmo {
+ LibBalsaAddressBook parent;
+
+ GDBusProxy *proxy;
+};
+
+struct _LibBalsaAddressBookOsmoClass {
+ LibBalsaAddressBookClass parent_class;
+};
+
+GType libbalsa_address_book_osmo_get_type(void);
+
+LibBalsaAddressBook *libbalsa_address_book_osmo_new(const gchar *name);
+
+
+#endif /* __LIBBALSA_ADDRESS_BOOK_LDAP_H__ */
diff --git a/libbalsa/rfc6350.c b/libbalsa/rfc6350.c
new file mode 100644
index 0000000..0c902c0
--- /dev/null
+++ b/libbalsa/rfc6350.c
@@ -0,0 +1,353 @@
+/* Balsa E-Mail Client
+ *
+ * Copyright (C) 1997-2016 Stuart Parmenter and others, see the file AUTHORS for a list.
+ *
+ * This module provides a simple RFC 6350 (aka VCard 4.0, see https://tools.ietf.org/html/rfc6350) parser
which extracts a single
+ * VCard from a GDataInputStream and returns it as LibBalsaAddress.
+ *
+ * Written by Copyright (C) 2016 Albrecht Dreß <albrecht dress arcor de>.
+ *
+ * This program is free software; you can redistribute it and/or modify it under the terms of the GNU
General Public License as
+ * published by the Free Software Foundation; either version 2, 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 General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License along with this program; if not, see
+ * <http://www.gnu.org/licenses/>.
+ */
+
+#if defined(HAVE_CONFIG_H) && HAVE_CONFIG_H
+# include "config.h"
+#endif /* HAVE_CONFIG_H */
+
+#if defined(HAVE_OSMO)
+
+#include <glib/gi18n.h>
+#include "rfc6350.h"
+
+
+/* define GError stuff for the RFC 6350/VCard parser */
+#define RFC6350_ERROR_QUARK (g_quark_from_static_string("rfc6350-parser"))
+#define RFC6350_ERROR_NO_COLON 1
+#define RFC6350_ERROR_EMPTY 2
+#define RFC6350_ERROR_BEGIN 3
+#define RFC6350_ERROR_END 4
+
+
+static gboolean rfc6350_eval_line(gchar *line,
+ LibBalsaAddress *address,
+ GError **error);
+static gchar **rfc6350_strsplit(const gchar *item,
+ guint count);
+static gchar *rfc6350_get_name(gchar *name);
+static void rfc6350_unescape(gchar *item);
+static gchar *rfc6350_fn_from_n(gchar **n_items);
+
+
+LibBalsaAddress *
+rfc6350_parse_from_stream(GDataInputStream *stream,
+ gboolean *eos,
+ GError **error)
+{
+ gchar *line;
+ LibBalsaAddress *result = NULL;
+
+ line = g_data_input_stream_read_line_utf8(stream, NULL, NULL, error);
+ if (line == NULL) {
+ if (*error == NULL) {
+ *eos = TRUE;
+ }
+ } else if (g_ascii_strcasecmp(line, "BEGIN:VCARD") != 0) {
+ g_set_error(error, RFC6350_ERROR_QUARK, RFC6350_ERROR_BEGIN, _("malformed card, BEGIN:VCARD
expected"));
+ g_free(line);
+ } else {
+ gboolean parse_done = FALSE;
+
+ result = libbalsa_address_new();
+ while (result && (line != NULL) && !parse_done) {
+ if (g_ascii_strcasecmp(line, "END:VCARD") == 0) {
+ parse_done = TRUE;
+ g_free(line);
+ } else {
+ gchar *nextline;
+
+ /* perform unfolding (RFC 6350, sect. 3.2. "Line Delimiting and Folding") */
+ nextline = g_data_input_stream_read_line_utf8(stream, NULL, NULL, error);
+ while ((nextline) != NULL && ((nextline[0] == ' ') || (nextline[0] == '\t')))
{
+ gchar *unfold;
+
+ unfold = g_strconcat(line, &nextline[1], NULL);
+ g_free(line);
+ g_free(nextline);
+ line = unfold;
+ nextline = g_data_input_stream_read_line_utf8(stream, NULL, NULL,
error);
+ }
+
+ /* evaluate unfolded line, drop address on error */
+ if (!rfc6350_eval_line(line, result, error)) {
+ g_object_unref(result);
+ result = NULL;
+ }
+
+ /* process next line */
+ g_free(line);
+ line = nextline;
+ }
+ }
+
+ if (!parse_done) {
+ g_set_error(error, RFC6350_ERROR_QUARK, RFC6350_ERROR_END, _("malformed card,
END:VCARD missing"));
+ g_object_unref(result);
+ result = NULL;
+ }
+ }
+
+ /* ignore items without an Email address, fill empty full name if necessary */
+ if (result != NULL) {
+ if (result->address_list == NULL) {
+ g_object_unref(result);
+ result = NULL;
+ } else if (result->full_name == NULL) {
+ result->full_name = g_strdup(_("No-Name"));
+ }
+ }
+
+ return result;
+}
+
+
+/** \brief Extract a VCard name item
+ *
+ * \param name input name field, modified in place
+ * \return the name field, with group and parameters stripped
+ *
+ * Remove the \em group and \em param parts of the VCard \em name field (see RFC 6350, sect. 3.3. "ABNF
Format Definition").
+ *
+ * \note Do \em not free the returned value.
+ */
+static gchar *
+rfc6350_get_name(gchar *name)
+{
+ gchar *result;
+ gchar *semicolon;
+
+ /* skip group */
+ result = strchr(name, '.');
+ if (result == NULL) {
+ result = name;
+ } else {
+ result = &result[1];
+ }
+
+ /* drop any name parameters */
+ semicolon = strchr(result, ';');
+ if (semicolon != NULL) {
+ semicolon[0] = '\0';
+ }
+
+ return result;
+}
+
+
+/** \brief RFC 6350 unescape a string
+ *
+ * \param item VCard item, modified in place
+ *
+ * Unescape a string according to RFC 6350, sect. 3.4. "Property Value Escaping". Note that all other
escaped characters than those
+ * defined in RFC 6350 are simply ignored, although they \em should be regarded as errors.
+ *
+ * \note Do \em not free the returned value.
+ */
+static void
+rfc6350_unescape(gchar *item)
+{
+ gchar *p;
+ gchar *bslash;
+
+ g_assert(item != NULL);
+
+ p = item;
+ do {
+ bslash = strchr(p, '\\');
+ if (bslash != NULL) {
+ if (strchr(",;\\nN", bslash[1]) != NULL) {
+ if (g_ascii_tolower(bslash[1]) == 'n') {
+ bslash[1] = '\n';
+ }
+ memmove(bslash, &bslash[1], strlen(bslash));
+ }
+ p = &bslash[1];
+ } else {
+ p = NULL;
+ }
+ } while (p != NULL);
+}
+
+
+/** \brief Split a ';' delimited list into items
+ *
+ * \param item value string consisting of ';' delimited items
+ * \param count maximum number of fields to split into
+ * \return a newly allocated NULL-terminated array of unescaped fields
+ *
+ * Split the passed value string into fields, and unescape the resulting values. If the \em item contains
more than \em count
+ * fields, the extra delimiters are ignored.
+ *
+ * \note Free the returned value by calling g_strfreev() on it.
+ */
+static gchar **
+rfc6350_strsplit(const gchar *item,
+ guint count)
+{
+ gchar **result;
+ const gchar *start;
+ guint index;
+
+ result = g_new0(gchar *, count + 1U);
+ start = item;
+ index = 0U;
+
+ while ((start != NULL) && (index < count)) {
+ if (start[0] == ';') {
+ result[index] = g_strdup("");
+ start = &start[1];
+ } else {
+ const gchar *delim;
+
+ delim = strchr(start, ';');
+ while ((delim != NULL) && (delim[-1] == '\\')) {
+ delim = strchr(&delim[1], ';');
+ }
+ if (delim != NULL) {
+ result[index] = g_strndup(start, delim - start);
+ start = &delim[1];
+ } else {
+ result[index] = g_strdup(start);
+ start = NULL;
+ }
+ rfc6350_unescape(result[index]);
+ }
+ index++;
+ }
+
+ return result;
+}
+
+
+/** \brief Evaluate a VCard line
+ *
+ * \param line VCard line
+ * \param address target address object
+ * \param error filled with error information on error
+ * \return TRUE on success, or FALSE is the VCard line is malformed
+ *
+ * Evaluate the VCard line, extract a N, FN, NICKNAME, ORG or EMAIL item and assign it to the appropriate
fields in the target
+ * address item.
+ */
+static gboolean
+rfc6350_eval_line(gchar *line,
+ LibBalsaAddress *address,
+ GError **error)
+{
+ gchar *value;
+ gboolean result;
+
+ /* split into name and value */
+ value = strchr(line, ':');
+ if (value == NULL) {
+ g_set_error(error, RFC6350_ERROR_QUARK, RFC6350_ERROR_NO_COLON, _("malformed line '%s',
missing ':'"), line);
+ result = FALSE;
+ } else {
+ gchar *namepart;
+ gchar *name;
+
+ /* get the name and make sure that neither name nor value are empty */
+ namepart = g_strndup(line, value - line);
+ name = rfc6350_get_name(namepart);
+ value = &value[1];
+ if ((name[0] == '\0') || (value[0] == '\0')) {
+ g_set_error(error, RFC6350_ERROR_QUARK, RFC6350_ERROR_EMPTY, _("malformed line '%s',
empty name or value"), line);
+ result = FALSE;
+ } else {
+ g_debug("%s: line='%s' name='%s', value='%s'", __func__, line, name, value);
+ if (g_ascii_strcasecmp(name, "FN") == 0) {
+ rfc6350_unescape(value);
+ g_free(address->full_name);
+ address->full_name = g_strdup(value);
+ } else if (g_ascii_strcasecmp(name, "N") == 0) {
+ gchar **n_items;
+
+ n_items = rfc6350_strsplit(value, 5U);
+ g_free(address->first_name);
+ g_free(address->last_name);
+ if (n_items[1] != NULL) {
+ address->first_name = g_strdup(n_items[1]);
+ } else {
+ address->first_name = NULL;
+ }
+ if (n_items[0] != NULL) {
+ address->last_name = g_strdup(n_items[0]);
+ } else {
+ address->last_name = NULL;
+ }
+ if (address->full_name == NULL) {
+ address->full_name = rfc6350_fn_from_n(n_items);
+ }
+ g_strfreev(n_items);
+ } else if (g_ascii_strcasecmp(name, "NICKNAME") == 0) {
+ rfc6350_unescape(value);
+ g_free(address->nick_name);
+ address->nick_name = g_strdup(value);
+ } else if (g_ascii_strcasecmp(name, "ORG") == 0) {
+ gchar **n_items;
+
+ n_items = rfc6350_strsplit(value, 2U);
+ g_free(address->organization);
+ address->organization = g_strdup(n_items[0]);
+ g_strfreev(n_items);
+ } else if (g_ascii_strcasecmp(name, "EMAIL") == 0) {
+ rfc6350_unescape(value);
+ address->address_list = g_list_prepend(address->address_list,
g_strdup(value));
+ } else {
+ /* ignore any other items */
+ }
+ }
+ g_free(namepart);
+ result = TRUE;
+ }
+
+ return result;
+}
+
+
+/** \brief Create a full name from VCard "N" items
+ *
+ * \param n_items extracted VCard N items
+ * \return the full name
+ *
+ * Construct a full name by concatenating -in this order- the VCard "N" elements Honorific Prefixes, Given
Names, Additional Names,
+ * Family Names and Honorific Suffixes, separated by a single space each.
+ */
+static gchar *
+rfc6350_fn_from_n(gchar **n_items)
+{
+ GString *fn;
+ static const guint add_idx[5] = { 3U, 1U, 2U, 0U, 4U };
+ guint n;
+
+ fn = g_string_new(NULL);
+
+ for (n = 0; n < 5U; n++) {
+ if ((n_items[add_idx[n]] != NULL) && (n_items[add_idx[n]][0] != '\0')) {
+ if (fn->len > 0U) {
+ fn = g_string_append_c(fn, ' ');
+ }
+ fn = g_string_append(fn, n_items[add_idx[n]]);
+ }
+ }
+
+ return g_string_free(fn, FALSE);
+}
+
+#endif /* HAVE_OSMO */
diff --git a/libbalsa/rfc6350.h b/libbalsa/rfc6350.h
new file mode 100644
index 0000000..4904ef1
--- /dev/null
+++ b/libbalsa/rfc6350.h
@@ -0,0 +1,45 @@
+/* Balsa E-Mail Client
+ *
+ * Copyright (C) 1997-2016 Stuart Parmenter and others, see the file AUTHORS for a list.
+ *
+ * This module provides a simple RFC 6350 (aka VCard 4.0, see https://tools.ietf.org/html/rfc6350) parser
which extracts a single
+ * VCard from a GDataInputStream and returns it as LibBalsaAddress.
+ *
+ * Written by Copyright (C) 2016 Albrecht Dreß <albrecht dress arcor de>.
+ *
+ * This program is free software; you can redistribute it and/or modify it under the terms of the GNU
General Public License as
+ * published by the Free Software Foundation; either version 2, 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 General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License along with this program; if not, see
+ * <http://www.gnu.org/licenses/>.
+ */
+
+#ifndef LIBBALSA_RFC6350_H_
+#define LIBBALSA_RFC6350_H_
+
+
+#include "address.h"
+
+
+/** \brief Extract a single VCard 4.0
+ *
+ * \param stream VCard input stream
+ * \param eos filled with TRUE is the end of the input stream has been reached
+ * \param error filled with error information on error
+ * \return a new address object on success
+ *
+ * Read a VCard from the input stream and extract all relevant fields into a LibBalsaAddress. A
LibBalsaAddress is returned only if
+ * it contains any email address. The caller shall distinguish this case from an error by checking if the
error argument is filled.
+ * The input stream is positioned immediately after the card read.
+ *
+ * \note The caller shall unref the returned address object.
+ */
+LibBalsaAddress *rfc6350_parse_from_stream(GDataInputStream *stream,
+ gboolean
*eos,
+ GError
**error);
+
+
+#endif /* LIBBALSA_RFC6350_H_ */
[
Date Prev][
Date Next] [
Thread Prev][
Thread Next]
[
Thread Index]
[
Date Index]
[
Author Index]