summaryrefslogtreecommitdiff
path: root/libmailutils
diff options
context:
space:
mode:
Diffstat (limited to 'libmailutils')
-rw-r--r--libmailutils/.gitignore16
-rw-r--r--libmailutils/Makefile.am186
-rw-r--r--libmailutils/acl.c784
-rw-r--r--libmailutils/address.c595
-rw-r--r--libmailutils/alloc.c116
-rw-r--r--libmailutils/amd.c1925
-rw-r--r--libmailutils/argcv.c549
-rw-r--r--libmailutils/asnprintf.c40
-rw-r--r--libmailutils/asprintf.c42
-rw-r--r--libmailutils/assoc.c529
-rw-r--r--libmailutils/attachment.c467
-rw-r--r--libmailutils/attribute.c470
-rw-r--r--libmailutils/auth.c164
-rw-r--r--libmailutils/base64.c273
-rw-r--r--libmailutils/binflt.c113
-rw-r--r--libmailutils/body.c419
-rw-r--r--libmailutils/cfg_driver.c755
-rw-r--r--libmailutils/cfg_format.c395
-rw-r--r--libmailutils/cfg_lexer.l405
-rw-r--r--libmailutils/cfg_parser.y1817
-rw-r--r--libmailutils/crlfdot.c336
-rw-r--r--libmailutils/crlfflt.c182
-rw-r--r--libmailutils/cstrcasecmp.c58
-rw-r--r--libmailutils/cstrlower.c31
-rw-r--r--libmailutils/cstrupper.c31
-rw-r--r--libmailutils/daemon.c189
-rw-r--r--libmailutils/date.c245
-rw-r--r--libmailutils/dbgstderr.c36
-rw-r--r--libmailutils/dbgstream.c86
-rw-r--r--libmailutils/dbgsyslog.c34
-rw-r--r--libmailutils/debug.c298
-rw-r--r--libmailutils/diag.c179
-rw-r--r--libmailutils/dot.c268
-rw-r--r--libmailutils/envelope.c181
-rw-r--r--libmailutils/errors87
-rw-r--r--libmailutils/fgetpwent.c163
-rw-r--r--libmailutils/file_stream.c312
-rw-r--r--libmailutils/filter.c193
-rw-r--r--libmailutils/filter_iconv.c474
-rw-r--r--libmailutils/fltstream.c533
-rw-r--r--libmailutils/folder.c511
-rw-r--r--libmailutils/freeitem.c29
-rw-r--r--libmailutils/gdebug.c263
-rw-r--r--libmailutils/gocs.c397
-rw-r--r--libmailutils/hdritr.c162
-rw-r--r--libmailutils/header.c1228
-rw-r--r--libmailutils/iostream.c261
-rw-r--r--libmailutils/ipsrv.c580
-rw-r--r--libmailutils/iterator.c284
-rw-r--r--libmailutils/kwd.c94
-rw-r--r--libmailutils/linelenflt.c110
-rw-r--r--libmailutils/list.c685
-rw-r--r--libmailutils/listlist.c139
-rw-r--r--libmailutils/locale.c242
-rw-r--r--libmailutils/locker.c1025
-rw-r--r--libmailutils/mailbox.c786
-rw-r--r--libmailutils/mailcap.c744
-rw-r--r--libmailutils/mailer.c698
-rw-r--r--libmailutils/mapfile_stream.c378
-rw-r--r--libmailutils/mbx_default.c454
-rw-r--r--libmailutils/mbxitr.c160
-rw-r--r--libmailutils/md5.c454
-rw-r--r--libmailutils/memory_stream.c224
-rw-r--r--libmailutils/message.c1292
-rw-r--r--libmailutils/message_stream.c438
-rw-r--r--libmailutils/mime.c1071
-rw-r--r--libmailutils/mimehdr.c695
-rw-r--r--libmailutils/mkfilename.c46
-rw-r--r--libmailutils/monitor.c282
-rw-r--r--libmailutils/msgscan.c106
-rw-r--r--libmailutils/msrv.c1091
-rw-r--r--libmailutils/mu_auth.c539
-rw-r--r--libmailutils/muctype.c152
-rw-r--r--libmailutils/muerrno.cin65
-rw-r--r--libmailutils/muerror.c117
-rw-r--r--libmailutils/munre.c93
-rw-r--r--libmailutils/mutil.c1491
-rw-r--r--libmailutils/nls.c62
-rw-r--r--libmailutils/nullrec.c49
-rw-r--r--libmailutils/observer.c239
-rw-r--r--libmailutils/opool.c446
-rw-r--r--libmailutils/parse822.c2040
-rw-r--r--libmailutils/parsedate.y1206
-rw-r--r--libmailutils/permstr.c128
-rw-r--r--libmailutils/prog_stream.c462
-rw-r--r--libmailutils/progmailer.c314
-rw-r--r--libmailutils/property.c192
-rw-r--r--libmailutils/qpflt.c262
-rw-r--r--libmailutils/rdcache_stream.c213
-rw-r--r--libmailutils/refcount.c107
-rw-r--r--libmailutils/registrar.c443
-rw-r--r--libmailutils/rfc2047.c314
-rw-r--r--libmailutils/secret.c135
-rw-r--r--libmailutils/server.c293
-rw-r--r--libmailutils/sha1.c418
-rw-r--r--libmailutils/size_max.h31
-rw-r--r--libmailutils/socket_stream.c122
-rw-r--r--libmailutils/stdio_stream.c83
-rw-r--r--libmailutils/stream.c1140
-rw-r--r--libmailutils/stream_printf.c32
-rw-r--r--libmailutils/stream_vprintf.c42
-rw-r--r--libmailutils/streamcpy.c128
-rw-r--r--libmailutils/streamref.c312
-rw-r--r--libmailutils/stripws.c33
-rw-r--r--libmailutils/strltrim.c63
-rw-r--r--libmailutils/strrtrim.c51
-rw-r--r--libmailutils/strskip.c50
-rw-r--r--libmailutils/syslog.c145
-rw-r--r--libmailutils/system.c160
-rw-r--r--libmailutils/tcp.c356
-rw-r--r--libmailutils/temp_file_stream.c65
-rw-r--r--libmailutils/testsuite/.gitignore7
-rw-r--r--libmailutils/testsuite/Addrs644
-rw-r--r--libmailutils/testsuite/Argcv52
-rw-r--r--libmailutils/testsuite/Decode5
-rw-r--r--libmailutils/testsuite/Decode204736
-rw-r--r--libmailutils/testsuite/Encodebin0 -> 256 bytes
-rw-r--r--libmailutils/testsuite/Encode204727
-rw-r--r--libmailutils/testsuite/Mailcap195
-rw-r--r--libmailutils/testsuite/Makefile.am92
-rw-r--r--libmailutils/testsuite/Mime212
-rw-r--r--libmailutils/testsuite/Urls629
-rw-r--r--libmailutils/testsuite/lib/DISTFILES1
-rw-r--r--libmailutils/testsuite/lib/mailbox.exp243
-rw-r--r--libmailutils/testsuite/mailbox/DISTFILES9
-rw-r--r--libmailutils/testsuite/mailbox/address.exp48
-rw-r--r--libmailutils/testsuite/mailbox/argcv.exp55
-rw-r--r--libmailutils/testsuite/mailbox/base64.exp37
-rw-r--r--libmailutils/testsuite/mailbox/decode2047.exp50
-rw-r--r--libmailutils/testsuite/mailbox/encode2047.exp62
-rw-r--r--libmailutils/testsuite/mailbox/list.exp330
-rw-r--r--libmailutils/testsuite/mailbox/mailcap.exp71
-rw-r--r--libmailutils/testsuite/mailbox/mime.exp39
-rw-r--r--libmailutils/testsuite/mailbox/url.exp48
-rw-r--r--libmailutils/ticket.c243
-rw-r--r--libmailutils/url.c1135
-rw-r--r--libmailutils/vartab.c347
-rw-r--r--libmailutils/vasnprintf.c87
-rw-r--r--libmailutils/version.c189
-rw-r--r--libmailutils/wicket.c360
-rw-r--r--libmailutils/xscript-stream.c437
141 files changed, 47188 insertions, 0 deletions
diff --git a/libmailutils/.gitignore b/libmailutils/.gitignore
new file mode 100644
index 000000000..8ce64ea2d
--- /dev/null
+++ b/libmailutils/.gitignore
@@ -0,0 +1,16 @@
+*.la
+*.lo
+.deps
+.libs
+Makefile
+Makefile.in
+T
+_*
+.gdbinit
+mailutils-config
+parsedate.c
+*y.output
+muerrno.c
+cfg_lexer.c
+cfg_parser.c
+cfg_parser.h
diff --git a/libmailutils/Makefile.am b/libmailutils/Makefile.am
new file mode 100644
index 000000000..f28b1bdbc
--- /dev/null
+++ b/libmailutils/Makefile.am
@@ -0,0 +1,186 @@
+## Process this file with GNU Automake to create Makefile.in
+
+## Copyright (C) 2000, 2001, 2002, 2004, 2005, 2006, 2007, 2008, 2009,
+## 2010 Free Software Foundation, Inc.
+##
+## GNU Mailutils 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 3, 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, write to the Free Software
+## Foundation, Inc. 51 Franklin Street, Fifth Floor, Boston, MA
+## 02110-1301 USA
+
+INCLUDES = @MU_LIB_COMMON_INCLUDES@ -I${top_srcdir}/libmailutils
+
+YLWRAP = $(SHELL) $(mu_aux_dir)/gylwrap
+AM_YFLAGS=-vt
+AM_LFLAGS=-dvp
+
+SUBDIRS = testsuite
+
+lib_LTLIBRARIES = libmailutils.la
+
+localedir = $(datadir)/locale
+AM_CPPFLAGS = \
+ -DSYSCONFDIR=\"$(sysconfdir)\"\
+ -DSITE_VIRTUAL_PWDDIR=\"@SITE_VIRTUAL_PWDDIR@\"\
+ -DLOCALEDIR=\"$(localedir)\"
+
+EXTRA_DIST = \
+ errors\
+ muerrno.cin\
+ parsedate.y\
+ fgetpwent.c\
+ cfg_lexer.l\
+ cfg_parser.y\
+ cfg_parser.h
+
+libmailutils_la_SOURCES = \
+ acl.c\
+ address.c\
+ alloc.c\
+ amd.c\
+ argcv.c\
+ asnprintf.c\
+ asprintf.c\
+ assoc.c\
+ attachment.c\
+ attribute.c\
+ auth.c\
+ base64.c\
+ binflt.c\
+ body.c\
+ cstrcasecmp.c\
+ cfg_driver.c\
+ cfg_format.c\
+ cfg_lexer.c\
+ cfg_parser.c\
+ crlfdot.c\
+ crlfflt.c\
+ cstrlower.c\
+ cstrupper.c\
+ daemon.c\
+ date.c\
+ dbgstderr.c\
+ dbgstream.c\
+ dbgsyslog.c\
+ debug.c\
+ diag.c\
+ dot.c\
+ envelope.c\
+ fgetpwent.c\
+ file_stream.c\
+ filter.c\
+ filter_iconv.c\
+ fltstream.c\
+ folder.c\
+ freeitem.c\
+ gdebug.c\
+ gocs.c\
+ hdritr.c\
+ header.c\
+ iostream.c\
+ iterator.c\
+ ipsrv.c\
+ kwd.c\
+ linelenflt.c\
+ list.c\
+ listlist.c\
+ locale.c\
+ locker.c\
+ mailbox.c\
+ mailcap.c\
+ mailer.c\
+ mapfile_stream.c\
+ mbx_default.c\
+ mbxitr.c\
+ md5.c\
+ message.c\
+ memory_stream.c\
+ message_stream.c\
+ mime.c\
+ mimehdr.c\
+ mkfilename.c\
+ monitor.c\
+ msgscan.c\
+ msrv.c\
+ mu_auth.c\
+ muctype.c\
+ munre.c\
+ mutil.c\
+ muerror.c\
+ muerrno.c\
+ nls.c\
+ nullrec.c\
+ observer.c\
+ opool.c\
+ parse822.c\
+ parsedate.c\
+ permstr.c\
+ progmailer.c\
+ prog_stream.c\
+ property.c\
+ qpflt.c\
+ rdcache_stream.c\
+ registrar.c\
+ refcount.c\
+ rfc2047.c\
+ sha1.c\
+ secret.c\
+ server.c\
+ socket_stream.c\
+ stdio_stream.c\
+ stream.c\
+ stream_printf.c\
+ stream_vprintf.c\
+ streamcpy.c\
+ streamref.c\
+ strltrim.c\
+ strskip.c\
+ stripws.c\
+ strrtrim.c\
+ syslog.c\
+ system.c\
+ temp_file_stream.c\
+ ticket.c\
+ tcp.c\
+ url.c\
+ vartab.c\
+ vasnprintf.c\
+ version.c\
+ wicket.c\
+ xscript-stream.c
+
+BUILT_SOURCES = parsedate.c muerrno.c cfg_parser.c cfg_parser.h cfg_lexer.c
+MOSTLYCLEANFILES=
+
+parsedate.c: $(srcdir)/parsedate.y
+ $(YLWRAP) "$(YACC) $(AM_YFLAGS)" $< \
+ y.tab.c parsedate.c y.output parsedate.y.output \
+ -- -yy pd_yy
+
+cfg_parser.c cfg_parser.h: $(srcdir)/cfg_parser.y
+ $(YLWRAP) "$(YACC) $(AM_YFLAGS) -d" $< \
+ y.tab.c cfg_parser.c y.tab.h cfg_parser.h \
+ y.output cfg_parser.y.output \
+ -- -yy mu_cfg_yy
+
+cfg_lexer.c: $(srcdir)/cfg_lexer.l cfg_parser.h
+ $(YLWRAP) "$(LEX) $(AM_LFLAGS) $(LFLAGS)" \
+ $(srcdir)/cfg_lexer.l lex.yy.c cfg_lexer.c \
+ -- -yy mu_cfg_yy
+
+muerrno.c: errors muerrno.cin
+ $(AWK) -f $(mu_aux_dir)/generr.awk $^ > $@
+
+libmailutils_la_LIBADD = @MU_COMMON_LIBRARIES@
+libmailutils_la_LDFLAGS = -version-info @VI_CURRENT@:@VI_REVISION@:@VI_AGE@
+
diff --git a/libmailutils/acl.c b/libmailutils/acl.c
new file mode 100644
index 000000000..a3e3764f2
--- /dev/null
+++ b/libmailutils/acl.c
@@ -0,0 +1,784 @@
+/* GNU Mailutils -- a suite of utilities for electronic mail
+ Copyright (C) 2007, 2008, 2009, 2010 Free Software Foundation, Inc.
+
+ This library is free software; you can redistribute it and/or
+ modify it under the terms of the GNU Lesser General Public
+ License as published by the Free Software Foundation; either
+ version 3 of the License, or (at your option) any later version.
+
+ This library 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
+ Lesser General Public License for more details.
+
+ You should have received a copy of the GNU Lesser General
+ Public License along with this library; If not, see
+ <http://www.gnu.org/licenses/>. */
+
+#ifdef HAVE_CONFIG_H
+#include <config.h>
+#endif
+#include <sys/socket.h>
+#include <netinet/in.h>
+#include <sys/un.h>
+#include <arpa/inet.h>
+
+#include <sys/types.h>
+#include <sys/wait.h>
+#include <unistd.h>
+#include <stdlib.h>
+#include <string.h>
+#include <mailutils/acl.h>
+#include <mailutils/argcv.h>
+#include <mailutils/list.h>
+#include <mailutils/debug.h>
+#include <mailutils/error.h>
+#include <mailutils/errno.h>
+#include <mailutils/kwd.h>
+#include <mailutils/vartab.h>
+#include <mailutils/io.h>
+
+struct _mu_acl_entry
+{
+ mu_acl_action_t action;
+ void *arg;
+ unsigned netmask;
+ int salen;
+ struct sockaddr sa[1];
+};
+
+struct _mu_acl
+{
+ mu_debug_t debug;
+ mu_list_t aclist;
+};
+
+
+static void
+_destroy_acl_entry (void *item)
+{
+ struct _mu_acl_entry *p = item;
+ free (p);
+ /* FIXME: free arg? */
+}
+
+static size_t
+mu_acl_entry_size (int salen)
+{
+ return sizeof (struct _mu_acl_entry) + salen - sizeof (struct sockaddr);
+}
+
+static int
+prepare_sa (struct sockaddr *sa)
+{
+ switch (sa->sa_family)
+ {
+ case AF_INET:
+ {
+ struct sockaddr_in *s_in = (struct sockaddr_in *)sa;
+ s_in->sin_addr.s_addr = ntohl (s_in->sin_addr.s_addr);
+ break;
+ }
+
+ case AF_UNIX:
+ break;
+
+ default:
+ return 1;
+ }
+ return 0;
+}
+
+int
+mu_acl_entry_create (struct _mu_acl_entry **pent,
+ mu_acl_action_t action, void *data,
+ struct sockaddr *sa, int salen, unsigned long netmask)
+{
+ struct _mu_acl_entry *p = malloc (mu_acl_entry_size (salen));
+ if (!p)
+ return EINVAL;
+
+ p->action = action;
+ p->arg = data;
+ p->netmask = ntohl (netmask);
+ p->salen = salen;
+ memcpy (p->sa, sa, salen);
+ if (prepare_sa (p->sa))
+ {
+ free (p);
+ return EINVAL;
+ }
+ *pent = p;
+ return 0;
+}
+
+
+int
+mu_acl_create (mu_acl_t *pacl)
+{
+ int rc;
+ mu_acl_t acl;
+ mu_log_level_t level;
+
+ acl = calloc (1, sizeof (*acl));
+ if (!acl)
+ return errno;
+ rc = mu_list_create (&acl->aclist);
+ if (rc)
+ free (acl);
+ else
+ *pacl = acl;
+ mu_list_set_destroy_item (acl->aclist, _destroy_acl_entry);
+
+ level = mu_global_debug_level ("acl");
+ if (level)
+ {
+ int status = mu_debug_create (&acl->debug, NULL);
+ if (status == 0)
+ mu_debug_set_level (acl->debug, level);
+ }
+
+ return rc;
+}
+
+int
+mu_acl_count (mu_acl_t acl, size_t *pcount)
+{
+ if (!acl)
+ return EINVAL;
+ return mu_list_count (acl->aclist, pcount);
+}
+
+int
+mu_acl_destroy (mu_acl_t *pacl)
+{
+ mu_acl_t acl;
+ if (!pacl || !*pacl)
+ return EINVAL;
+ acl = *pacl;
+ mu_list_destroy (&acl->aclist);
+ mu_debug_destroy (&acl->debug, NULL);
+ free (acl);
+ *pacl = acl;
+ return 0;
+}
+
+int
+mu_acl_get_debug (mu_acl_t acl, mu_debug_t *pdebug)
+{
+ if (!acl)
+ return EINVAL;
+ if (!pdebug)
+ return MU_ERR_OUT_NULL;
+ *pdebug = acl->debug;
+ return 0;
+}
+
+int
+mu_acl_set_debug (mu_acl_t acl, mu_debug_t debug)
+{
+ if (!acl)
+ return EINVAL;
+ acl->debug = debug;
+ return 0;
+}
+
+int
+mu_acl_get_iterator (mu_acl_t acl, mu_iterator_t *pitr)
+{
+ if (!acl)
+ return EINVAL;
+ return mu_list_get_iterator (acl->aclist, pitr);
+}
+
+int
+mu_acl_append (mu_acl_t acl, mu_acl_action_t act,
+ void *data, struct sockaddr *sa, int salen,
+ unsigned long netmask)
+{
+ int rc;
+ struct _mu_acl_entry *ent;
+
+ if (!acl)
+ return EINVAL;
+ rc = mu_acl_entry_create (&ent, act, data, sa, salen, netmask);
+ if (rc)
+ {
+ MU_DEBUG1 (acl->debug, MU_DEBUG_ERROR, "Cannot allocate ACL entry: %s",
+ mu_strerror (rc));
+ return ENOMEM;
+ }
+
+ rc = mu_list_append (acl->aclist, ent);
+ if (rc)
+ {
+ MU_DEBUG1 (acl->debug, MU_DEBUG_ERROR, "Cannot append ACL entry: %s",
+ mu_strerror (rc));
+ free (ent);
+ }
+ return rc;
+}
+
+int
+mu_acl_prepend (mu_acl_t acl, mu_acl_action_t act, void *data,
+ struct sockaddr *sa, int salen, unsigned long netmask)
+{
+ int rc;
+ struct _mu_acl_entry *ent;
+
+ if (!acl)
+ return EINVAL;
+ rc = mu_acl_entry_create (&ent, act, data, sa, salen, netmask);
+ if (rc)
+ {
+ MU_DEBUG1 (acl->debug, MU_DEBUG_ERROR, "Cannot allocate ACL entry: %s",
+ mu_strerror (rc));
+ return ENOMEM;
+ }
+ rc = mu_list_prepend (acl->aclist, ent);
+ if (rc)
+ {
+ MU_DEBUG1 (acl->debug, MU_DEBUG_ERROR, "Cannot prepend ACL entry: %s",
+ mu_strerror (rc));
+ free (ent);
+ }
+ return rc;
+}
+
+int
+mu_acl_insert (mu_acl_t acl, size_t pos, int before,
+ mu_acl_action_t act, void *data,
+ struct sockaddr *sa, int salen, unsigned long netmask)
+{
+ int rc;
+ void *ptr;
+ struct _mu_acl_entry *ent;
+
+ if (!acl)
+ return EINVAL;
+
+ rc = mu_list_get (acl->aclist, pos, &ptr);
+ if (rc)
+ {
+ MU_DEBUG1 (acl->debug, MU_DEBUG_ERROR, "No such entry %lu",
+ (unsigned long) pos);
+ return rc;
+ }
+ rc = mu_acl_entry_create (&ent, act, data, sa, salen, netmask);
+ if (!ent)
+ {
+ MU_DEBUG1 (acl->debug, MU_DEBUG_ERROR, "Cannot allocate ACL entry: %s",
+ mu_strerror (rc));
+ return ENOMEM;
+ }
+ rc = mu_list_insert (acl->aclist, ptr, ent, before);
+ if (rc)
+ {
+ MU_DEBUG1 (acl->debug, MU_DEBUG_ERROR, "Cannot insert ACL entry: %s",
+ mu_strerror (rc));
+ free (ent);
+ }
+ return rc;
+}
+
+
+static mu_kwd_t action_tab[] = {
+ { "accept", mu_acl_accept },
+ { "deny", mu_acl_deny },
+ { "log", mu_acl_log },
+ { "exec", mu_acl_exec },
+ { "ifexec", mu_acl_ifexec },
+ { NULL }
+};
+
+int
+mu_acl_action_to_string (mu_acl_action_t act, const char **pstr)
+{
+ return mu_kwd_xlat_tok (action_tab, act, pstr);
+}
+
+int
+mu_acl_string_to_action (const char *str, mu_acl_action_t *pres)
+{
+ int x;
+ int rc = mu_kwd_xlat_name (action_tab, str, &x);
+ if (rc == 0)
+ *pres = x;
+ return rc;
+}
+
+#define MU_S_UN_NAME(sa, salen) \
+ ((salen < mu_offsetof (struct sockaddr_un,sun_path)) ? "" : (sa)->sun_path)
+
+static void
+debug_sockaddr (mu_debug_t dbg, mu_log_level_t lvl, struct sockaddr *sa,
+ int salen)
+{
+ switch (sa->sa_family)
+ {
+ case AF_INET:
+ {
+ struct sockaddr_in s_in = *(struct sockaddr_in *)sa;
+ s_in.sin_addr.s_addr = htonl (s_in.sin_addr.s_addr);
+ mu_debug_printf (dbg, lvl, "{AF_INET %s:%d}",
+ inet_ntoa (s_in.sin_addr), ntohs (s_in.sin_port));
+ break;
+ }
+
+ case AF_UNIX:
+ {
+ struct sockaddr_un *s_un = (struct sockaddr_un *)sa;
+ if (MU_S_UN_NAME(s_un, salen)[0] == 0)
+ mu_debug_printf (dbg, lvl, "{AF_UNIX}");
+ else
+ mu_debug_printf (dbg, lvl, "{AF_UNIX %s}", s_un->sun_path);
+ break;
+ }
+
+ default:
+ mu_debug_printf (dbg, lvl, "{Unsupported family: %d}", sa->sa_family);
+ }
+}
+
+size_t
+mu_stpcpy (char **pbuf, size_t *psize, const char *src)
+{
+ size_t slen = strlen (src);
+ if (pbuf == NULL || *pbuf == NULL)
+ return slen;
+ else
+ {
+ char *buf = *pbuf;
+ size_t size = *psize;
+ if (size > slen)
+ size = slen;
+ memcpy (buf, src, size);
+ *psize -= size;
+ *pbuf += size;
+ if (*psize)
+ **pbuf = 0;
+ else
+ (*pbuf)[-1] = 0;
+ return size;
+ }
+}
+
+void
+mu_sockaddr_to_str (const struct sockaddr *sa, int salen,
+ char *bufptr, size_t buflen,
+ size_t *plen)
+{
+ char *nbuf;
+ size_t len = 0;
+ switch (sa->sa_family)
+ {
+ case AF_INET:
+ {
+ struct sockaddr_in s_in = *(struct sockaddr_in *)sa;
+ len += mu_stpcpy (&bufptr, &buflen, inet_ntoa (s_in.sin_addr));
+ len += mu_stpcpy (&bufptr, &buflen, ":");
+ if (mu_asprintf (&nbuf, "%hu", ntohs (s_in.sin_port)) == 0)
+ {
+ len += mu_stpcpy (&bufptr, &buflen, nbuf);
+ free (nbuf);
+ }
+ break;
+ }
+
+ case AF_UNIX:
+ {
+ struct sockaddr_un *s_un = (struct sockaddr_un *)sa;
+ if (MU_S_UN_NAME(s_un, salen)[0] == 0)
+ len += mu_stpcpy (&bufptr, &buflen, "anonymous socket");
+ else
+ {
+ len += mu_stpcpy (&bufptr, &buflen, "socket ");
+ len += mu_stpcpy (&bufptr, &buflen, s_un->sun_path);
+ }
+ break;
+ }
+
+ default:
+ len += mu_stpcpy (&bufptr, &buflen, "{Unsupported family");
+ if (mu_asprintf (&nbuf, ": %d", sa->sa_family) == 0)
+ {
+ len += mu_stpcpy (&bufptr, &buflen, nbuf);
+ free (nbuf);
+ }
+ len += mu_stpcpy (&bufptr, &buflen, "}");
+ }
+ if (plen)
+ *plen = len + 1;
+}
+
+char *
+mu_sockaddr_to_astr (const struct sockaddr *sa, int salen)
+{
+ size_t size;
+ char *p;
+
+ mu_sockaddr_to_str (sa, salen, NULL, 0, &size);
+ p = malloc (size);
+ if (p)
+ mu_sockaddr_to_str (sa, salen, p, size, NULL);
+ return p;
+}
+
+int
+_acl_match (mu_debug_t debug, struct _mu_acl_entry *ent, struct sockaddr *sa,
+ int salen)
+{
+#define RESMATCH(word) \
+ if (mu_debug_check_level (debug, MU_DEBUG_TRACE0)) \
+ mu_debug_printf (debug, MU_DEBUG_TRACE0, "%s; ", word);
+
+ if (mu_debug_check_level (debug, MU_DEBUG_TRACE0))
+ {
+ struct in_addr a;
+
+ __MU_DEBUG1 (debug, MU_DEBUG_TRACE0, "%s", "Does ");
+ debug_sockaddr (debug, MU_DEBUG_TRACE0, sa, salen);
+ mu_debug_printf (debug, MU_DEBUG_TRACE0, " match ");
+ debug_sockaddr (debug, MU_DEBUG_TRACE0, ent->sa, salen);
+ a.s_addr = ent->netmask;
+ a.s_addr = htonl (a.s_addr);
+ mu_debug_printf (debug, MU_DEBUG_TRACE0, " netmask %s? ", inet_ntoa (a));
+ }
+
+ if (ent->sa->sa_family != sa->sa_family)
+ {
+ RESMATCH ("no");
+ return 1;
+ }
+
+ switch (ent->sa->sa_family)
+ {
+ case AF_INET:
+ {
+ struct sockaddr_in *sin_ent = (struct sockaddr_in *)ent->sa;
+ struct sockaddr_in *sin_item = (struct sockaddr_in *)sa;
+
+ if (sin_ent->sin_addr.s_addr !=
+ (sin_item->sin_addr.s_addr & ent->netmask))
+ {
+ RESMATCH ("no (address differs)");
+ return 1;
+ }
+
+ if (sin_ent->sin_port && sin_item->sin_port
+ && sin_ent->sin_port != sin_item->sin_port)
+ {
+ RESMATCH ("no (port differs)");
+ return 1;
+ }
+ break;
+ }
+
+ case AF_UNIX:
+ {
+ struct sockaddr_un *sun_ent = (struct sockaddr_un *)ent->sa;
+ struct sockaddr_un *sun_item = (struct sockaddr_un *)sa;
+
+ if (MU_S_UN_NAME (sun_ent, ent->salen)[0]
+ && MU_S_UN_NAME (sun_item, salen)[0]
+ && strcmp (sun_ent->sun_path, sun_item->sun_path))
+ {
+ RESMATCH ("no");
+ return 1;
+ }
+ break;
+ }
+ }
+
+ RESMATCH ("yes");
+ return 0;
+}
+
+struct run_closure
+{
+ unsigned idx;
+ mu_debug_t debug;
+ struct sockaddr *sa;
+ int salen;
+ mu_acl_result_t *result;
+};
+
+static int
+_expand_aclno (const char *name, void *data, char **p)
+{
+ struct run_closure *rp = data;
+ /*FIXME: memory leak*/
+ return mu_asprintf (p, "%u", rp->idx);
+}
+
+#if defined (HAVE_SYSCONF) && defined (_SC_OPEN_MAX)
+# define getmaxfd() sysconf (_SC_OPEN_MAX)
+#elif defined (HAVE_GETDTABLESIZE)
+# define getmaxfd() getdtablesize ()
+#else
+# define getmaxfd() 64
+#endif
+
+static int
+expand_arg (const char *cmdline, struct run_closure *rp, char **s)
+{
+ int rc;
+ mu_vartab_t vtab;
+
+ MU_DEBUG1 (rp->debug, MU_DEBUG_TRACE0, "Expanding \"%s\" => ", cmdline);
+
+ mu_vartab_create (&vtab);
+ mu_vartab_define_exp (vtab, "aclno", _expand_aclno, NULL, rp);
+ switch (rp->sa->sa_family)
+ {
+ case AF_INET:
+ {
+ struct sockaddr_in *s_in = (struct sockaddr_in *)rp->sa;
+ struct in_addr addr = s_in->sin_addr;
+ char *p;
+
+ mu_vartab_define (vtab, "family", "AF_INET", 1);
+ addr.s_addr = htonl (addr.s_addr);
+ mu_vartab_define (vtab, "address", inet_ntoa (addr), 0);
+ if (mu_asprintf (&p, "%hu", ntohs (s_in->sin_port)) == 0)
+ {
+ mu_vartab_define (vtab, "port", p, 0);
+ free (p);
+ }
+ }
+ break;
+
+ case AF_UNIX:
+ {
+ struct sockaddr_un *s_un = (struct sockaddr_un *)rp->sa;
+
+ mu_vartab_define (vtab, "family", "AF_UNIX", 1);
+ mu_vartab_define (vtab, "address", s_un->sun_path, 1);
+ }
+ break;
+ }
+
+ rc = mu_vartab_expand (vtab, cmdline, s);
+ mu_vartab_destroy (&vtab);
+
+ if (rc == 0)
+ MU_DEBUG1 (rp->debug, MU_DEBUG_TRACE0, "\"%s\". ", *s);
+ else
+ MU_DEBUG (rp->debug, MU_DEBUG_TRACE0, "failed. ");
+ return rc;
+}
+
+static int
+spawn_prog (const char *cmdline, int *pstatus, struct run_closure *rp)
+{
+ char *s;
+ pid_t pid;
+
+ if (expand_arg (cmdline, rp, &s))
+ s = strdup (cmdline);
+
+ pid = fork ();
+ if (pid == 0)
+ {
+ int i;
+ int argc;
+ char **argv;
+
+ mu_argcv_get (s, " \t", NULL, &argc, &argv);
+ for (i = getmaxfd (); i > 2; i--)
+ close (i);
+ execvp (argv[0], argv);
+ exit (127);
+ }
+
+ free (s);
+
+ if (pid == (pid_t)-1)
+ {
+ MU_DEBUG1 (rp->debug, MU_DEBUG_ERROR, "cannot fork: %s",
+ mu_strerror (errno));
+ return errno;
+ }
+
+ if (pstatus)
+ {
+ int status;
+ waitpid (pid, &status, 0);
+ if (WIFEXITED (status))
+ {
+ status = WEXITSTATUS (status);
+ MU_DEBUG1 (rp->debug, MU_DEBUG_TRACE0,
+ "Program finished with code %d.", status);
+ *pstatus = status;
+ }
+ else if (WIFSIGNALED (status))
+ {
+ MU_DEBUG1 (rp->debug, MU_DEBUG_ERROR,
+ "Program terminated on signal %d.",
+ WTERMSIG (status));
+ return MU_ERR_PROCESS_SIGNALED;
+ }
+ else
+ return MU_ERR_PROCESS_UNKNOWN_FAILURE;
+ }
+
+ return 0;
+}
+
+
+int
+_run_entry (void *item, void *data)
+{
+ int status = 0;
+ struct _mu_acl_entry *ent = item;
+ struct run_closure *rp = data;
+
+ rp->idx++;
+
+ if (mu_debug_check_level (rp->debug, MU_DEBUG_TRACE0))
+ {
+ const char *s = "undefined";
+ mu_acl_action_to_string (ent->action, &s);
+ __MU_DEBUG2 (rp->debug, MU_DEBUG_TRACE0, "%d:%s: ", rp->idx, s);
+ }
+
+ if (_acl_match (rp->debug, ent, rp->sa, rp->salen) == 0)
+ {
+ switch (ent->action)
+ {
+ case mu_acl_accept:
+ *rp->result = mu_acl_result_accept;
+ status = 1;
+ break;
+
+ case mu_acl_deny:
+ *rp->result = mu_acl_result_deny;
+ status = 1;
+ break;
+
+ case mu_acl_log:
+ {
+ char *s;
+ mu_debug_t dbg = NULL;
+ mu_diag_get_debug (&dbg);
+ if (ent->arg && expand_arg (ent->arg, rp, &s) == 0)
+ {
+ mu_debug_printf (dbg, MU_DIAG_INFO, "%s\n", s);
+ free (s);
+ }
+ else
+ {
+ debug_sockaddr (dbg, MU_DIAG_INFO, rp->sa, rp->salen);
+ mu_debug_printf (dbg, MU_DIAG_INFO, "\n");
+ }
+ }
+ break;
+
+ case mu_acl_exec:
+ spawn_prog (ent->arg, NULL, rp);
+ break;
+
+ case mu_acl_ifexec:
+ {
+ int prog_status;
+ int rc = spawn_prog (ent->arg, &prog_status, rp);
+ if (rc == 0)
+ {
+ switch (prog_status)
+ {
+ case 0:
+ *rp->result = mu_acl_result_accept;
+ status = 1;
+ break;
+
+ case 1:
+ *rp->result = mu_acl_result_deny;
+ status = 1;
+ }
+ }
+ }
+ break;
+ }
+ }
+
+ if (mu_debug_check_level (rp->debug, MU_DEBUG_TRACE0))
+ mu_debug_printf (rp->debug, MU_DEBUG_TRACE0, "\n");
+
+ return status;
+}
+
+int
+mu_acl_check_sockaddr (mu_acl_t acl, const struct sockaddr *sa, int salen,
+ mu_acl_result_t *pres)
+{
+ struct run_closure r;
+
+ if (!acl)
+ return EINVAL;
+
+ r.sa = malloc (salen);
+ if (!r.sa)
+ return ENOMEM;
+ memcpy (r.sa, sa, salen);
+ if (prepare_sa (r.sa))
+ {
+ free (r.sa);
+ return EINVAL;
+ }
+ r.salen = salen;
+
+ if (mu_debug_check_level (acl->debug, MU_DEBUG_TRACE0))
+ {
+ __MU_DEBUG1 (acl->debug, MU_DEBUG_TRACE0, "%s", "Checking sockaddr ");
+ debug_sockaddr (acl->debug, MU_DEBUG_TRACE0, r.sa, r.salen);
+ mu_debug_printf (acl->debug, MU_DEBUG_TRACE0, "\n");
+ }
+
+ r.idx = 0;
+ r.debug = acl->debug;
+ r.result = pres;
+ *r.result = mu_acl_result_undefined;
+ mu_list_do (acl->aclist, _run_entry, &r);
+ free (r.sa);
+ return 0;
+}
+
+int
+mu_acl_check_inaddr (mu_acl_t acl, const struct in_addr *inp,
+ mu_acl_result_t *pres)
+{
+ struct sockaddr_in cs;
+ int len = sizeof cs;
+
+ cs.sin_family = AF_INET;
+ cs.sin_addr = *inp;
+ cs.sin_addr.s_addr = ntohl (cs.sin_addr.s_addr);
+ return mu_acl_check_sockaddr (acl, (struct sockaddr *) &cs, len, pres);
+}
+
+int
+mu_acl_check_ipv4 (mu_acl_t acl, unsigned int addr, mu_acl_result_t *pres)
+{
+ struct in_addr in;
+
+ in.s_addr = addr;
+ return mu_acl_check_inaddr (acl, &in, pres);
+}
+
+int
+mu_acl_check_fd (mu_acl_t acl, int fd, mu_acl_result_t *pres)
+{
+ struct sockaddr_in cs;
+ socklen_t len = sizeof cs;
+
+ if (getpeername (fd, (struct sockaddr *) &cs, &len) < 0)
+ {
+ MU_DEBUG1 (acl->debug, MU_DEBUG_ERROR,
+ "Cannot obtain IP address of client: %s",
+ mu_strerror (errno));
+ return MU_ERR_FAILURE;
+ }
+
+ return mu_acl_check_sockaddr (acl, (struct sockaddr *) &cs, len, pres);
+}
+
diff --git a/libmailutils/address.c b/libmailutils/address.c
new file mode 100644
index 000000000..cd151c141
--- /dev/null
+++ b/libmailutils/address.c
@@ -0,0 +1,595 @@
+/* GNU Mailutils -- a suite of utilities for electronic mail
+ Copyright (C) 1999, 2000, 2001, 2005, 2006, 2007, 2009, 2010 Free
+ Software Foundation, Inc.
+
+ This library is free software; you can redistribute it and/or
+ modify it under the terms of the GNU Lesser General Public
+ License as published by the Free Software Foundation; either
+ version 3 of the License, or (at your option) any later version.
+
+ This library 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
+ Lesser General Public License for more details.
+
+ You should have received a copy of the GNU Lesser General
+ Public License along with this library; if not, write to the
+ Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor,
+ Boston, MA 02110-1301 USA */
+
+#ifdef HAVE_CONFIG_H
+# include <config.h>
+#endif
+
+#include <assert.h>
+#include <ctype.h>
+#include <errno.h>
+#include <stdio.h>
+#include <stdlib.h>
+#include <string.h>
+#include <string.h>
+
+#include <sys/types.h>
+
+#include <mailutils/errno.h>
+#include <mailutils/mutil.h>
+#include <mailutils/parse822.h>
+#include <mailutils/address.h>
+#include <mailutils/cstr.h>
+
+/* Get email addresses from rfc822 address. */
+int
+mu_address_create_hint (mu_address_t *a, const char *s, mu_address_t hint,
+ int hflags)
+{
+ /* 'a' must exist, and can't already have been initialized
+ */
+ int status;
+
+ if (!a)
+ return MU_ERR_OUT_PTR_NULL;
+
+ if (!s)
+ return EINVAL;
+
+ *a = NULL;
+ status = mu_parse822_address_list (a, s, hint, hflags);
+ if (status == 0)
+ {
+ /* And address-list may contain 0 addresses but parse correctly.
+ */
+ if (!*a)
+ return MU_ERR_EMPTY_ADDRESS;
+
+ (*a)->addr = strdup (s);
+ if (!(*a)->addr)
+ {
+ mu_address_destroy (a);
+ return ENOMEM;
+ }
+ }
+ return status;
+}
+
+int
+mu_address_create (mu_address_t *a, const char *s)
+{
+ struct mu_address hint;
+ const char *d;
+ mu_get_user_email_domain (&d);
+ hint.domain = (char*) d;
+ return mu_address_create_hint (a, s, &hint, MU_ADDR_HINT_DOMAIN);
+}
+
+/* Get email addresses from array of rfc822 addresses.
+ FIXME: No hints? */
+int
+mu_address_createv (mu_address_t *a, const char *sv[], size_t len)
+{
+ int status = 0;
+ size_t buflen = 0;
+ char *buf = 0;
+ size_t i;
+
+ if (!a)
+ return MU_ERR_OUT_PTR_NULL;
+
+ if (!sv)
+ return EINVAL;
+
+ if (len == (size_t) - 1)
+ {
+ const char **vp = sv;
+
+ len = 0;
+
+ for (len = 0; *vp; vp++, len++)
+ ;
+ }
+
+ if (len == 0)
+ return EINVAL;
+
+ for (i = 0; i < len; i++)
+ {
+ /* NULL strings are allowed */
+ if (sv[i])
+ buflen += strlen (sv[i]);
+ }
+
+ buflen += (len - 1) * strlen (", ");
+ buflen += 1; /* Termination null. */
+
+ buf = malloc (buflen);
+
+ if (!buf)
+ return ENOMEM;
+
+ for (i = 0, buf[0] = '\0'; i < len; i++)
+ {
+ if (i != 0)
+ strcat (buf, ", ");
+
+ if (sv[i])
+ strcat (buf, sv[i]);
+ }
+
+ status = mu_address_create (a, buf);
+
+ free (buf);
+
+ return status;
+}
+
+void
+mu_address_destroy (mu_address_t *paddress)
+{
+ if (paddress && *paddress)
+ {
+ mu_address_t address = *paddress;
+ mu_address_t current;
+ for (; address; address = current)
+ {
+ if (address->addr)
+ free (address->addr);
+ if (address->comments)
+ free (address->comments);
+ if (address->personal)
+ free (address->personal);
+ if (address->email)
+ free (address->email);
+ if (address->local_part)
+ free (address->local_part);
+ if (address->domain)
+ free (address->domain);
+ if (address->route)
+ free (address->route);
+ current = address->next;
+ free (address);
+ }
+ *paddress = NULL;
+ }
+}
+
+int
+mu_address_concatenate (mu_address_t to, mu_address_t *from)
+{
+ if (!to || !from || !*from)
+ return EINVAL;
+
+ while (to->next)
+ to = to->next;
+
+ assert (to && !to->next);
+
+ to->next = *from;
+ *from = NULL;
+
+ /* discard the current string cache as it is now inaccurate */
+ if (to->addr)
+ {
+ free (to->addr);
+ to->addr = NULL;
+ }
+
+ to = to->next;
+
+ /* only the first address must have a cache */
+ if (to->addr)
+ {
+ free (to->addr);
+ to->addr = NULL;
+ }
+
+ return 0;
+}
+
+mu_address_t
+_address_get_nth (mu_address_t addr, size_t no)
+{
+ int i;
+
+ for (i = 1; addr; addr = addr->next, i++)
+ if (i == no)
+ break;
+ return addr;
+}
+
+int
+mu_address_get_nth (mu_address_t addr, size_t no, mu_address_t *pret)
+{
+ mu_address_t subaddr = _address_get_nth (addr, no);
+ if (!subaddr)
+ return MU_ERR_NOENT;
+ *pret = mu_address_dup (subaddr);
+ return 0;
+}
+
+
+/* General accessors: */
+#define AC4(a,b,c,d) a ## b ## c ## d
+#define ACCESSOR(action,field) AC4(mu_address_,action,_,field)
+
+#define DECL_SET(field) \
+int \
+ACCESSOR(set, field) (mu_address_t addr, size_t no, const char *buf) \
+{ \
+ char *s; \
+ mu_address_t subaddr; \
+ \
+ if (addr == NULL) \
+ return EINVAL; \
+ \
+ subaddr = _address_get_nth (addr, no); \
+ if (!subaddr) \
+ return MU_ERR_NOENT; \
+ \
+ s = strdup (buf); \
+ if (!s) \
+ return errno; \
+ \
+ free (subaddr->field); \
+ subaddr->field = s; \
+ \
+ return 0; \
+}
+
+#define DECL_SGET(field) \
+int \
+ACCESSOR(sget,field) (mu_address_t addr, size_t no, char const **sptr) \
+{ \
+ mu_address_t subaddr; \
+ \
+ if (addr == NULL) \
+ return EINVAL; \
+ \
+ subaddr = _address_get_nth (addr, no); \
+ if (!subaddr) \
+ return MU_ERR_NOENT; \
+ *sptr = subaddr->field; \
+ return 0; \
+}
+
+#define DECL_GET(field) \
+int \
+ACCESSOR(get,field) (mu_address_t addr, size_t no, char *buf, size_t len, \
+ size_t *n) \
+{ \
+ size_t i; \
+ const char *str; \
+ int status = ACCESSOR(sget, field) (addr, no, &str); \
+ \
+ if (status) \
+ return status; \
+ \
+ i = mu_cpystr (buf, str, len); \
+ if (n) \
+ *n = i; \
+ return 0; \
+}
+
+#define DECL_AGET(field) \
+int \
+ACCESSOR(aget, field) (mu_address_t addr, size_t no, char **buf) \
+{ \
+ const char *str; \
+ int status = ACCESSOR(sget, field) (addr, no, &str); \
+ \
+ if (status) \
+ return status; \
+ \
+ if (str) \
+ { \
+ *buf = strdup (str); \
+ if (!*buf) \
+ status = ENOMEM; \
+ } \
+ else \
+ *buf = NULL; \
+ return status; \
+}
+
+#define DECL_ACCESSORS(field) \
+DECL_SET(field) \
+DECL_SGET(field) \
+DECL_GET(field) \
+DECL_AGET(field)
+
+
+
+/* Personal part */
+DECL_ACCESSORS(personal)
+/* Comments */
+DECL_ACCESSORS(comments)
+/* Email */
+DECL_ACCESSORS(email)
+/* Local part */
+DECL_ACCESSORS(local_part)
+/* Domain */
+DECL_ACCESSORS(domain)
+/* Route */
+DECL_ACCESSORS(route)
+
+
+
+#define format_char(c) do {\
+ if (buflen) \
+ {\
+ *buf++ = c;\
+ buflen--;\
+ }\
+ else\
+ rc++;\
+} while(0)
+
+#define format_string(str) do {\
+ if (buflen) \
+ {\
+ int n = snprintf (buf, buflen, "%s", str);\
+ buf += n;\
+ buflen -= n;\
+ }\
+ else\
+ rc += strlen (str);\
+} while (0)
+
+size_t
+mu_address_format_string (mu_address_t addr, char *buf, size_t buflen)
+{
+ int rc = 0;
+ int comma = 0;
+
+ for (;addr; addr = addr->next)
+ {
+ if (addr->email)
+ {
+ int space = 0;
+
+ if (comma)
+ format_char (',');
+
+ if (addr->personal)
+ {
+ format_char ('"');
+ format_string (addr->personal);
+ format_char ('"');
+ space++;
+ }
+
+ if (addr->comments)
+ {
+ if (space)
+ format_char (' ');
+ format_char ('(');
+ format_string (addr->comments);
+ format_char (')');
+ space++;
+ }
+
+ if (space)
+ format_char (' ');
+ format_char ('<');
+ format_string (addr->email);
+ format_char ('>');
+ comma++;
+ }
+ }
+ format_char (0);
+ return rc;
+}
+
+static int
+_address_is_group (mu_address_t addr)
+{
+ if (addr->personal && !addr->local_part && !addr->domain)
+ return 1;
+ return 0;
+}
+
+static int
+_address_is_email (mu_address_t addr)
+{
+ if (addr->email)
+ return 1;
+ return 0;
+}
+
+static int
+_address_is_unix_mailbox (mu_address_t addr)
+{
+ if (addr->local_part && !addr->email)
+ return 1;
+ return 0;
+}
+
+int
+mu_address_is_group (mu_address_t addr, size_t no, int *yes)
+{
+ mu_address_t subaddr;
+
+ if (addr == NULL)
+ return EINVAL;
+
+ subaddr = _address_get_nth (addr, no);
+ if (!subaddr)
+ return MU_ERR_NOENT;
+
+ if (yes)
+ *yes = _address_is_group (subaddr);
+ return 0;
+}
+
+int
+mu_address_to_string (mu_address_t addr, char *buf, size_t len, size_t *n)
+{
+ size_t i;
+ if (addr == NULL)
+ return EINVAL;
+ if (buf)
+ *buf = '\0';
+
+ if (!addr->addr)
+ {
+ i = mu_address_format_string (addr, NULL, 0);
+ addr->addr = malloc (i + 1);
+ if (!addr->addr)
+ return ENOMEM;
+ mu_address_format_string (addr, addr->addr, i+1);
+ }
+
+ i = mu_cpystr (buf, addr->addr, len);
+ if (n)
+ *n = i;
+ return 0;
+}
+
+int
+mu_address_get_count (mu_address_t addr, size_t *pcount)
+{
+ size_t j;
+ for (j = 0; addr; addr = addr->next, j++)
+ ;
+ if (pcount)
+ *pcount = j;
+ return 0;
+}
+
+int
+mu_address_get_group_count (mu_address_t addr, size_t *pcount)
+{
+ size_t j;
+ for (j = 0; addr; addr = addr->next)
+ {
+ if (_address_is_group (addr))
+ j++;
+ }
+ if (pcount)
+ *pcount = j;
+ return 0;
+}
+
+int
+mu_address_get_email_count (mu_address_t addr, size_t *pcount)
+{
+ size_t j;
+ for (j = 0; addr; addr = addr->next)
+ {
+ if (_address_is_email (addr))
+ j++;
+ }
+ if (pcount)
+ *pcount = j;
+ return 0;
+}
+
+int
+mu_address_get_unix_mailbox_count (mu_address_t addr, size_t *pcount)
+{
+ size_t j;
+ for (j = 0; addr; addr = addr->next)
+ {
+ if (_address_is_unix_mailbox (addr))
+ j++;
+ }
+ if (pcount)
+ *pcount = j;
+ return 0;
+}
+
+int
+mu_address_contains_email (mu_address_t addr, const char *email)
+{
+ for (; addr; addr = addr->next)
+ if (mu_c_strcasecmp (addr->email, email) == 0)
+ return 1;
+ return 0;
+}
+
+mu_address_t
+mu_address_dup (mu_address_t src)
+{
+ mu_address_t dst = calloc (1, sizeof (*dst));
+
+ if (!dst)
+ return NULL;
+
+ /* FIXME: How about:
+ if (src->addr)
+ dst->addr = strdup (src->addr);
+ ?
+ */
+ if (src->comments)
+ dst->comments = strdup (src->comments);
+ if (src->personal)
+ dst->personal = strdup (src->personal);
+ if (src->email)
+ dst->email = strdup (src->email);
+ if (src->local_part)
+ dst->local_part = strdup (src->local_part);
+ if (src->domain)
+ dst->domain = strdup (src->domain);
+ if (src->route)
+ dst->route = strdup (src->route);
+
+ return dst;
+}
+
+int
+mu_address_union (mu_address_t *a, mu_address_t b)
+{
+ mu_address_t last = NULL;
+
+ if (!a || !b)
+ return EINVAL;
+
+ if (!*a)
+ {
+ *a = mu_address_dup (b);
+ if (!*a)
+ return ENOMEM;
+ last = *a;
+ b = b->next;
+ }
+ else
+ {
+ if ((*a)->addr)
+ {
+ free ((*a)->addr);
+ (*a)->addr = NULL;
+ }
+ for (last = *a; last->next; last = last->next)
+ ;
+ }
+
+ for (; b; b = b->next)
+ if (!mu_address_contains_email (*a, b->email))
+ {
+ mu_address_t next = mu_address_dup (b);
+ if (!next)
+ return ENOMEM;
+ last->next = next;
+ last = next;
+ }
+ return 0;
+}
+
diff --git a/libmailutils/alloc.c b/libmailutils/alloc.c
new file mode 100644
index 000000000..05801bb1a
--- /dev/null
+++ b/libmailutils/alloc.c
@@ -0,0 +1,116 @@
+/* Error-proof memory allocation functions.
+ Copyright (C) 2008, 2010 Free Software Foundation, Inc.
+
+ This library is free software; you can redistribute it and/or modify
+ it under the terms of the GNU Lesser General Public License as published by
+ the Free Software Foundation; either version 3, or (at your option)
+ any later version.
+
+ This library 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 Lesser General Public License for more details.
+
+ You should have received a copy of the GNU Lesser General Public License
+ along with GNU Mailutils. If not, see <http://www.gnu.org/licenses/>. */
+
+#ifdef HAVE_CONFIG_H
+# include <config.h>
+#endif
+#include <stdio.h>
+#include <stdlib.h>
+#include <string.h>
+#include <mailutils/error.h>
+#include <mailutils/nls.h>
+#include <mailutils/alloc.h>
+
+void (*mu_alloc_die_hook) (void) = NULL;
+
+void
+mu_alloc_die ()
+{
+ if (mu_alloc_die_hook)
+ mu_alloc_die_hook ();
+ mu_error (_("Not enough memory"));
+ abort ();
+}
+
+void *
+mu_alloc (size_t size)
+{
+ void *p = malloc (size);
+ if (!p)
+ mu_alloc_die ();
+ return p;
+}
+
+void *
+mu_calloc (size_t nmemb, size_t size)
+{
+ void *p = calloc (nmemb, size);
+ if (!p)
+ mu_alloc_die ();
+ return p;
+}
+
+void *
+mu_zalloc (size_t size)
+{
+ void *p = mu_alloc (size);
+ memset (p, 0, size);
+ return p;
+}
+
+void *
+mu_realloc (void *p, size_t size)
+{
+ void *newp = realloc (p, size);
+ if (!newp)
+ mu_alloc_die ();
+ return newp;
+}
+
+char *
+mu_strdup (const char *s)
+{
+ char *news = strdup (s);
+ if (!news)
+ mu_alloc_die ();
+ return news;
+}
+
+/* Copied from gnulib */
+void *
+mu_2nrealloc (void *p, size_t *pn, size_t s)
+{
+ size_t n = *pn;
+
+ if (!p)
+ {
+ if (!n)
+ {
+ /* The approximate size to use for initial small allocation
+ requests, when the invoking code specifies an old size of
+ zero. 64 bytes is the largest "small" request for the
+ GNU C library malloc. */
+ enum { DEFAULT_MXFAST = 64 };
+
+ n = DEFAULT_MXFAST / s;
+ n += !n;
+ }
+ }
+ else
+ {
+ /* Set N = ceil (1.5 * N) so that progress is made if N == 1.
+ Check for overflow, so that N * S stays in size_t range.
+ The check is slightly conservative, but an exact check isn't
+ worth the trouble. */
+ if ((size_t) -1 / 3 * 2 / s <= n)
+ mu_alloc_die ();
+ n += (n + 1) / 2;
+ }
+
+ *pn = n;
+ return mu_realloc (p, n * s);
+}
+
diff --git a/libmailutils/amd.c b/libmailutils/amd.c
new file mode 100644
index 000000000..3cf3dde6c
--- /dev/null
+++ b/libmailutils/amd.c
@@ -0,0 +1,1925 @@
+/* GNU Mailutils -- a suite of utilities for electronic mail
+ Copyright (C) 1999, 2000, 2001, 2002, 2003, 2004, 2005, 2006, 2007,
+ 2008, 2009, 2010 Free Software Foundation, Inc.
+
+ This library is free software; you can redistribute it and/or
+ modify it under the terms of the GNU Lesser General Public
+ License as published by the Free Software Foundation; either
+ version 3 of the License, or (at your option) any later version.
+
+ This library 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
+ Lesser General Public License for more details.
+
+ You should have received a copy of the GNU Lesser General
+ Public License along with this library; if not, write to the
+ Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor,
+ Boston, MA 02110-1301 USA */
+
+/* Mailutils Abstract Mail Directory Layer
+ First draft by Sergey Poznyakoff.
+ Thanks Tang Yong Ping <yongping.tang@radixs.com> for initial
+ patch (although not used here).
+
+ This module provides basic support for "MH" and "Maildir" formats. */
+
+#ifdef HAVE_CONFIG_H
+# include <config.h>
+#endif
+
+#include <sys/types.h>
+#include <stdlib.h>
+#include <stdio.h>
+#include <time.h>
+#include <sys/stat.h>
+#include <fcntl.h>
+#include <unistd.h>
+#include <errno.h>
+#include <dirent.h>
+
+#ifdef WITH_PTHREAD
+# ifdef HAVE_PTHREAD_H
+# ifndef _XOPEN_SOURCE
+# define _XOPEN_SOURCE 500
+# endif
+# include <pthread.h>
+# endif
+#endif
+
+#include <string.h>
+#ifdef HAVE_STRINGS_H
+# include <strings.h>
+#endif
+
+#include <mailutils/cctype.h>
+#include <mailutils/cstr.h>
+#include <mailutils/attribute.h>
+#include <mailutils/body.h>
+#include <mailutils/debug.h>
+#include <mailutils/envelope.h>
+#include <mailutils/error.h>
+#include <mailutils/errno.h>
+#include <mailutils/header.h>
+#include <mailutils/locker.h>
+#include <mailutils/message.h>
+#include <mailutils/mutil.h>
+#include <mailutils/property.h>
+#include <mailutils/stream.h>
+#include <mailutils/url.h>
+#include <mailutils/observer.h>
+#include <mailutils/sys/stream.h>
+#include <mailutils/sys/mailbox.h>
+#include <mailutils/sys/registrar.h>
+#include <mailutils/sys/url.h>
+#include <mailutils/sys/amd.h>
+
+static void amd_destroy (mu_mailbox_t mailbox);
+static int amd_open (mu_mailbox_t, int);
+static int amd_close (mu_mailbox_t);
+static int amd_get_message (mu_mailbox_t, size_t, mu_message_t *);
+static int amd_quick_get_message (mu_mailbox_t mailbox, mu_message_qid_t qid,
+ mu_message_t *pmsg);
+static int amd_append_message (mu_mailbox_t, mu_message_t);
+static int amd_messages_count (mu_mailbox_t, size_t *);
+static int amd_messages_recent (mu_mailbox_t, size_t *);
+static int amd_message_unseen (mu_mailbox_t, size_t *);
+static int amd_expunge (mu_mailbox_t);
+static int amd_sync (mu_mailbox_t);
+static int amd_uidnext (mu_mailbox_t mailbox, size_t *puidnext);
+static int amd_uidvalidity (mu_mailbox_t, unsigned long *);
+static int amd_scan (mu_mailbox_t, size_t, size_t *);
+static int amd_is_updated (mu_mailbox_t);
+static int amd_get_size (mu_mailbox_t, mu_off_t *);
+
+static int amd_body_size (mu_body_t body, size_t *psize);
+static int amd_body_lines (mu_body_t body, size_t *plines);
+
+static int amd_header_fill (void *data, char **pbuf, size_t *plen);
+
+static int amd_get_attr_flags (mu_attribute_t attr, int *pflags);
+static int amd_set_attr_flags (mu_attribute_t attr, int flags);
+static int amd_unset_attr_flags (mu_attribute_t attr, int flags);
+
+static void _amd_message_delete (struct _amd_data *amd,
+ struct _amd_message *msg);
+static int amd_pool_open (struct _amd_message *mhm);
+static int amd_pool_open_count (struct _amd_data *amd);
+static void amd_pool_flush (struct _amd_data *amd);
+static struct _amd_message **amd_pool_lookup (struct _amd_message *mhm);
+
+static int amd_envelope_date (mu_envelope_t envelope, char *buf, size_t len,
+ size_t *psize);
+static int amd_envelope_sender (mu_envelope_t envelope, char *buf, size_t len,
+ size_t *psize);
+
+
+static int amd_body_stream_read (mu_stream_t str, char *buffer,
+ size_t buflen,
+ size_t *pnread);
+static int amd_body_stream_readdelim (mu_stream_t is,
+ char *buffer, size_t buflen,
+ int delim,
+ size_t *pnread);
+static int amd_body_stream_size (mu_stream_t str, mu_off_t *psize);
+static int amd_body_stream_seek (mu_stream_t str, mu_off_t off,
+ mu_off_t *presult);
+
+struct _amd_body_stream
+{
+ struct _mu_stream stream;
+ mu_body_t body;
+ mu_off_t off;
+};
+
+
+/* Operations on message array */
+
+/* Perform binary search for message MSG on a segment of message array
+ of AMD between the indexes FIRST and LAST inclusively.
+ If found, return 0 and store index of the located entry in the
+ variable PRET. Otherwise, return 1 and place into PRET index of
+ the nearest array element that is less than MSG (in the sense of
+ amd->msg_cmp)
+ Indexes are zero-based. */
+
+static int
+amd_msg_bsearch (struct _amd_data *amd, mu_off_t first, mu_off_t last,
+ struct _amd_message *msg,
+ mu_off_t *pret)
+{
+ mu_off_t mid;
+ int rc;
+
+ if (last < first)
+ return 1;
+
+ mid = (first + last) / 2;
+ rc = amd->msg_cmp (amd->msg_array[mid], msg);
+ if (rc > 0)
+ return amd_msg_bsearch (amd, first, mid-1, msg, pret);
+ *pret = mid;
+ if (rc < 0)
+ return amd_msg_bsearch (amd, mid+1, last, msg, pret);
+ /* else */
+ return 0;
+}
+
+/* Search for message MSG in the message array of AMD.
+ If found, return 0 and store index of the located entry in the
+ variable PRET. Otherwise, return 1 and place into PRET index of
+ the array element that is less than MSG (in the sense of
+ amd->msg_cmp)
+ Index returned in PRET is 1-based, so *PRET == 0 means that MSG
+ is less than the very first element of the message array.
+
+ In other words, when amd_msg_lookup() returns 1, the value in *PRET
+ can be regarded as a 0-based index of the array slot where MSG can
+ be inserted */
+
+int
+amd_msg_lookup (struct _amd_data *amd, struct _amd_message *msg,
+ size_t *pret)
+{
+ int rc;
+ mu_off_t i;
+
+ if (amd->msg_count == 0)
+ {
+ *pret = 0;
+ return 1;
+ }
+
+ rc = amd->msg_cmp (msg, amd->msg_array[0]);
+ if (rc < 0)
+ {
+ *pret = 0;
+ return 1;
+ }
+ else if (rc == 0)
+ {
+ *pret = 1;
+ return 0;
+ }
+
+ rc = amd->msg_cmp (msg, amd->msg_array[amd->msg_count - 1]);
+ if (rc > 0)
+ {
+ *pret = amd->msg_count;
+ return 1;
+ }
+ else if (rc == 0)
+ {
+ *pret = amd->msg_count;
+ return 0;
+ }
+
+ rc = amd_msg_bsearch (amd, 0, amd->msg_count - 1, msg, &i);
+ *pret = i + 1;
+ return rc;
+}
+
+#define AMD_MSG_INC 64
+
+/* Prepare the message array for insertion of a new message
+ at position INDEX (zero based), by moving its contents
+ one slot to the right. If necessary, expand the array by
+ AMD_MSG_INC */
+int
+amd_array_expand (struct _amd_data *amd, size_t index)
+{
+ if (amd->msg_count == amd->msg_max)
+ {
+ struct _amd_message **p;
+
+ amd->msg_max += AMD_MSG_INC; /* FIXME: configurable? */
+ p = realloc (amd->msg_array, amd->msg_max * amd->msg_size);
+ if (!p)
+ {
+ amd->msg_max -= AMD_MSG_INC;
+ return ENOMEM;
+ }
+ amd->msg_array = p;
+ }
+ memmove (&amd->msg_array[index+1], &amd->msg_array[index],
+ (amd->msg_count-index) * amd->msg_size);
+ amd->msg_count++;
+ return 0;
+}
+
+/* Shrink the message array by removing element at INDEX-1 and
+ shifting left by one position all the elements on the right of
+ it. */
+int
+amd_array_shrink (struct _amd_data *amd, size_t index)
+{
+ memmove (&amd->msg_array[index-1], &amd->msg_array[index],
+ (amd->msg_count-index) * amd->msg_size);
+ amd->msg_count--;
+ return 0;
+}
+
+
+int
+amd_init_mailbox (mu_mailbox_t mailbox, size_t amd_size,
+ struct _amd_data **pamd)
+{
+ int status;
+ struct _amd_data *amd;
+
+ if (mailbox == NULL)
+ return MU_ERR_MBX_NULL;
+ if (amd_size < sizeof (*amd))
+ return EINVAL;
+
+ amd = mailbox->data = calloc (1, amd_size);
+ if (mailbox->data == NULL)
+ return ENOMEM;
+
+ /* Back pointer. */
+ amd->mailbox = mailbox;
+
+ status = mu_url_aget_path (mailbox->url, &amd->name);
+ if (status)
+ {
+ free (amd);
+ mailbox->data = NULL;
+ return status;
+ }
+
+ /* Overloading the defaults. */
+ mailbox->_destroy = amd_destroy;
+
+ mailbox->_open = amd_open;
+ mailbox->_close = amd_close;
+
+ /* Overloading of the entire mailbox object methods. */
+ mailbox->_get_message = amd_get_message;
+ mailbox->_quick_get_message = amd_quick_get_message;
+ mailbox->_append_message = amd_append_message;
+ mailbox->_messages_count = amd_messages_count;
+ mailbox->_messages_recent = amd_messages_recent;
+ mailbox->_message_unseen = amd_message_unseen;
+ mailbox->_expunge = amd_expunge;
+ mailbox->_sync = amd_sync;
+ mailbox->_uidvalidity = amd_uidvalidity;
+ mailbox->_uidnext = amd_uidnext;
+
+ mailbox->_scan = amd_scan;
+ mailbox->_is_updated = amd_is_updated;
+
+ mailbox->_get_size = amd_get_size;
+
+ MU_DEBUG1 (mailbox->debug, MU_DEBUG_TRACE1, "amd_init(%s)\n", amd->name);
+ *pamd = amd;
+ return 0;
+}
+
+static void
+amd_destroy (mu_mailbox_t mailbox)
+{
+ struct _amd_data *amd = mailbox->data;
+ size_t i;
+
+ if (!amd)
+ return;
+
+ amd_pool_flush (amd);
+ mu_monitor_wrlock (mailbox->monitor);
+ for (i = 0; i < amd->msg_count; i++)
+ {
+ mu_message_destroy (&amd->msg_array[i]->message, amd->msg_array[i]);
+ free (amd->msg_array[i]);
+ }
+ free (amd->msg_array);
+
+ if (amd->name)
+ free (amd->name);
+
+ free (amd);
+ mailbox->data = NULL;
+ mu_monitor_unlock (mailbox->monitor);
+}
+
+static int
+amd_open (mu_mailbox_t mailbox, int flags)
+{
+ struct _amd_data *amd = mailbox->data;
+ struct stat st;
+
+ mailbox->flags = flags;
+ if (stat (amd->name, &st) < 0)
+ {
+ if ((flags & MU_STREAM_CREAT) && errno == ENOENT)
+ {
+ int rc;
+ int perms = mu_stream_flags_to_mode (flags, 1);
+ if (mkdir (amd->name, S_IRUSR|S_IWUSR|S_IXUSR|perms))
+ return errno;
+ if (stat (amd->name, &st) < 0)
+ return errno;
+ if (amd->create && (rc = amd->create (amd, flags)))
+ return rc;
+ }
+ else
+ return errno;
+ }
+
+ if (!S_ISDIR (st.st_mode))
+ return EINVAL;
+
+ if (mailbox->locker == NULL)
+ mu_locker_create (&mailbox->locker, "/dev/null", 0);
+
+ return 0;
+}
+
+static int
+amd_close (mu_mailbox_t mailbox)
+{
+ struct _amd_data *amd;
+ int i;
+
+ if (!mailbox)
+ return MU_ERR_MBX_NULL;
+
+ amd = mailbox->data;
+
+ /* Destroy all cached data */
+ amd_pool_flush (amd);
+ mu_monitor_wrlock (mailbox->monitor);
+ for (i = 0; i < amd->msg_count; i++)
+ {
+ mu_message_destroy (&amd->msg_array[i]->message, amd->msg_array[i]);
+ free (amd->msg_array[i]);
+ }
+ free (amd->msg_array);
+ amd->msg_array = NULL;
+
+ amd->msg_count = 0; /* number of messages in the list */
+ amd->msg_max = 0; /* maximum message buffer capacity */
+
+ amd->uidvalidity = 0;
+ mu_monitor_unlock (mailbox->monitor);
+
+ return 0;
+}
+
+static int
+amd_message_qid (mu_message_t msg, mu_message_qid_t *pqid)
+{
+ struct _amd_message *mhm = mu_message_get_owner (msg);
+
+ return mhm->amd->cur_msg_file_name (mhm, pqid);
+}
+
+struct _amd_message *
+_amd_get_message (struct _amd_data *amd, size_t msgno)
+{
+ msgno--;
+ if (msgno >= amd->msg_count)
+ return NULL;
+ return amd->msg_array[msgno];
+}
+
+static int
+_amd_attach_message (mu_mailbox_t mailbox, struct _amd_message *mhm,
+ mu_message_t *pmsg)
+{
+ int status;
+ mu_message_t msg;
+
+ /* Check if we already have it. */
+ if (mhm->message)
+ {
+ if (pmsg)
+ *pmsg = mhm->message;
+ return 0;
+ }
+
+ /* Get an empty message struct. */
+ status = mu_message_create (&msg, mhm);
+ if (status != 0)
+ return status;
+
+ /* Set the header. */
+ {
+ mu_header_t header = NULL;
+ status = mu_header_create (&header, NULL, 0);
+ if (status != 0)
+ {
+ mu_message_destroy (&msg, mhm);
+ return status;
+ }
+ mu_header_set_fill (header, amd_header_fill, msg);
+ /*FIXME:
+ mu_header_set_get_fvalue (header, amd_header_get_fvalue, msg);
+ */
+ mu_message_set_header (msg, header, mhm);
+ }
+
+ /* Set the attribute. */
+ {
+ mu_attribute_t attribute;
+ status = mu_attribute_create (&attribute, msg);
+ if (status != 0)
+ {
+ mu_message_destroy (&msg, mhm);
+ return status;
+ }
+ mu_attribute_set_get_flags (attribute, amd_get_attr_flags, msg);
+ mu_attribute_set_set_flags (attribute, amd_set_attr_flags, msg);
+ mu_attribute_set_unset_flags (attribute, amd_unset_attr_flags, msg);
+ mu_message_set_attribute (msg, attribute, mhm);
+ }
+
+ /* Prepare the body. */
+ {
+ mu_body_t body = NULL;
+ struct _amd_body_stream *str;
+
+ if ((status = mu_body_create (&body, msg)) != 0)
+ return status;
+
+ str = (struct _amd_body_stream *)
+ _mu_stream_create (sizeof (*str),
+ mailbox->flags | MU_STREAM_SEEK);
+ if (!str)
+ {
+ mu_body_destroy (&body, msg);
+ mu_message_destroy (&msg, mhm);
+ return ENOMEM;
+ }
+ str->stream.read = amd_body_stream_read;
+ str->stream.readdelim = amd_body_stream_readdelim;
+ str->stream.size = amd_body_stream_size;
+ str->stream.seek = amd_body_stream_seek;
+ mu_body_set_stream (body, (mu_stream_t) str, msg);
+ mu_body_clear_modified (body);
+ mu_body_set_size (body, amd_body_size, msg);
+ mu_body_set_lines (body, amd_body_lines, msg);
+ mu_message_set_body (msg, body, mhm);
+ str->body = body;
+ }
+
+ /* Set the envelope. */
+ {
+ mu_envelope_t envelope = NULL;
+ status = mu_envelope_create (&envelope, msg);
+ if (status != 0)
+ {
+ mu_message_destroy (&msg, mhm);
+ return status;
+ }
+ mu_envelope_set_sender (envelope, amd_envelope_sender, msg);
+ mu_envelope_set_date (envelope, amd_envelope_date, msg);
+ mu_message_set_envelope (msg, envelope, mhm);
+ }
+
+ /* Set the UID. */
+ if (mhm->amd->message_uid)
+ mu_message_set_uid (msg, mhm->amd->message_uid, mhm);
+ mu_message_set_qid (msg, amd_message_qid, mhm);
+
+ /* Attach the message to the mailbox mbox data. */
+ mhm->message = msg;
+ mu_message_set_mailbox (msg, mailbox, mhm);
+
+ /* Some of mu_message_set_ functions above mark message as modified.
+ Undo it now.
+
+ FIXME: Marking message as modified is not always appropriate. Find
+ a better way. */
+
+ mu_message_clear_modified (msg);
+
+ if (pmsg)
+ *pmsg = msg;
+
+ return 0;
+}
+
+static int
+amd_get_message (mu_mailbox_t mailbox, size_t msgno, mu_message_t *pmsg)
+{
+ int status;
+ struct _amd_data *amd = mailbox->data;
+ struct _amd_message *mhm;
+
+ /* Sanity checks. */
+ if (pmsg == NULL)
+ return MU_ERR_OUT_PTR_NULL;
+ if (amd == NULL)
+ return EINVAL;
+
+ /* If we did not start a scanning yet do it now. */
+ if (amd->msg_count == 0)
+ {
+ status = amd->scan0 (mailbox, 1, NULL, 0);
+ if (status != 0)
+ return status;
+ }
+
+ if ((mhm = _amd_get_message (amd, msgno)) == NULL)
+ return EINVAL;
+ return _amd_attach_message (mailbox, mhm, pmsg);
+}
+
+static int
+amd_quick_get_message (mu_mailbox_t mailbox, mu_message_qid_t qid,
+ mu_message_t *pmsg)
+{
+ int status;
+ struct _amd_data *amd = mailbox->data;
+ if (amd->msg_count)
+ {
+ mu_message_qid_t vqid;
+ mu_message_t msg = amd->msg_array[0]->message;
+ status = mu_message_get_qid (msg, &vqid);
+ if (status)
+ return status;
+ status = strcmp (qid, vqid);
+ free (vqid);
+ if (status)
+ return MU_ERR_EXISTS;
+ *pmsg = msg;
+ }
+ else if (amd->qfetch)
+ {
+ status = amd->qfetch (amd, qid);
+ if (status)
+ return status;
+ return _amd_attach_message (mailbox, amd->msg_array[0], pmsg);
+ }
+
+ return ENOSYS;
+}
+
+static FILE *
+_amd_tempfile(struct _amd_data *amd, char **namep)
+{
+ int fd = mu_tempfile (amd->name, namep);
+ if (fd == -1)
+ return NULL;
+ return fdopen (fd, "w");
+}
+
+static int
+_amd_delim (char *str)
+{
+ if (str[0] == '-')
+ {
+ for (; *str == '-'; str++)
+ ;
+ for (; *str == ' ' || *str == '\t'; str++)
+ ;
+ }
+ return str[0] == '\n';
+}
+
+static int
+_amd_message_save (struct _amd_data *amd, struct _amd_message *mhm,
+ int expunge)
+{
+ mu_stream_t stream = NULL;
+ char *name = NULL, *buf = NULL, *msg_name, *old_name;
+ size_t n;
+ size_t bsize;
+ size_t nlines, nbytes;
+ size_t new_body_start, new_header_lines;
+ FILE *fp;
+ mu_message_t msg = mhm->message;
+ mu_header_t hdr;
+ int status;
+ mu_attribute_t attr;
+ mu_body_t body;
+ const char *sbuf;
+ mu_envelope_t env = NULL;
+ char statbuf[MU_STATUS_BUF_SIZE];
+
+ status = mu_message_size (msg, &bsize);
+ if (status)
+ return status;
+
+ status = amd->new_msg_file_name (mhm, mhm->attr_flags, expunge, &msg_name);
+ if (status)
+ return status;
+ if (!msg_name)
+ {
+ /* Unlink the original file */
+ char *old_name;
+ status = amd->cur_msg_file_name (mhm, &old_name);
+ free (msg_name);
+ if (status == 0 && unlink (old_name))
+ status = errno;
+ free (old_name);
+ return status;
+ }
+
+ fp = _amd_tempfile (mhm->amd, &name);
+ if (!fp)
+ {
+ free (msg_name);
+ return errno;
+ }
+
+ /* Try to allocate large buffer */
+ for (; bsize > 1; bsize /= 2)
+ if ((buf = malloc (bsize)))
+ break;
+
+ if (!bsize)
+ {
+ unlink (name);
+ free (name);
+ free (msg_name);
+ return ENOMEM;
+ }
+
+ /* Copy flags */
+ mu_message_get_header (msg, &hdr);
+ mu_header_get_streamref (hdr, &stream);
+ status = mu_stream_seek (stream, 0, MU_SEEK_SET, NULL);
+ if (status)
+ {
+ /* FIXME: Provide a common exit point for all error
+ cases */
+ unlink (name);
+ free (name);
+ free (msg_name);
+ mu_stream_destroy (&stream);
+ return status;
+ }
+
+ nlines = nbytes = 0;
+ while ((status = mu_stream_readline (stream, buf, bsize, &n)) == 0
+ && n != 0)
+ {
+ if (_amd_delim (buf))
+ break;
+
+ if (!(mu_c_strncasecmp (buf, "status:", 7) == 0
+ || mu_c_strncasecmp (buf, "x-imapbase:", 11) == 0
+ || mu_c_strncasecmp (buf, "x-uid:", 6) == 0
+ || mu_c_strncasecmp (buf,
+ MU_HEADER_ENV_DATE ":", sizeof (MU_HEADER_ENV_DATE)) == 0
+ || mu_c_strncasecmp (buf,
+ MU_HEADER_ENV_SENDER ":", sizeof (MU_HEADER_ENV_SENDER)) == 0))
+ {
+ nlines++;
+ nbytes += fprintf (fp, "%s", buf);
+ }
+ }
+ mu_stream_destroy (&stream);
+
+ /* Add imapbase */
+ if (!(amd->mailbox->flags & MU_STREAM_APPEND)
+ && amd->next_uid
+ && (!amd->msg_array || (amd->msg_array[0] == mhm))) /*FIXME*/
+ {
+ nbytes += fprintf (fp, "X-IMAPbase: %lu %u\n",
+ (unsigned long) amd->uidvalidity,
+ (unsigned) amd->next_uid (amd));
+ nlines++;
+ }
+
+ mu_message_get_envelope (msg, &env);
+ if (mu_envelope_sget_date (env, &sbuf) == 0)
+ {
+ /* NOTE: buffer might be terminated with \n */
+ while (*sbuf && mu_isspace (*sbuf))
+ sbuf++;
+ nbytes += fprintf (fp, "%s: %s", MU_HEADER_ENV_DATE, sbuf);
+
+ if (*sbuf && sbuf[strlen (sbuf) - 1] != '\n')
+ nbytes += fprintf (fp, "\n");
+
+ nlines++;
+ }
+
+ if (mu_envelope_sget_sender (env, &sbuf) == 0)
+ {
+ fprintf (fp, "%s: %s\n", MU_HEADER_ENV_SENDER, sbuf);
+ nlines++;
+ }
+
+ /* Add status */
+ mu_message_get_attribute (msg, &attr);
+ mu_attribute_to_string (attr, statbuf, sizeof (statbuf), &n);
+ if (n)
+ {
+ nbytes += fprintf (fp, "Status: %s\n", statbuf);
+ nlines++;
+ }
+ nbytes += fprintf (fp, "\n");
+ nlines++;
+
+ new_header_lines = nlines;
+ new_body_start = nbytes;
+
+ /* Copy message body */
+
+ mu_message_get_body (msg, &body);
+ mu_body_get_streamref (body, &stream);
+ status = mu_stream_seek (stream, 0, MU_SEEK_SET, NULL);
+ if (status)
+ {
+ unlink (name);
+ free (name);
+ free (msg_name);
+ mu_stream_destroy (&stream);
+ return status;
+ }
+
+ nlines = 0;
+ while (mu_stream_read (stream, buf, bsize, &n) == 0 && n != 0)
+ {
+ char *p;
+ for (p = buf; p < buf + n; p++)
+ if (*p == '\n')
+ nlines++;
+ fwrite (buf, 1, n, fp);
+ nbytes += n;
+ }
+ mu_stream_destroy (&stream);
+
+ mhm->header_lines = new_header_lines;
+ mhm->body_start = new_body_start;
+ mhm->body_lines = nlines;
+ mhm->body_end = nbytes;
+
+ free (buf);
+ fclose (fp);
+
+ status = amd->cur_msg_file_name (mhm, &old_name);
+ if (status == 0)
+ {
+ if (rename (name, msg_name))
+ status = errno;
+ else
+ {
+ mode_t perms;
+
+ perms = mu_stream_flags_to_mode (amd->mailbox->flags, 0);
+ if (perms != 0)
+ {
+ /* It is documented that the mailbox permissions are
+ affected by the current umask, so take it into account
+ here.
+ FIXME: I'm still not sure we should honor umask, though.
+ --gray
+ */
+ mode_t mask = umask (0);
+ chmod (msg_name, (0600 | perms) & ~mask);
+ umask (mask);
+ }
+ if (strcmp (old_name, msg_name))
+ /* Unlink original message */
+ unlink (old_name);
+ }
+ free (old_name);
+
+ mhm->orig_flags = mhm->attr_flags;
+ }
+ free (msg_name);
+ free (name);
+
+ return status;
+}
+
+static int
+amd_append_message (mu_mailbox_t mailbox, mu_message_t msg)
+{
+ int status;
+ struct _amd_data *amd = mailbox->data;
+ struct _amd_message *mhm;
+
+ if (!mailbox)
+ return MU_ERR_MBX_NULL;
+ if (!msg)
+ return EINVAL;
+
+ mhm = calloc (1, amd->msg_size);
+ if (!mhm)
+ return ENOMEM;
+
+ /* If we did not start a scanning yet do it now. */
+ if (amd->msg_count == 0)
+ {
+ status = amd->scan0 (mailbox, 1, NULL, 0);
+ if (status != 0)
+ {
+ free (mhm);
+ return status;
+ }
+ }
+
+ amd->has_new_msg = 1;
+
+ mhm->amd = amd;
+ if (amd->msg_init_delivery)
+ {
+ status = amd->msg_init_delivery (amd, mhm);
+ if (status)
+ {
+ free (mhm);
+ return status;
+ }
+ }
+
+ mhm->message = msg;
+ status = _amd_message_save (amd, mhm, 0);
+ if (status)
+ {
+ free (mhm);
+ return status;
+ }
+
+ mhm->message = NULL;
+ /* Insert and re-scan the message */
+ status = _amd_message_insert (amd, mhm);
+ if (status)
+ {
+ free (mhm);
+ return status;
+ }
+
+ if (amd->msg_finish_delivery)
+ status = amd->msg_finish_delivery (amd, mhm, msg);
+
+ if (status == 0 && mailbox->observable)
+ {
+ char *qid;
+ if (amd->cur_msg_file_name (mhm, &qid) == 0)
+ {
+ mu_observable_notify (mailbox->observable, MU_EVT_MESSAGE_APPEND,
+ qid);
+ free (qid);
+ }
+ }
+
+ return status;
+}
+
+static int
+amd_messages_count (mu_mailbox_t mailbox, size_t *pcount)
+{
+ struct _amd_data *amd = mailbox->data;
+
+ if (amd == NULL)
+ return EINVAL;
+
+ if (!amd_is_updated (mailbox))
+ return amd->scan0 (mailbox, amd->msg_count, pcount, 0);
+
+ if (pcount)
+ *pcount = amd->msg_count;
+
+ return 0;
+}
+
+/* A "recent" message is the one not marked with MU_ATTRIBUTE_SEEN
+ ('O' in the Status header), i.e. a message that is first seen
+ by the current session (see attributes.h) */
+static int
+amd_messages_recent (mu_mailbox_t mailbox, size_t *pcount)
+{
+ struct _amd_data *amd = mailbox->data;
+ size_t count, i;
+
+ /* If we did not start a scanning yet do it now. */
+ if (amd->msg_count == 0)
+ {
+ int status = amd->scan0 (mailbox, 1, NULL, 0);
+ if (status != 0)
+ return status;
+ }
+ count = 0;
+ for (i = 0; i < amd->msg_count; i++)
+ {
+ if (MU_ATTRIBUTE_IS_UNSEEN(amd->msg_array[i]->attr_flags))
+ count++;
+ }
+ *pcount = count;
+ return 0;
+}
+
+/* An "unseen" message is the one that has not been read yet */
+static int
+amd_message_unseen (mu_mailbox_t mailbox, size_t *pmsgno)
+{
+ struct _amd_data *amd = mailbox->data;
+ size_t i;
+
+ /* If we did not start a scanning yet do it now. */
+ if (amd->msg_count == 0)
+ {
+ int status = amd->scan0 (mailbox, 1, NULL, 0);
+ if (status != 0)
+ return status;
+ }
+
+ for (i = 0; i < amd->msg_count; i++)
+ {
+ if (MU_ATTRIBUTE_IS_UNREAD(amd->msg_array[0]->attr_flags))
+ {
+ *pmsgno = i + 1;
+ break;
+ }
+ }
+ return 0;
+}
+
+static char *
+make_size_file_name (struct _amd_data *amd)
+{
+ size_t size = strlen (amd->name) + 1 + sizeof (MU_AMD_SIZE_FILE_NAME);
+ char *name = malloc (size);
+ if (name)
+ {
+ strcpy (name, amd->name);
+ strcat (name, "/");
+ strcat (name, MU_AMD_SIZE_FILE_NAME);
+ }
+ return name;
+}
+
+static int
+read_size_file (struct _amd_data *amd, mu_off_t *psize)
+{
+ FILE *fp;
+ int rc;
+ char *name = make_size_file_name (amd);
+ if (!name)
+ return 1;
+ fp = fopen (name, "r");
+ if (fp)
+ {
+ unsigned long size;
+ if (fscanf (fp, "%lu", &size) == 1)
+ {
+ *psize = size;
+ rc = 0;
+ }
+ else
+ rc = 1;
+ fclose (fp);
+ }
+ else
+ rc = 1;
+ free (name);
+ return rc;
+}
+
+static int
+write_size_file (struct _amd_data *amd, mu_off_t size)
+{
+ FILE *fp;
+ int rc;
+ char *name = make_size_file_name (amd);
+ if (!name)
+ return 1;
+ fp = fopen (name, "w");
+ if (fp)
+ {
+ fprintf (fp, "%lu", (unsigned long) size);
+ fclose (fp);
+ rc = 0;
+ }
+ else
+ rc = 1;
+ free (name);
+ return rc;
+}
+
+static int
+compute_mailbox_size (struct _amd_data *amd, const char *name, mu_off_t *psize)
+{
+ DIR *dir;
+ struct dirent *entry;
+ char *buf;
+ size_t bufsize;
+ size_t dirlen;
+ size_t flen;
+ int status = 0;
+ struct stat sb;
+
+ dir = opendir (name);
+ if (!dir)
+ return errno;
+
+ dirlen = strlen (name);
+ bufsize = dirlen + 32;
+ buf = malloc (bufsize);
+ if (!buf)
+ {
+ closedir (dir);
+ return ENOMEM;
+ }
+
+ strcpy (buf, name);
+ if (buf[dirlen-1] != '/')
+ buf[++dirlen - 1] = '/';
+
+ while ((entry = readdir (dir)))
+ {
+ switch (entry->d_name[0])
+ {
+ case '.':
+ break;
+
+ default:
+ flen = strlen (entry->d_name);
+ if (dirlen + flen + 1 > bufsize)
+ {
+ bufsize = dirlen + flen + 1;
+ buf = realloc (buf, bufsize);
+ if (!buf)
+ {
+ status = ENOMEM;
+ break;
+ }
+ }
+ strcpy (buf + dirlen, entry->d_name);
+ if (stat (buf, &sb) == 0)
+ {
+ if (S_ISREG (sb.st_mode))
+ *psize += sb.st_size;
+ else if (S_ISDIR (sb.st_mode))
+ compute_mailbox_size (amd, buf, psize);
+ }
+ /* FIXME: else? */
+ break;
+ }
+ }
+
+ free (buf);
+
+ closedir (dir);
+ return 0;
+}
+
+static int
+amd_expunge (mu_mailbox_t mailbox)
+{
+ struct _amd_data *amd = mailbox->data;
+ struct _amd_message *mhm;
+ size_t i;
+ int updated = amd->has_new_msg;
+
+ if (amd == NULL)
+ return EINVAL;
+
+ if (amd->msg_count == 0)
+ return 0;
+
+ /* Find the first dirty(modified) message. */
+ for (i = 0; i < amd->msg_count; i++)
+ {
+ mhm = amd->msg_array[i];
+ if ((mhm->attr_flags & MU_ATTRIBUTE_MODIFIED) ||
+ (mhm->attr_flags & MU_ATTRIBUTE_DELETED) ||
+ (mhm->message && mu_message_is_modified (mhm->message)))
+ break;
+ }
+
+ while (i < amd->msg_count)
+ {
+ mhm = amd->msg_array[i];
+
+ if (mhm->attr_flags & MU_ATTRIBUTE_DELETED)
+ {
+ int rc;
+ char *old_name;
+ char *new_name;
+
+ rc = amd->cur_msg_file_name (mhm, &old_name);
+ if (rc)
+ return rc;
+ rc = amd->new_msg_file_name (mhm, mhm->attr_flags, 1,
+ &new_name);
+ if (rc)
+ {
+ free (old_name);
+ return rc;
+ }
+
+ if (new_name)
+ {
+ /* FIXME: It may be a good idea to have a capability flag
+ in struct _amd_data indicating that no actual removal
+ is needed (e.g. for traditional MH). It will allow to
+ bypass lots of no-op code here. */
+ if (strcmp (old_name, new_name))
+ /* Rename original message */
+ rename (old_name, new_name);
+ }
+ else
+ /* Unlink original file */
+ unlink (old_name);
+
+ free (old_name);
+ free (new_name);
+
+ _amd_message_delete (amd, mhm);
+ updated = 1;
+ /* Do not increase i! */
+ }
+ else
+ {
+ if ((mhm->attr_flags & MU_ATTRIBUTE_MODIFIED)
+ || (mhm->message && mu_message_is_modified (mhm->message)))
+ {
+ _amd_attach_message (mailbox, mhm, NULL);
+ _amd_message_save (amd, mhm, 1);
+ updated = 1;
+ }
+ i++; /* Move to the next message */
+ }
+ }
+
+ if (updated && !amd->mailbox_size)
+ {
+ mu_off_t size = 0;
+ int rc = compute_mailbox_size (amd, amd->name, &size);
+ if (rc == 0)
+ write_size_file (amd, size);
+ }
+ return 0;
+}
+
+static int
+amd_sync (mu_mailbox_t mailbox)
+{
+ struct _amd_data *amd = mailbox->data;
+ struct _amd_message *mhm;
+ size_t i;
+ int updated = amd->has_new_msg;
+
+ if (amd == NULL)
+ return EINVAL;
+
+ if (amd->msg_count == 0)
+ return 0;
+
+ /* Find the first dirty(modified) message. */
+ for (i = 0; i < amd->msg_count; i++)
+ {
+ mhm = amd->msg_array[i];
+ if ((mhm->attr_flags & MU_ATTRIBUTE_MODIFIED)
+ || (mhm->message && mu_message_is_modified (mhm->message)))
+ break;
+ }
+
+ for ( ; i < amd->msg_count; i++)
+ {
+ mhm = amd->msg_array[i];
+
+ if ((mhm->attr_flags & MU_ATTRIBUTE_MODIFIED)
+ || (mhm->message && mu_message_is_modified (mhm->message)))
+ {
+ _amd_attach_message (mailbox, mhm, NULL);
+ _amd_message_save (amd, mhm, 0);
+ updated = 1;
+ }
+ }
+
+ if (updated && !amd->mailbox_size)
+ {
+ mu_off_t size = 0;
+ int rc = compute_mailbox_size (amd, amd->name, &size);
+ if (rc == 0)
+ write_size_file (amd, size);
+ }
+
+ return 0;
+}
+
+static int
+amd_uidvalidity (mu_mailbox_t mailbox, unsigned long *puidvalidity)
+{
+ struct _amd_data *amd = mailbox->data;
+ int status = amd_messages_count (mailbox, NULL);
+ if (status != 0)
+ return status;
+ /* If we did not start a scanning yet do it now. */
+ if (amd->msg_count == 0)
+ {
+ status = amd->scan0 (mailbox, 1, NULL, 0);
+ if (status != 0)
+ return status;
+ }
+ if (puidvalidity)
+ *puidvalidity = amd->uidvalidity;
+ return 0;
+}
+
+static int
+amd_uidnext (mu_mailbox_t mailbox, size_t *puidnext)
+{
+ struct _amd_data *amd = mailbox->data;
+ int status;
+
+ if (!amd->next_uid)
+ return ENOSYS;
+ status = mu_mailbox_messages_count (mailbox, NULL);
+ if (status != 0)
+ return status;
+ /* If we did not start a scanning yet do it now. */
+ if (amd->msg_count == 0)
+ {
+ status = amd->scan0 (mailbox, 1, NULL, 0);
+ if (status != 0)
+ return status;
+ }
+ if (puidnext)
+ *puidnext = amd->next_uid (amd);
+ return 0;
+}
+
+/* FIXME: effectively the same as mbox_cleanup */
+void
+amd_cleanup (void *arg)
+{
+ mu_mailbox_t mailbox = arg;
+ mu_monitor_unlock (mailbox->monitor);
+ mu_locker_unlock (mailbox->locker);
+}
+
+/* Insert message msg into the message list on the appropriate position */
+int
+_amd_message_insert (struct _amd_data *amd, struct _amd_message *msg)
+{
+ size_t index;
+
+ if (amd_msg_lookup (amd, msg, &index))
+ {
+ /* Not found. Index is the index of the array cell where msg
+ must be placed */
+ int rc = amd_array_expand (amd, index);
+ if (rc)
+ return rc;
+ amd->msg_array[index] = msg;
+ msg->amd = amd;
+ }
+ else
+ {
+ /*FIXME: Found? Shouldn't happen */
+ return EEXIST;
+ }
+ return 0;
+}
+
+static void
+_amd_message_delete (struct _amd_data *amd, struct _amd_message *msg)
+{
+ size_t index;
+ struct _amd_message **pp;
+
+ if (amd_msg_lookup (amd, msg, &index))
+ {
+ /* FIXME: Not found? */
+ return;
+ }
+
+ msg = _amd_get_message (amd, index);
+
+ pp = amd_pool_lookup (msg);
+ if (pp)
+ *pp = NULL;
+
+ mu_message_destroy (&msg->message, msg);
+ if (amd->msg_free)
+ amd->msg_free (msg);
+ free (msg);
+ amd_array_shrink (amd, index);
+}
+
+/* Scan given message and fill amd_message_t fields.
+ NOTE: the function assumes mhm->stream != NULL. */
+static int
+amd_scan_message (struct _amd_message *mhm)
+{
+ mu_stream_t stream = mhm->stream;
+ char buf[1024];
+ size_t off;
+ size_t n;
+ int status;
+ int in_header = 1;
+ size_t hlines = 0;
+ size_t blines = 0;
+ size_t body_start = 0;
+ struct stat st;
+ char *msg_name;
+
+ /* Check if the message was modified after the last scan */
+ status = mhm->amd->cur_msg_file_name (mhm, &msg_name);
+ if (status)
+ return status;
+
+ if (stat (msg_name, &st) == 0 && st.st_mtime == mhm->mtime)
+ {
+ /* Nothing to do */
+ free (msg_name);
+ return 0;
+ }
+ free (msg_name);
+
+ off = 0;
+ status = mu_stream_seek (stream, 0, MU_SEEK_SET, NULL);
+ if (status == 0)
+ while ((status = mu_stream_readline (stream, buf, sizeof (buf), &n)) == 0
+ && n != 0)
+ {
+ if (in_header)
+ {
+ if (buf[0] == '\n')
+ {
+ in_header = 0;
+ body_start = off + 1;
+ }
+ if (buf[n - 1] == '\n')
+ hlines++;
+
+ /* Process particular attributes */
+ if (mu_c_strncasecmp (buf, "status:", 7) == 0)
+ {
+ int deleted = mhm->attr_flags & MU_ATTRIBUTE_DELETED;
+ mu_string_to_flags (buf, &mhm->attr_flags);
+ mhm->attr_flags |= deleted;
+ }
+ else if (mu_c_strncasecmp (buf, "x-imapbase:", 11) == 0)
+ {
+ char *p;
+ mhm->amd->uidvalidity = strtoul (buf + 11, &p, 10);
+ /* second number is next uid. Ignored */
+ }
+ }
+ else
+ {
+ if (buf[n - 1] == '\n')
+ blines++;
+ }
+ off += n;
+ }
+
+ if (status == 0)
+ {
+ mhm->mtime = st.st_mtime;
+ if (!body_start)
+ body_start = off;
+ mhm->header_lines = hlines;
+ mhm->body_lines = blines;
+ mhm->body_start = body_start;
+ mhm->body_end = off;
+ }
+ return status;
+}
+
+static int
+amd_scan (mu_mailbox_t mailbox, size_t msgno, size_t *pcount)
+{
+ struct _amd_data *amd = mailbox->data;
+
+ if (! amd_is_updated (mailbox))
+ return amd->scan0 (mailbox, msgno, pcount, 1);
+
+ if (pcount)
+ *pcount = amd->msg_count;
+
+ return 0;
+}
+
+/* Is the internal representation of the mailbox up to date.
+ Return 1 if so, 0 otherwise. */
+static int
+amd_is_updated (mu_mailbox_t mailbox)
+{
+ struct stat st;
+ struct _amd_data *amd = mailbox->data;
+
+ if (stat (amd->name, &st) < 0)
+ return 1;
+
+ return amd->mtime == st.st_mtime;
+}
+
+static int
+amd_get_size (mu_mailbox_t mailbox, mu_off_t *psize)
+{
+ struct _amd_data *amd = mailbox->data;
+ if (amd->mailbox_size)
+ return amd->mailbox_size (mailbox, psize);
+ *psize = 0;
+ if (read_size_file (amd, psize))
+ {
+ int rc = compute_mailbox_size (amd, amd->name, psize);
+ if (rc == 0)
+ write_size_file (amd, *psize);
+ return rc;
+ }
+ return 0;
+}
+
+/* Return number of open streams residing in a message pool */
+static int
+amd_pool_open_count (struct _amd_data *amd)
+{
+ int cnt = amd->pool_last - amd->pool_first;
+ if (cnt < 0)
+ cnt += MAX_OPEN_STREAMS;
+ return cnt;
+}
+
+/* Look up a _amd_message in the pool of open messages.
+ If the message is found in the pool, returns the address of
+ the pool slot occupied by it. Otherwise returns NULL. */
+static struct _amd_message **
+amd_pool_lookup (struct _amd_message *mhm)
+{
+ struct _amd_data *amd = mhm->amd;
+ int i;
+
+ for (i = amd->pool_first; i != amd->pool_last; )
+ {
+ if (amd->msg_pool[i] == mhm)
+ return &amd->msg_pool[i];
+ if (++i == MAX_OPEN_STREAMS)
+ i = 0;
+ }
+ return NULL;
+}
+
+/* Open a stream associated with the message mhm. If the stream is
+ already open, do nothing.
+ NOTE: We could have reused the NULL holes in the msg_pool, but
+ that hardly is worth the effort, since the holes appear only when
+ expunging. On the other hand this may be useful when MAX_OPEN_STREAMS
+ size is very big. "Premature optimization is the root of all evil" */
+static int
+amd_pool_open (struct _amd_message *mhm)
+{
+ int status;
+ struct _amd_data *amd = mhm->amd;
+ if (amd_pool_lookup (mhm))
+ return 0;
+ if (amd_pool_open_count(amd) == MAX_OPEN_STREAMS-1)
+ {
+ amd_message_stream_close (amd->msg_pool[amd->pool_first++]);
+ amd->pool_first %= MAX_OPEN_STREAMS;
+ }
+ status = amd_message_stream_open (mhm);
+ if (status)
+ return status;
+ amd->msg_pool[amd->pool_last++] = mhm;
+ amd->pool_last %= MAX_OPEN_STREAMS;
+ return 0;
+}
+
+static void
+amd_pool_flush (struct _amd_data *amd)
+{
+ int i;
+
+ for (i = amd->pool_first; i != amd->pool_last; )
+ {
+ if (amd->msg_pool[i])
+ amd_message_stream_close (amd->msg_pool[i]);
+ if (++i == MAX_OPEN_STREAMS)
+ i = 0;
+ }
+ amd->pool_first = amd->pool_last = 0;
+}
+
+/* Attach a stream to a given message structure. The latter is supposed
+ to be already added to the open message pool. */
+int
+amd_message_stream_open (struct _amd_message *mhm)
+{
+ struct _amd_data *amd = mhm->amd;
+ char *filename;
+ int status;
+ int flags = MU_STREAM_ALLOW_LINKS;
+
+ status = amd->cur_msg_file_name (mhm, &filename);
+ if (status)
+ return status;
+
+ /* The message should be at least readable */
+ if (amd->mailbox->flags & (MU_STREAM_RDWR|MU_STREAM_WRITE|MU_STREAM_APPEND))
+ flags |= MU_STREAM_RDWR;
+ else
+ flags |= MU_STREAM_READ;
+ status = mu_file_stream_create (&mhm->stream, filename, flags);
+
+ free (filename);
+
+ if (status != 0)
+ return status;
+
+ /* FIXME: Select buffer size dynamically */
+ mu_stream_set_buffer (mhm->stream, mu_buffer_full, 16384);
+
+ status = mu_stream_open (mhm->stream);
+
+ if (status != 0)
+ mu_stream_destroy (&mhm->stream);
+
+ if (status == 0)
+ status = amd_scan_message (mhm);
+
+ return status;
+}
+
+/* Close the stream associated with the given message. */
+void
+amd_message_stream_close (struct _amd_message *mhm)
+{
+ if (mhm)
+ {
+ mu_stream_close (mhm->stream);
+ mhm->stream = NULL;
+ }
+}
+
+void
+amd_check_message (struct _amd_message *mhm)
+{
+ if (mhm->body_end == 0)
+ amd_pool_open (mhm);
+}
+
+/* Reading functions */
+static int
+amd_body_stream_read (mu_stream_t is, char *buffer, size_t buflen,
+ size_t *pnread)
+{
+ struct _amd_body_stream *amdstr = (struct _amd_body_stream *)is;
+ mu_body_t body = amdstr->body;
+ mu_message_t msg = mu_body_get_owner (body);
+ struct _amd_message *mhm = mu_message_get_owner (msg);
+ size_t nread = 0;
+ int status = 0;
+ mu_off_t ln;
+
+ amd_pool_open (mhm);
+
+ if (buffer == NULL || buflen == 0)
+ {
+ *pnread = nread;
+ return 0;
+ }
+
+ mu_monitor_rdlock (mhm->amd->mailbox->monitor);
+#ifdef WITH_PTHREAD
+ /* read() is cancellation point since we're doing a potentially
+ long operation. Lets make sure we clean the state. */
+ pthread_cleanup_push (amd_cleanup, (void *)mhm->amd->mailbox);
+#endif
+
+ ln = mhm->body_end - (mhm->body_start + amdstr->off);
+ if (ln > 0)
+ {
+ nread = ((size_t)ln < buflen) ? (size_t)ln : buflen;
+ status = mu_stream_seek (mhm->stream, mhm->body_start + amdstr->off,
+ MU_SEEK_SET, NULL);
+ if (status == 0)
+ {
+ status = mu_stream_read (mhm->stream, buffer, nread, &nread);
+ amdstr->off += nread;
+ }
+ }
+
+ *pnread = nread;
+
+ mu_monitor_unlock (mhm->amd->mailbox->monitor);
+#ifdef WITH_PTHREAD
+ pthread_cleanup_pop (0);
+#endif
+
+ return status;
+}
+
+static int
+amd_body_stream_readdelim (mu_stream_t is, char *buffer, size_t buflen,
+ int delim,
+ size_t *pnread)
+{
+ struct _amd_body_stream *amdstr = (struct _amd_body_stream *)is;
+ mu_body_t body = amdstr->body;
+ mu_message_t msg = mu_body_get_owner (body);
+ struct _amd_message *mhm = mu_message_get_owner (msg);
+ int status = 0;
+
+ amd_pool_open (mhm);
+
+ if (buffer == NULL || buflen == 0)
+ {
+ if (pnread)
+ *pnread = 0;
+ return 0;
+ }
+
+ mu_monitor_rdlock (mhm->amd->mailbox->monitor);
+#ifdef WITH_PTHREAD
+ /* read() is cancellation point since we're doing a potentially
+ long operation. Lets make sure we clean the state. */
+ pthread_cleanup_push (amd_cleanup, (void *)mhm->amd->mailbox);
+#endif
+
+ status = mu_stream_seek (mhm->stream, mhm->body_start + amdstr->off,
+ MU_SEEK_SET, NULL);
+ if (status == 0)
+ {
+ size_t nread = 0;
+ size_t ln;
+
+ ln = mhm->body_end - (mhm->body_start + amdstr->off) + 1;
+ if (ln > 0)
+ {
+ size_t rdsize = ((size_t)ln < buflen) ? (size_t)ln : buflen;
+ status = mu_stream_readdelim (mhm->stream, buffer, rdsize,
+ delim, &nread);
+ amdstr->off += nread;
+ }
+
+ if (pnread)
+ *pnread = nread;
+ }
+
+ mu_monitor_unlock (mhm->amd->mailbox->monitor);
+#ifdef WITH_PTHREAD
+ pthread_cleanup_pop (0);
+#endif
+
+ return status;
+}
+
+static int
+amd_body_stream_seek (mu_stream_t str, mu_off_t off, mu_off_t *presult)
+{
+ size_t size;
+ struct _amd_body_stream *amdstr = (struct _amd_body_stream *)str;
+
+ amd_body_size (amdstr->body, &size);
+
+ if (off < 0 || off > size)
+ return ESPIPE;
+
+ amdstr->off = off;
+ if (presult)
+ *presult = off;
+ return 0;
+}
+
+/* Return corresponding sizes */
+
+static int
+amd_body_stream_size (mu_stream_t stream, mu_off_t *psize)
+{
+ mu_body_t body = ((struct _amd_body_stream *)stream)->body;
+ size_t size;
+ int rc = amd_body_size (body, &size);
+ if (rc == 0)
+ *psize = size;
+ return rc;
+}
+
+static int
+amd_body_size (mu_body_t body, size_t *psize)
+{
+ mu_message_t msg = mu_body_get_owner (body);
+ struct _amd_message *mhm = mu_message_get_owner (msg);
+ if (mhm == NULL)
+ return EINVAL;
+ amd_check_message (mhm);
+ if (psize)
+ *psize = mhm->body_end - mhm->body_start;
+ return 0;
+}
+
+static int
+amd_body_lines (mu_body_t body, size_t *plines)
+{
+ mu_message_t msg = mu_body_get_owner (body);
+ struct _amd_message *mhm = mu_message_get_owner (msg);
+ if (mhm == NULL)
+ return EINVAL;
+ amd_check_message (mhm);
+ if (plines)
+ *plines = mhm->body_lines;
+ return 0;
+}
+
+/* Headers */
+static int
+amd_header_fill (void *data, char **pbuf, size_t *plen)
+{
+ char *buffer;
+ size_t len;
+ mu_message_t msg = data;
+ struct _amd_message *mhm = mu_message_get_owner (msg);
+ int status, rc;
+ mu_off_t pos;
+
+ status = amd_pool_open (mhm);
+ if (status)
+ return status;
+
+ len = mhm->body_start;
+ buffer = malloc (len);
+ if (!buffer)
+ return ENOMEM;
+
+ status = mu_stream_seek (mhm->stream, 0, MU_SEEK_CUR, &pos);
+ if (status)
+ return status;
+ status = mu_stream_seek (mhm->stream, 0, MU_SEEK_SET, NULL);
+ if (status)
+ return status;
+
+ status = mu_stream_read (mhm->stream, buffer, len, NULL);
+ rc = mu_stream_seek (mhm->stream, pos, MU_SEEK_SET, NULL);
+
+ if (!status)
+ status = rc;
+
+ if (status)
+ {
+ free (buffer);
+ return status;
+ }
+
+ *plen = len;
+ *pbuf = buffer;
+ return 0;
+}
+
+/* Attributes */
+static int
+amd_get_attr_flags (mu_attribute_t attr, int *pflags)
+{
+ mu_message_t msg = mu_attribute_get_owner (attr);
+ struct _amd_message *mhm = mu_message_get_owner (msg);
+
+ if (mhm == NULL)
+ return EINVAL;
+ if (pflags)
+ *pflags = mhm->attr_flags;
+ return 0;
+}
+
+static int
+amd_set_attr_flags (mu_attribute_t attr, int flags)
+{
+ mu_message_t msg = mu_attribute_get_owner (attr);
+ struct _amd_message *mhm = mu_message_get_owner (msg);
+
+ if (mhm == NULL)
+ return EINVAL;
+ mhm->attr_flags |= flags;
+ return 0;
+}
+
+static int
+amd_unset_attr_flags (mu_attribute_t attr, int flags)
+{
+ mu_message_t msg = mu_attribute_get_owner (attr);
+ struct _amd_message *mhm = mu_message_get_owner (msg);
+
+ if (mhm == NULL)
+ return EINVAL;
+ mhm->attr_flags &= ~flags;
+ return 0;
+}
+
+/* Envelope */
+static int
+amd_envelope_date (mu_envelope_t envelope, char *buf, size_t len,
+ size_t *psize)
+{
+ mu_message_t msg = mu_envelope_get_owner (envelope);
+ struct _amd_message *mhm = mu_message_get_owner (msg);
+ mu_header_t hdr = NULL;
+ char *date;
+ int status;
+
+ if (mhm == NULL)
+ return EINVAL;
+
+ if ((status = mu_message_get_header (msg, &hdr)) != 0)
+ return status;
+ if (mu_header_aget_value (hdr, MU_HEADER_ENV_DATE, &date)
+ && mu_header_aget_value (hdr, MU_HEADER_DELIVERY_DATE, &date))
+ return MU_ERR_NOENT;
+ else
+ {
+ time_t t;
+ int rc;
+
+ /* Convert to ctime format */
+ rc = mu_parse_date (date, &t, NULL); /* FIXME: TZ info is lost */
+ free (date);
+ if (rc)
+ return MU_ERR_NOENT;
+ date = strdup (ctime (&t));
+ }
+
+ /* Format: "sender date" */
+ if (buf && len > 0)
+ {
+ len--; /* Leave space for the null. */
+ strncpy (buf, date, len);
+ if (strlen (date) < len)
+ {
+ len = strlen (buf);
+ if (buf[len-1] != '\n')
+ buf[len++] = '\n';
+ }
+ buf[len] = '\0';
+ }
+ else
+ len = strlen (date);
+
+ free (date);
+
+ if (psize)
+ *psize = len;
+ return 0;
+}
+
+static int
+amd_envelope_sender (mu_envelope_t envelope, char *buf, size_t len, size_t *psize)
+{
+ mu_message_t msg = mu_envelope_get_owner (envelope);
+ struct _amd_message *mhm = mu_message_get_owner (msg);
+ mu_header_t hdr = NULL;
+ char *from;
+ int status;
+
+ if (mhm == NULL)
+ return EINVAL;
+
+ if ((status = mu_message_get_header (msg, &hdr)))
+ return status;
+ if ((status = mu_header_aget_value (hdr, MU_HEADER_ENV_SENDER, &from)))
+ return status;
+
+ if (buf && len > 0)
+ {
+ int slen = strlen (from);
+
+ if (len < slen + 1)
+ slen = len - 1;
+ memcpy (buf, from, slen);
+ buf[slen] = 0;
+ }
+ else
+ len = strlen (from);
+
+ if (psize)
+ *psize = len;
+ return 0;
+}
+
+
diff --git a/libmailutils/argcv.c b/libmailutils/argcv.c
new file mode 100644
index 000000000..636b9af96
--- /dev/null
+++ b/libmailutils/argcv.c
@@ -0,0 +1,549 @@
+/* argcv.c - simple functions for parsing input based on whitespace
+ Copyright (C) 1999, 2000, 2001, 2003, 2004, 2005, 2006, 2010 Free
+ Software Foundation, Inc.
+
+ This library is free software; you can redistribute it and/or
+ modify it under the terms of the GNU Lesser General Public
+ License as published by the Free Software Foundation; either
+ version 3 of the License, or (at your option) any later version.
+
+ This library 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
+ Lesser General Public License for more details.
+
+ You should have received a copy of the GNU Lesser General
+ Public License along with this library; if not, write to the
+ Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor,
+ Boston, MA 02110-1301 USA */
+
+#ifdef HAVE_CONFIG_H
+# include <config.h>
+#endif
+
+#include <ctype.h>
+#include <errno.h>
+#include <mailutils/argcv.h>
+
+/* Keep mailutils namespace clean */
+#define argcv_get mu_argcv_get
+#define argcv_get_n mu_argcv_get_n
+#define argcv_get_np mu_argcv_get_np
+#define argcv_string mu_argcv_string
+#define argcv_free mu_argcv_free
+#define argv_free mu_argv_free
+#define argcv_unquote_char mu_argcv_unquote_char
+#define argcv_quote_char mu_argcv_quote_char
+#define argcv_quoted_length mu_argcv_quoted_length
+#define argcv_unquote_copy mu_argcv_unquote_copy
+#define argcv_quote_copy mu_argcv_quote_copy
+
+/*
+ * takes a string and splits it into several strings, breaking at ' '
+ * command is the string to split
+ * the number of strings is placed into argc
+ * the split strings are put into argv
+ * returns 0 on success, nonzero on failure
+ */
+
+#define isws(c) ((c)==' '||(c)=='\t'||(c)=='\n')
+#define isdelim(c,delim) (strchr(delim,(c))!=NULL)
+
+struct argcv_info
+{
+ int len;
+ const char *command;
+ const char *delim;
+ const char *comment;
+ int flags;
+
+ int start;
+ int end;
+ int save;
+ int finish_pos;
+};
+
+static void
+init_argcv_info (struct argcv_info *ap, int flags,
+ int len, const char *command, const char *delim,
+ const char *comment)
+{
+ memset (ap, 0, sizeof *ap);
+ ap->len = len;
+ ap->command = command;
+ ap->delim = delim;
+ ap->comment = comment;
+ ap->flags = flags;
+}
+
+static int
+argcv_scan (struct argcv_info *ap)
+{
+ int i = 0;
+ int len = ap->len;
+ const char *command = ap->command;
+ const char *delim = ap->delim;
+ const char *comment = ap->comment;
+
+ for (;;)
+ {
+ i = ap->save;
+
+ if (i >= len)
+ return i + 1;
+
+ /* Skip initial whitespace */
+ while (i < len && isws (command[i]))
+ i++;
+ ap->start = i;
+
+ if (!isdelim (command[i], delim))
+ {
+ while (i < len)
+ {
+ if (command[i] == '\\')
+ {
+ if (++i == len)
+ break;
+ i++;
+ continue;
+ }
+
+ if (command[i] == '\'' || command[i] == '"')
+ {
+ int j;
+ for (j = i + 1; j < len && command[j] != command[i]; j++)
+ if (command[j] == '\\')
+ j++;
+ if (j < len)
+ i = j + 1;
+ else
+ i++;
+ }
+ else if (isws (command[i]) || isdelim (command[i], delim))
+ break;
+ else
+ i++; /* skip the escaped character */
+ }
+ i--;
+ }
+ else if (!(ap->flags & MU_ARGCV_RETURN_DELIMS))
+ {
+ while (i < len && isdelim (command[i], delim))
+ i++;
+ ap->save = i;
+ continue;
+ }
+
+
+ ap->end = i;
+ ap->save = ap->finish_pos = i + 1;
+
+ /* If we have a token, and it starts with a comment character, skip
+ to the newline and restart the token search. */
+ if (ap->save <= len)
+ {
+ if (strchr (comment, command[ap->start]) != NULL)
+ {
+ ap->finish_pos = ap->start;
+ i = ap->save;
+ while (i < len && command[i] != '\n')
+ i++;
+
+ ap->save = i;
+ continue;
+ }
+ }
+ break;
+ }
+ return ap->save;
+}
+
+static char quote_transtab[] = "\\\\\"\"a\ab\bf\fn\nr\rt\tv\v";
+
+int
+argcv_unquote_char (int c)
+{
+ char *p;
+
+ for (p = quote_transtab; *p; p += 2)
+ {
+ if (*p == c)
+ return p[1];
+ }
+ return c;
+}
+
+int
+argcv_quote_char (int c)
+{
+ char *p;
+
+ for (p = quote_transtab + sizeof(quote_transtab) - 2;
+ p > quote_transtab; p -= 2)
+ {
+ if (*p == c)
+ return p[-1];
+ }
+ return -1;
+}
+
+#define to_num(c) \
+ (isdigit(c) ? c - '0' : (isxdigit(c) ? toupper(c) - 'A' + 10 : 255 ))
+
+static int
+xtonum (int *pval, const char *src, int base, int cnt)
+{
+ int i, val;
+
+ for (i = 0, val = 0; i < cnt; i++, src++)
+ {
+ int n = *(unsigned char*)src;
+ if (n > 127 || (n = to_num(n)) >= base)
+ break;
+ val = val*base + n;
+ }
+ *pval = val;
+ return i;
+}
+
+size_t
+argcv_quoted_length (const char *str, int *quote)
+{
+ size_t len = 0;
+
+ *quote = 0;
+ for (; *str; str++)
+ {
+ if (*str == ' ')
+ {
+ len++;
+ *quote = 1;
+ }
+ else if (*str == '"')
+ {
+ len += 2;
+ *quote = 1;
+ }
+ else if (*str != '\t' && *str != '\\' && isprint (*str))
+ len++;
+ else if (argcv_quote_char (*str) != -1)
+ len += 2;
+ else
+ len += 4;
+ }
+ return len;
+}
+
+void
+argcv_unquote_copy (char *dst, const char *src, size_t n)
+{
+ int i = 0;
+ int c;
+ int expect_delim = 0;
+
+ while (i < n)
+ {
+ switch (src[i])
+ {
+ case '\'':
+ case '"':
+ if (!expect_delim)
+ {
+ const char *p;
+
+ for (p = src+i+1; *p && *p != src[i]; p++)
+ if (*p == '\\')
+ p++;
+ if (*p)
+ expect_delim = src[i++];
+ else
+ *dst++ = src[i++];
+ }
+ else if (expect_delim == src[i])
+ ++i;
+ else
+ *dst++ = src[i++];
+ break;
+
+ case '\\':
+ ++i;
+ if (src[i] == 'x' || src[i] == 'X')
+ {
+ if (n - i < 2)
+ {
+ *dst++ = '\\';
+ *dst++ = src[i++];
+ }
+ else
+ {
+ int off = xtonum(&c, src + i + 1, 16, 2);
+ if (off == 0)
+ {
+ *dst++ = '\\';
+ *dst++ = src[i++];
+ }
+ else
+ {
+ *dst++ = c;
+ i += off + 1;
+ }
+ }
+ }
+ else if ((unsigned char)src[i] < 128 && isdigit (src[i]))
+ {
+ if (n - i < 1)
+ {
+ *dst++ = '\\';
+ *dst++ = src[i++];
+ }
+ else
+ {
+ int off = xtonum (&c, src+i, 8, 3);
+ if (off == 0)
+ {
+ *dst++ = '\\';
+ *dst++ = src[i++];
+ }
+ else
+ {
+ *dst++ = c;
+ i += off;
+ }
+ }
+ }
+ else
+ *dst++ = argcv_unquote_char (src[i++]);
+ break;
+
+ default:
+ *dst++ = src[i++];
+ }
+ }
+ *dst = 0;
+}
+
+void
+argcv_quote_copy (char *dst, const char *src)
+{
+ for (; *src; src++)
+ {
+ if (*src == '"')
+ {
+ *dst++ = '\\';
+ *dst++ = *src;
+ }
+ else if (*src != '\t' && *src != '\\' && isprint(*src))
+ *dst++ = *src;
+ else
+ {
+ int c = argcv_quote_char (*src);
+ *dst++ = '\\';
+ if (c != -1)
+ *dst++ = c;
+ else
+ {
+ char tmp[4];
+ snprintf (tmp, sizeof tmp, "%03o", *(unsigned char*)src);
+ memcpy (dst, tmp, 3);
+ dst += 3;
+ }
+ }
+ }
+}
+
+int
+argcv_get_np (const char *command, int len,
+ const char *delim, const char *cmnt,
+ int flags,
+ int *pargc, char ***pargv, char **endp)
+{
+ int i = 0;
+ struct argcv_info info;
+ int argc;
+ char **argv;
+
+ if (!delim)
+ delim = "";
+ if (!cmnt)
+ cmnt = "";
+
+ init_argcv_info (&info, flags, len, command, delim, cmnt);
+
+ /* Count number of arguments */
+ argc = 0;
+ while (argcv_scan (&info) <= len)
+ argc++;
+
+ argv = calloc ((argc + 1), sizeof (char *));
+ if (argv == NULL)
+ return ENOMEM;
+
+ i = 0;
+ info.save = 0;
+ for (i = 0; i < argc; i++)
+ {
+ int n;
+ int unquote;
+
+ argcv_scan (&info);
+
+ if ((command[info.start] == '"' || command[info.end] == '\'')
+ && command[info.end] == command[info.start])
+ {
+ if (info.start < info.end)
+ {
+ info.start++;
+ info.end--;
+ }
+ unquote = 0;
+ }
+ else
+ unquote = 1;
+
+ n = info.end - info.start + 1;
+ argv[i] = calloc (n + 1, sizeof (char));
+ if (argv[i] == NULL)
+ {
+ argcv_free (i, argv);
+ return ENOMEM;
+ }
+ if (unquote)
+ argcv_unquote_copy (argv[i], &command[info.start], n);
+ else
+ memcpy (argv[i], &command[info.start], n);
+ argv[i][n] = 0;
+ }
+ argv[i] = NULL;
+
+ *pargc = argc;
+ *pargv = argv;
+ if (endp)
+ *endp = (char*) (command + info.finish_pos);
+ return 0;
+}
+
+int
+argcv_get_n (const char *command, int len, const char *delim, const char *cmnt,
+ int *pargc, char ***pargv)
+{
+ return argcv_get_np (command, len, delim, cmnt, MU_ARGCV_RETURN_DELIMS,
+ pargc, pargv, NULL);
+}
+
+int
+argcv_get (const char *command, const char *delim, const char *cmnt,
+ int *argc, char ***argv)
+{
+ return argcv_get_n (command, strlen (command), delim, cmnt, argc, argv);
+}
+
+
+/*
+ * frees all elements of an argv array
+ * argc is the number of elements
+ * argv is the array
+ */
+void
+argcv_free (int argc, char **argv)
+{
+ if (argc <= 0)
+ return;
+ while (--argc >= 0)
+ if (argv[argc])
+ free (argv[argc]);
+ free (argv);
+}
+
+void
+argv_free (char **argv)
+{
+ int i;
+
+ for (i = 0; argv[i]; i++)
+ free (argv[i]);
+ free (argv);
+}
+
+/* Make a argv an make string separated by ' '. */
+
+int
+argcv_string (int argc, char **argv, char **pstring)
+{
+ size_t i, j, len;
+ char *buffer;
+
+ /* No need. */
+ if (pstring == NULL)
+ return EINVAL;
+
+ buffer = malloc (1);
+ if (buffer == NULL)
+ return ENOMEM;
+ *buffer = '\0';
+
+ for (len = i = j = 0; i < argc; i++)
+ {
+ int quote;
+ int toklen;
+
+ toklen = argcv_quoted_length (argv[i], &quote);
+
+ len += toklen + 2;
+ if (quote)
+ len += 2;
+
+ buffer = realloc (buffer, len);
+ if (buffer == NULL)
+ return ENOMEM;
+
+ if (i != 0)
+ buffer[j++] = ' ';
+ if (quote)
+ buffer[j++] = '"';
+ argcv_quote_copy (buffer + j, argv[i]);
+ j += toklen;
+ if (quote)
+ buffer[j++] = '"';
+ }
+
+ for (; j > 0 && isspace (buffer[j-1]); j--)
+ ;
+ buffer[j] = 0;
+ if (pstring)
+ *pstring = buffer;
+ return 0;
+}
+
+void
+mu_argcv_remove (int *pargc, char ***pargv,
+ int (*sel) (const char *, void *), void *data)
+{
+ int i, j;
+ int argc = *pargc;
+ char **argv = *pargv;
+ int cnt = 0;
+
+ for (i = j = 0; i < argc; i++)
+ {
+ if (sel (argv[i], data))
+ {
+ free (argv[i]);
+ cnt++;
+ }
+ else
+ {
+ if (i != j)
+ argv[j] = argv[i];
+ j++;
+ }
+ }
+ if (i != j)
+ argv[j] = NULL;
+ argc -= cnt;
+
+ *pargc = argc;
+ *pargv = argv;
+}
+
+
diff --git a/libmailutils/asnprintf.c b/libmailutils/asnprintf.c
new file mode 100644
index 000000000..f8ef54334
--- /dev/null
+++ b/libmailutils/asnprintf.c
@@ -0,0 +1,40 @@
+/* GNU Mailutils -- a suite of utilities for electronic mail
+ Copyright (C) 2009, 2010 Free Software Foundation, Inc.
+
+ This library is free software; you can redistribute it and/or
+ modify it under the terms of the GNU Lesser General Public
+ License as published by the Free Software Foundation; either
+ version 3 of the License, or (at your option) any later version.
+
+ This library 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
+ Lesser General Public License for more details.
+
+ You should have received a copy of the GNU Lesser General
+ Public License along with this library; if not, write to the
+ Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor,
+ Boston, MA 02110-1301 USA */
+
+#if HAVE_CONFIG_H
+# include <config.h>
+#endif
+
+#include <stdlib.h>
+#include <stdarg.h>
+#include <stdio.h>
+
+#include <mailutils/io.h>
+
+int
+mu_asnprintf (char **pbuf, size_t *psize, const char *fmt, ...)
+{
+ int rc;
+ va_list ap;
+
+ va_start (ap, fmt);
+ rc = mu_vasnprintf (pbuf, psize, fmt, ap);
+ va_end (ap);
+ return rc;
+}
+
diff --git a/libmailutils/asprintf.c b/libmailutils/asprintf.c
new file mode 100644
index 000000000..c563bcb84
--- /dev/null
+++ b/libmailutils/asprintf.c
@@ -0,0 +1,42 @@
+/* GNU Mailutils -- a suite of utilities for electronic mail
+ Copyright (C) 2009, 2010 Free Software Foundation, Inc.
+
+ This library is free software; you can redistribute it and/or
+ modify it under the terms of the GNU Lesser General Public
+ License as published by the Free Software Foundation; either
+ version 3 of the License, or (at your option) any later version.
+
+ This library 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
+ Lesser General Public License for more details.
+
+ You should have received a copy of the GNU Lesser General
+ Public License along with this library; if not, write to the
+ Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor,
+ Boston, MA 02110-1301 USA */
+
+#if HAVE_CONFIG_H
+# include <config.h>
+#endif
+
+#include <stdlib.h>
+#include <stdarg.h>
+#include <stdio.h>
+
+#include <mailutils/io.h>
+
+int
+mu_asprintf (char **pbuf, const char *fmt, ...)
+{
+ va_list ap;
+ size_t size;
+ int rc;
+
+ va_start (ap, fmt);
+ *pbuf = NULL;
+ size = 0;
+ rc = mu_vasnprintf (pbuf, &size, fmt, ap);
+ va_end (ap);
+ return rc;
+}
diff --git a/libmailutils/assoc.c b/libmailutils/assoc.c
new file mode 100644
index 000000000..a0c13a0ed
--- /dev/null
+++ b/libmailutils/assoc.c
@@ -0,0 +1,529 @@
+/* GNU Mailutils -- a suite of utilities for electronic mail
+ Copyright (C) 2007, 2009, 2010 Free Software Foundation, Inc.
+
+ GNU Mailutils 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 3, or (at your option)
+ any later version.
+
+ GNU Mailutils 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 GNU Mailutils; if not, write to the Free Software
+ Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston,
+ MA 02110-1301 USA */
+
+#if HAVE_CONFIG_H
+# include <config.h>
+#endif
+
+#include <stdlib.h>
+#include <string.h>
+#include <mailutils/types.h>
+#include <mailutils/assoc.h>
+#include <mailutils/errno.h>
+#include <mailutils/error.h>
+#include <mailutils/iterator.h>
+#include <mailutils/mutil.h>
+#include <mailutils/cstr.h>
+#include <mailutils/sys/iterator.h>
+
+/* |hash_size| defines a sequence of symbol table sizes. These are prime
+ numbers, the distance between each pair of them grows exponentially,
+ starting from 64. Hardly someone will need more than 16411 symbols, and
+ even if someone will, it is easy enough to add more numbers to the
+ sequence. */
+
+static unsigned int hash_size[] = {
+ 37, 101, 229, 487, 1009, 2039, 4091, 8191, 16411
+};
+
+/* |max_rehash| keeps the number of entries in |hash_size| table. */
+static unsigned int max_rehash = sizeof (hash_size) / sizeof (hash_size[0]);
+
+struct _mu_assoc_elem
+{
+ char *name;
+ char data[1];
+};
+
+struct _mu_assoc
+{
+ int flags;
+ unsigned int hash_num; /* Index to hash_size table */
+ size_t elsize; /* Size of an element */
+ void *tab;
+ mu_assoc_free_fn free;
+ mu_iterator_t itr;
+};
+
+struct _mu_assoc_elem_align
+{
+ char c;
+ struct _mu_assoc_elem x;
+};
+
+#define __ASSOC_ELEM_ALIGNMENT (mu_offsetof(struct _mu_assoc_elem_align, x))
+
+#define __ASSOC_ELEM_SIZE(a) \
+ ((a)->elsize + mu_offsetof(struct _mu_assoc_elem, data))
+#define __ASSOC_ALIGN(a, b) (((a) + (b) - 1) & ~((b) - 1))
+#define ASSOC_ELEM_SIZE(a) \
+ __ASSOC_ALIGN(__ASSOC_ELEM_SIZE(a),__ASSOC_ELEM_ALIGNMENT)
+
+#define __ASSOC_ELEM(a,p,n) \
+ ((struct _mu_assoc_elem*) ((char*) (p) + ASSOC_ELEM_SIZE (a) * n))
+
+#define ASSOC_ELEM(a,n) __ASSOC_ELEM(a,(a)->tab,n)
+
+#define ASSOC_ELEM_INDEX(a,e) \
+ (((char*)(e) - (char*)(a)->tab) / ASSOC_ELEM_SIZE (a))
+
+
+static unsigned
+hash (const char *name, unsigned long hash_num)
+{
+ unsigned i;
+
+ for (i = 0; *name; name++)
+ {
+ i <<= 1;
+ i ^= *(unsigned char*) name;
+ }
+ return i % hash_size[hash_num];
+};
+
+static int
+assoc_lookup_or_install (struct _mu_assoc_elem **elp,
+ mu_assoc_t assoc, const char *name, int *install);
+
+static int
+assoc_rehash (mu_assoc_t assoc)
+{
+ void *old_tab = assoc->tab;
+ void *new_tab;
+ unsigned int i;
+ unsigned int hash_num = assoc->hash_num + 1;
+
+ if (hash_num >= max_rehash)
+ return MU_ERR_BUFSPACE;
+
+ new_tab = calloc (hash_size[hash_num], ASSOC_ELEM_SIZE (assoc));
+ assoc->tab = new_tab;
+ if (old_tab)
+ {
+ assoc->hash_num = hash_num;
+ for (i = 0; i < hash_size[hash_num-1]; i++)
+ {
+ struct _mu_assoc_elem *elt = __ASSOC_ELEM (assoc, old_tab, i);
+ if (elt->name)
+ {
+ int tmp;
+ struct _mu_assoc_elem *newp;
+
+ int rc = assoc_lookup_or_install (&newp, assoc, elt->name, &tmp);
+ if (rc)
+ return rc;
+ newp->name = elt->name;
+ memcpy(newp->data, elt->data, assoc->elsize);
+ }
+ }
+ free (old_tab);
+ }
+ return 0;
+}
+
+static void
+assoc_free_elem (mu_assoc_t assoc, struct _mu_assoc_elem *elem)
+{
+ if (assoc->free)
+ assoc->free (elem->data);
+ if (!(assoc->flags & MU_ASSOC_COPY_KEY))
+ free (elem->name);
+}
+
+static int
+assoc_remove (mu_assoc_t assoc, struct _mu_assoc_elem *elem)
+{
+ unsigned int i, j, r;
+
+ if (!(ASSOC_ELEM (assoc, 0) <= elem
+ && elem < ASSOC_ELEM (assoc, hash_size[assoc->hash_num])))
+ return EINVAL;
+
+ assoc_free_elem (assoc, elem);
+
+ for (i = ASSOC_ELEM_INDEX (assoc, elem);;)
+ {
+ struct _mu_assoc_elem *p = ASSOC_ELEM (assoc, i);
+ p->name = NULL;
+ j = i;
+
+ do
+ {
+ if (++i >= hash_size[assoc->hash_num])
+ i = 0;
+ p = ASSOC_ELEM (assoc, i);
+ if (!p->name)
+ return 0;
+ r = hash (p->name, assoc->hash_num);
+ }
+ while ((j < r && r <= i) || (i < j && j < r) || (r <= i && i < j));
+
+ if (j != i)
+ memcpy (ASSOC_ELEM (assoc, j), ASSOC_ELEM (assoc, i),
+ assoc->elsize);
+ }
+ return 0;
+}
+
+#define name_cmp(assoc,a,b) (((assoc)->flags & MU_ASSOC_ICASE) ? \
+ mu_c_strcasecmp(a,b) : strcmp(a,b))
+
+static int
+assoc_lookup_or_install (struct _mu_assoc_elem **elp,
+ mu_assoc_t assoc, const char *name, int *install)
+{
+ int rc;
+ unsigned i, pos;
+ struct _mu_assoc_elem *elem;
+
+ if (!assoc->tab)
+ {
+ if (install)
+ {
+ rc = assoc_rehash (assoc);
+ if (rc)
+ return rc;
+ }
+ else
+ return MU_ERR_NOENT;
+ }
+
+ pos = hash (name, assoc->hash_num);
+
+ for (i = pos; (elem = ASSOC_ELEM (assoc, i))->name;)
+ {
+ if (name_cmp (assoc, elem->name, name) == 0)
+ {
+ if (install)
+ *install = 0;
+ *elp = elem;
+ return 0;
+ }
+
+ if (++i >= hash_size[assoc->hash_num])
+ i = 0;
+ if (i == pos)
+ break;
+ }
+
+ if (!install)
+ return MU_ERR_NOENT;
+
+ if (elem->name == NULL)
+ {
+ *install = 1;
+ if (assoc->flags & MU_ASSOC_COPY_KEY)
+ elem->name = (char *) name;
+ else
+ {
+ elem->name = strdup (name);
+ if (!elem->name)
+ return ENOMEM;
+ }
+ *elp = elem;
+ return 0;
+ }
+
+ if ((rc = assoc_rehash (assoc)) != 0)
+ return rc;
+
+ return assoc_lookup_or_install (elp, assoc, name, install);
+}
+
+int
+mu_assoc_create (mu_assoc_t *passoc, size_t elsize, int flags)
+{
+ mu_assoc_t assoc = calloc (1, sizeof (*assoc));
+ if (!assoc)
+ return ENOMEM;
+ assoc->flags = flags;
+ assoc->elsize = elsize;
+ *passoc = assoc;
+ return 0;
+}
+
+void
+mu_assoc_clear (mu_assoc_t assoc)
+{
+ unsigned i, hs;
+
+ if (!assoc || !assoc->tab)
+ return;
+
+ hs = hash_size[assoc->hash_num];
+ for (i = 0; i < hs; i++)
+ {
+ struct _mu_assoc_elem *elem = ASSOC_ELEM (assoc, i);
+ if (elem->name)
+ {
+ assoc_free_elem (assoc, elem);
+ elem->name = NULL;
+ }
+ }
+}
+
+void
+mu_assoc_destroy (mu_assoc_t *passoc)
+{
+ mu_assoc_t assoc;
+ if (passoc && (assoc = *passoc) != NULL)
+ {
+ mu_assoc_clear (assoc);
+ free (assoc->tab);
+ free (assoc);
+ *passoc = NULL;
+ }
+}
+
+int
+mu_assoc_set_free (mu_assoc_t assoc, mu_assoc_free_fn fn)
+{
+ if (!assoc)
+ return EINVAL;
+ assoc->free = fn;
+ return 0;
+}
+
+void *
+mu_assoc_ref (mu_assoc_t assoc, const char *name)
+{
+ int rc;
+ static struct _mu_assoc_elem *elp;
+
+ if (!assoc || !name)
+ return NULL;
+
+ rc = assoc_lookup_or_install (&elp, assoc, name, NULL);
+ if (rc == 0)
+ return elp->data;
+ return NULL;
+}
+
+int
+mu_assoc_install (mu_assoc_t assoc, const char *name, void *value)
+{
+ int rc;
+ int inst;
+ static struct _mu_assoc_elem *elp;
+
+ if (!assoc || !name)
+ return EINVAL;
+
+ rc = assoc_lookup_or_install (&elp, assoc, name, &inst);
+ if (rc)
+ return rc;
+ if (!inst)
+ return MU_ERR_EXISTS;
+ memcpy (elp->data, value, assoc->elsize);
+ return 0;
+}
+
+int
+mu_assoc_ref_install (mu_assoc_t assoc, const char *name, void **pval)
+{
+ int rc;
+ int inst;
+ static struct _mu_assoc_elem *elp;
+
+ if (!assoc || !name)
+ return EINVAL;
+
+ rc = assoc_lookup_or_install (&elp, assoc, name, &inst);
+ if (rc)
+ return rc;
+ *pval = elp->data;
+ return inst ? 0 : MU_ERR_EXISTS;
+}
+
+int
+mu_assoc_remove (mu_assoc_t assoc, const char *name)
+{
+ int rc;
+ static struct _mu_assoc_elem *elem;
+
+ if (!assoc || !name)
+ return EINVAL;
+ rc = assoc_lookup_or_install (&elem, assoc, name, NULL);
+ if (rc)
+ return rc;
+ return assoc_remove (assoc, elem);
+}
+
+#define OFFSET(s,f) (size_t)(&((s*)0)->f)
+
+int
+mu_assoc_remove_ref (mu_assoc_t assoc, void *data)
+{
+ struct _mu_assoc_elem *elem;
+
+ elem = (struct _mu_assoc_elem *) ((char*)data -
+ OFFSET(struct _mu_assoc_elem, data));
+ return assoc_remove (assoc, elem);
+}
+
+
+/* Iterator interface */
+
+struct assoc_iterator
+{
+ mu_assoc_t assoc;
+ unsigned start;
+ unsigned index;
+};
+
+static int
+first (void *owner)
+{
+ struct assoc_iterator *itr = owner;
+ mu_assoc_t assoc = itr->assoc;
+ unsigned hash_max = hash_size[assoc->hash_num];
+ unsigned i;
+
+ for (i = 0; i < hash_max; i++)
+ if ((ASSOC_ELEM (assoc, i))->name)
+ break;
+ itr->index = i;
+ return 0;
+}
+
+static int
+next (void *owner)
+{
+ struct assoc_iterator *itr = owner;
+ mu_assoc_t assoc = itr->assoc;
+ unsigned hash_max = hash_size[assoc->hash_num];
+ unsigned i;
+
+ for (i = itr->index + 1; i < hash_max; i++)
+ if ((ASSOC_ELEM (assoc, i))->name)
+ break;
+
+ itr->index = i;
+ return 0;
+}
+
+static int
+getitem (void *owner, void **pret, const void **pkey)
+{
+ struct assoc_iterator *itr = owner;
+ struct _mu_assoc_elem *elem;
+
+ if (itr->index >= hash_size[itr->assoc->hash_num])
+ return EINVAL;
+ elem = ASSOC_ELEM (itr->assoc, itr->index);
+ *pret = elem->data;
+ if (pkey)
+ *pkey = elem->name;
+ return 0;
+}
+
+static int
+finished_p (void *owner)
+{
+ struct assoc_iterator *itr = owner;
+ return itr->index >= hash_size[itr->assoc->hash_num];
+}
+
+static int
+destroy (mu_iterator_t iterator, void *data)
+{
+ struct assoc_iterator *itr = data;
+ mu_iterator_detach (&itr->assoc->itr, iterator);
+ free (data);
+ return 0;
+}
+
+static int
+curitem_p (void *owner, void *item)
+{
+ struct assoc_iterator *itr = owner;
+ mu_assoc_t assoc = itr->assoc;
+ struct _mu_assoc_elem *elem = ASSOC_ELEM (assoc, itr->index);
+
+ return elem == item;
+}
+
+static int
+assoc_data_dup (void **ptr, void *owner)
+{
+ *ptr = malloc (sizeof (struct assoc_iterator));
+ if (*ptr == NULL)
+ return ENOMEM;
+ memcpy (*ptr, owner, sizeof (struct assoc_iterator));
+ return 0;
+}
+
+int
+mu_assoc_get_iterator (mu_assoc_t assoc, mu_iterator_t *piterator)
+{
+ mu_iterator_t iterator;
+ int status;
+ struct assoc_iterator *itr;
+
+ if (!assoc)
+ return EINVAL;
+
+ itr = calloc (1, sizeof *itr);
+ if (!itr)
+ return ENOMEM;
+ itr->assoc = assoc;
+ itr->index = 0;
+
+ status = mu_iterator_create (&iterator, itr);
+ if (status)
+ {
+ free (itr);
+ return status;
+ }
+
+ mu_iterator_set_first (iterator, first);
+ mu_iterator_set_next (iterator, next);
+ mu_iterator_set_getitem (iterator, getitem);
+ mu_iterator_set_finished_p (iterator, finished_p);
+ mu_iterator_set_curitem_p (iterator, curitem_p);
+ mu_iterator_set_destroy (iterator, destroy);
+ mu_iterator_set_dup (iterator, assoc_data_dup);
+
+ mu_iterator_attach (&assoc->itr, iterator);
+
+ *piterator = iterator;
+ return 0;
+}
+
+
+
+int
+mu_assoc_count (mu_assoc_t assoc, size_t *pcount)
+{
+ mu_iterator_t itr;
+ int rc;
+ size_t count = 0;
+
+ if (!assoc || !pcount)
+ return EINVAL;
+ rc = mu_assoc_get_iterator (assoc, &itr);
+ if (rc)
+ return rc;
+ for (mu_iterator_first (itr); !mu_iterator_is_done (itr);
+ mu_iterator_next (itr))
+ count++;
+ mu_iterator_destroy (&itr);
+ *pcount = count;
+ return 0;
+}
+
diff --git a/libmailutils/attachment.c b/libmailutils/attachment.c
new file mode 100644
index 000000000..6bed79fff
--- /dev/null
+++ b/libmailutils/attachment.c
@@ -0,0 +1,467 @@
+/* GNU Mailutils -- a suite of utilities for electronic mail
+ Copyright (C) 1999, 2000, 2001, 2004, 2005, 2007, 2009, 2010 Free
+ Software Foundation, Inc.
+
+ This library is free software; you can redistribute it and/or
+ modify it under the terms of the GNU Lesser General Public
+ License as published by the Free Software Foundation; either
+ version 3 of the License, or (at your option) any later version.
+
+ This library 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
+ Lesser General Public License for more details.
+
+ You should have received a copy of the GNU Lesser General
+ Public License along with this library; if not, write to the
+ Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor,
+ Boston, MA 02110-1301 USA */
+
+#ifdef HAVE_CONFIG_H
+#include <config.h>
+#endif
+
+#include <errno.h>
+#include <stdio.h>
+#include <stdlib.h>
+#include <string.h>
+#include <unistd.h>
+
+#ifdef HAVE_LIBGEN_H
+# include <libgen.h>
+#endif
+
+#ifdef HAVE_STRINGS_H
+# include <strings.h>
+#endif
+
+#include <mailutils/cctype.h>
+#include <mailutils/cstr.h>
+#include <mailutils/body.h>
+#include <mailutils/filter.h>
+#include <mailutils/header.h>
+#include <mailutils/message.h>
+#include <mailutils/stream.h>
+#include <mailutils/errno.h>
+#include <mailutils/mutil.h>
+
+#define BUF_SIZE 2048
+
+struct _mu_mime_io_buffer
+{
+ unsigned int refcnt;
+ char *buf;
+ size_t bufsize;
+ char *charset;
+ mu_header_t hdr;
+ mu_message_t msg;
+ mu_stream_t stream; /* output file/decoding stream for saving attachment */
+ mu_stream_t fstream; /* output file stream for saving attachment */
+};
+
+#define MSG_HDR "Content-Type: %s; name=%s\nContent-Transfer-Encoding: %s\nContent-Disposition: attachment; filename=%s\n\n"
+
+int
+mu_message_create_attachment (const char *content_type, const char *encoding,
+ const char *filename, mu_message_t *newmsg)
+{
+ mu_header_t hdr;
+ mu_body_t body;
+ mu_stream_t fstream = NULL, tstream = NULL;
+ char *header = NULL, *name = NULL, *fname = NULL;
+ int ret;
+
+ if (newmsg == NULL)
+ return MU_ERR_OUT_PTR_NULL;
+ if (filename == NULL)
+ return EINVAL;
+
+ if ((ret = mu_message_create (newmsg, NULL)) == 0)
+ {
+ if (content_type == NULL)
+ content_type = "text/plain";
+ if (encoding == NULL)
+ encoding = "7bit";
+ if ((fname = strdup (filename)) != NULL)
+ {
+ name = strrchr (fname, '/');
+ if (name)
+ name++;
+ else
+ name = fname;
+ if ((header =
+ malloc (strlen (MSG_HDR) + strlen (content_type) +
+ strlen (name) * 2 + strlen (encoding) + 1)) == NULL)
+ ret = ENOMEM;
+ else
+ {
+ sprintf (header, MSG_HDR, content_type, name, encoding, name);
+ if ((ret = mu_header_create (&hdr, header, strlen (header)))
+ == 0)
+ {
+ mu_message_get_body (*newmsg, &body);
+ if ((ret =
+ mu_file_stream_create (&fstream, filename,
+ MU_STREAM_READ)) == 0)
+ {
+ if ((ret = mu_stream_open (fstream)) == 0)
+ {
+ if ((ret =
+ mu_filter_create (&tstream, fstream, encoding,
+ MU_FILTER_ENCODE,
+ MU_STREAM_READ)) == 0)
+ {
+ mu_body_set_stream (body, tstream, *newmsg);
+ mu_message_set_header (*newmsg, hdr, NULL);
+ }
+ }
+ }
+ }
+ free (header);
+ }
+ }
+ }
+ if (ret)
+ {
+ if (*newmsg)
+ mu_message_destroy (newmsg, NULL);
+ if (hdr)
+ mu_header_destroy (&hdr);
+ if (fstream)
+ mu_stream_destroy (&fstream);
+ if (fname)
+ free (fname);
+ }
+ return ret;
+}
+
+int
+mu_mime_io_buffer_create (mu_mime_io_buffer_t *pinfo)
+{
+ mu_mime_io_buffer_t info;
+
+ if ((info = calloc (1, sizeof (*info))) == NULL)
+ return ENOMEM;
+ info->refcnt = 1;
+ info->bufsize = BUF_SIZE;
+ *pinfo = info;
+ return 0;
+}
+
+void
+mu_mime_io_buffer_set_size (mu_mime_io_buffer_t info, size_t size)
+{
+ info->bufsize = size;
+}
+
+void
+mu_mime_io_buffer_get_size (mu_mime_io_buffer_t info, size_t *psize)
+{
+ *psize = info->bufsize;
+}
+
+int
+mu_mime_io_buffer_set_charset (mu_mime_io_buffer_t info, const char *charset)
+{
+ char *cp = strdup (charset);
+ if (!cp)
+ return ENOMEM;
+ free (info->charset);
+ info->charset = cp;
+ return 0;
+}
+
+void
+mu_mime_io_buffer_sget_charset (mu_mime_io_buffer_t info, const char **charset)
+{
+ *charset = info->charset;
+}
+
+int
+mu_mime_io_buffer_aget_charset (mu_mime_io_buffer_t info, const char **charset)
+{
+ *charset = strdup (info->charset);
+ if (!charset)
+ return ENOMEM;
+ return 0;
+}
+
+void
+mu_mime_io_buffer_destroy (mu_mime_io_buffer_t *pinfo)
+{
+ if (pinfo && *pinfo)
+ {
+ mu_mime_io_buffer_t info = *pinfo;
+ free (info->charset);
+ free (info->buf);
+ free (info);
+ *pinfo = NULL;
+ }
+}
+
+static void
+_attachment_free (struct _mu_mime_io_buffer *info, int free_message)
+{
+ if (free_message)
+ {
+ if (info->msg)
+ mu_message_destroy (&info->msg, NULL);
+ else if (info->hdr)
+ mu_header_destroy (&info->hdr);
+ }
+ info->msg = NULL;
+ info->hdr = NULL;
+ info->stream = NULL;
+ info->fstream = NULL;
+ if (--info->refcnt == 0)
+ {
+ free (info->charset);
+ free (info->buf);
+ free (info);
+ }
+}
+
+static int
+_attachment_setup (mu_mime_io_buffer_t *pinfo, mu_message_t msg,
+ mu_stream_t *pstream)
+{
+ int ret;
+ mu_body_t body;
+ mu_mime_io_buffer_t info;
+ mu_stream_t stream;
+
+ if ((ret = mu_message_get_body (msg, &body)) != 0 ||
+ (ret = mu_body_get_streamref (body, &stream)) != 0)
+ return ret;
+ ret = mu_stream_seek (stream, 0, SEEK_SET, NULL);
+ if (ret)
+ return ret;
+ *pstream = stream;
+ if (*pinfo)
+ {
+ info = *pinfo;
+ info->refcnt++;
+ }
+ else
+ {
+ ret = mu_mime_io_buffer_create (&info);
+ if (ret)
+ return ret;
+ }
+
+ if (!info->buf && ((info->buf = malloc (info->bufsize)) == NULL))
+ {
+ _attachment_free (info, 0);
+ return ENOMEM;
+ }
+ info->msg = msg;
+ *pinfo = info;
+ return 0;
+}
+
+int
+mu_message_save_attachment (mu_message_t msg, const char *filename,
+ mu_mime_io_buffer_t info)
+{
+ mu_stream_t istream;
+ int ret;
+ size_t size;
+ size_t nbytes;
+ mu_header_t hdr;
+ const char *fname = NULL;
+ char *partname = NULL;
+
+ if (msg == NULL)
+ return EINVAL;
+
+ if ((ret = _attachment_setup (&info, msg, &istream)) != 0)
+ return ret;
+
+ if (ret == 0 && (ret = mu_message_get_header (msg, &hdr)) == 0)
+ {
+ if (filename == NULL)
+ {
+ ret = mu_message_aget_decoded_attachment_name (msg, info->charset,
+ &partname, NULL);
+ if (partname)
+ fname = partname;
+ }
+ else
+ fname = filename;
+ if (fname
+ && (ret =
+ mu_file_stream_create (&info->fstream, fname,
+ MU_STREAM_WRITE | MU_STREAM_CREAT)) == 0)
+ {
+ if ((ret = mu_stream_open (info->fstream)) == 0)
+ {
+ char *content_encoding;
+ char *content_encoding_mem = NULL;
+
+ mu_header_get_value (hdr, "Content-Transfer-Encoding", NULL, 0,
+ &size);
+ if (size)
+ {
+ content_encoding_mem = malloc (size + 1);
+ if (content_encoding_mem == NULL)
+ ret = ENOMEM;
+ content_encoding = content_encoding_mem;
+ mu_header_get_value (hdr, "Content-Transfer-Encoding",
+ content_encoding, size + 1, 0);
+ }
+ else
+ content_encoding = "7bit";
+ ret =
+ mu_filter_create (&info->stream, istream, content_encoding,
+ MU_FILTER_DECODE,
+ MU_STREAM_READ);
+ free (content_encoding_mem);
+ }
+ }
+ }
+ if (info->stream && istream)
+ {
+ while (((ret =
+ mu_stream_read (info->stream, info->buf, BUF_SIZE,
+ &nbytes)) == 0 && nbytes))
+ {
+ if ((ret =
+ mu_stream_write (info->fstream, info->buf, nbytes, NULL)) != 0)
+ break;
+ }
+ }
+ if (ret != EAGAIN && info)
+ {
+ mu_stream_close (info->fstream);
+ mu_stream_destroy (&info->stream);
+ mu_stream_destroy (&info->fstream);
+ }
+
+ mu_stream_destroy (&istream);
+ _attachment_free (info, ret); /* FIXME: or 0? */
+
+ /* Free fname if we allocated it. */
+ if (partname)
+ free (partname);
+
+ return ret;
+}
+
+int
+mu_message_encapsulate (mu_message_t msg, mu_message_t *newmsg,
+ mu_mime_io_buffer_t info)
+{
+ mu_stream_t istream, ostream;
+ const char *header;
+ int ret = 0;
+ size_t nbytes;
+ mu_message_t tmsg = NULL;
+
+ if (newmsg == NULL)
+ return MU_ERR_OUT_PTR_NULL;
+
+ if (msg == NULL)
+ {
+ mu_header_t hdr;
+
+ ret = mu_message_create (&tmsg, NULL);
+ if (ret)
+ return ret;
+ msg = tmsg;
+ header =
+ "Content-Type: message/rfc822\nContent-Transfer-Encoding: 7bit\n\n";
+ if ((ret =
+ mu_header_create (&hdr, header, strlen (header))) == 0)
+ ret = mu_message_set_header (msg, hdr, NULL);
+ if (ret)
+ {
+ mu_message_destroy (&msg, NULL);
+ return ret;
+ }
+ }
+
+ if ((ret = _attachment_setup (&info, msg, &ostream)) != 0)
+ {
+ mu_message_destroy (&tmsg, NULL);
+ return ret;
+ }
+
+ if (ret == 0 && (ret = mu_message_get_streamref (msg, &istream)) == 0)
+ {
+ mu_stream_seek (istream, 0, MU_SEEK_SET, NULL);
+ while (((ret = mu_stream_read (istream, info->buf, BUF_SIZE,
+ &nbytes)) == 0 && nbytes))
+ {
+ if ((ret =
+ mu_stream_write (ostream, info->buf, nbytes, NULL)) != 0)
+ break;
+ }
+ mu_stream_destroy (&istream);
+ }
+ if (ret == 0)
+ *newmsg = info->msg;
+ mu_stream_destroy (&ostream);
+ _attachment_free (info, ret && ret != EAGAIN);
+ return ret;
+}
+
+#define MESSAGE_RFC822_STR "message/rfc822"
+
+int
+mu_message_unencapsulate (mu_message_t msg, mu_message_t *newmsg,
+ mu_mime_io_buffer_t info)
+{
+ size_t size, nbytes;
+ int ret = 0;
+ mu_header_t hdr;
+ mu_stream_t istream, ostream;
+
+ if (msg == NULL)
+ return EINVAL;
+ if (newmsg == NULL)
+ return MU_ERR_OUT_PTR_NULL;
+
+ if (info == NULL /* FIXME: not needed? */
+ && (ret = mu_message_get_header (msg, &hdr)) == 0)
+ {
+ mu_header_get_value (hdr, "Content-Type", NULL, 0, &size);
+ if (size)
+ {
+ char *content_type;
+ if ((content_type = malloc (size + 1)) == NULL)
+ return ENOMEM;
+ mu_header_get_value (hdr, "Content-Type", content_type, size + 1,
+ 0);
+ ret = mu_c_strncasecmp (content_type, MESSAGE_RFC822_STR,
+ sizeof (MESSAGE_RFC822_STR) - 1);
+ free (content_type);
+ if (ret != 0)
+ return EINVAL;
+ }
+ else
+ return EINVAL;
+ }
+ if ((ret = _attachment_setup (&info, msg, &istream)) != 0)
+ return ret;
+ if (info->msg == NULL)
+ ret = mu_message_create (&info->msg, NULL);
+ if (ret == 0)
+ {
+ mu_message_get_streamref (info->msg, &ostream);
+ mu_stream_seek (ostream, 0, MU_SEEK_SET, NULL);
+ while (((ret =
+ mu_stream_read (istream, info->buf, BUF_SIZE,
+ &nbytes)) == 0 && nbytes))
+ {
+ if ((ret =
+ mu_stream_write (ostream, info->buf, nbytes, NULL)) != 0)
+ break;
+ }
+ mu_stream_destroy (&ostream);
+ }
+ if (ret == 0)
+ *newmsg = info->msg;
+ mu_stream_destroy (&istream);
+ _attachment_free (info, ret && ret != EAGAIN);
+ return ret;
+}
diff --git a/libmailutils/attribute.c b/libmailutils/attribute.c
new file mode 100644
index 000000000..3e76855fd
--- /dev/null
+++ b/libmailutils/attribute.c
@@ -0,0 +1,470 @@
+/* GNU Mailutils -- a suite of utilities for electronic mail
+ Copyright (C) 1999, 2000, 2001, 2004, 2005, 2007, 2009, 2010 Free
+ Software Foundation, Inc.
+
+ This library is free software; you can redistribute it and/or
+ modify it under the terms of the GNU Lesser General Public
+ License as published by the Free Software Foundation; either
+ version 3 of the License, or (at your option) any later version.
+
+ This library 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
+ Lesser General Public License for more details.
+
+ You should have received a copy of the GNU Lesser General
+ Public License along with this library; if not, write to the
+ Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor,
+ Boston, MA 02110-1301 USA */
+
+#ifdef HAVE_CONFIG_H
+# include <config.h>
+#endif
+
+#include <sys/types.h>
+#include <stdlib.h>
+#include <string.h>
+#include <errno.h>
+
+#ifdef HAVE_STRINGS_H
+# include <strings.h>
+#endif
+
+#include <mailutils/errno.h>
+#include <mailutils/mutil.h>
+#include <mailutils/cstr.h>
+#include <mailutils/sys/attribute.h>
+
+int
+mu_attribute_create (mu_attribute_t *pattr, void *owner)
+{
+ mu_attribute_t attr;
+ if (pattr == NULL)
+ return MU_ERR_OUT_PTR_NULL;
+ attr = calloc (1, sizeof(*attr));
+ if (attr == NULL)
+ return ENOMEM;
+ attr->owner = owner;
+ *pattr = attr;
+ return 0;
+}
+
+void
+mu_attribute_destroy (mu_attribute_t *pattr, void *owner)
+{
+ if (pattr && *pattr)
+ {
+ mu_attribute_t attr = *pattr;
+ if (attr->owner == owner)
+ free (*pattr);
+ /* Loose the link */
+ *pattr = NULL;
+ }
+}
+
+void *
+mu_attribute_get_owner (mu_attribute_t attr)
+{
+ return (attr) ? attr->owner : NULL;
+}
+
+int
+mu_attribute_is_modified (mu_attribute_t attr)
+{
+ return (attr) ? attr->flags & MU_ATTRIBUTE_MODIFIED : 0;
+}
+
+int
+mu_attribute_clear_modified (mu_attribute_t attr)
+{
+ if (attr)
+ attr->flags &= ~MU_ATTRIBUTE_MODIFIED;
+ return 0;
+}
+
+int
+mu_attribute_set_modified (mu_attribute_t attr)
+{
+ if (attr)
+ attr->flags |= MU_ATTRIBUTE_MODIFIED;
+ return 0;
+}
+
+int
+mu_attribute_get_flags (mu_attribute_t attr, int *pflags)
+{
+ if (attr == NULL)
+ return EINVAL;
+ if (pflags == NULL)
+ return MU_ERR_OUT_PTR_NULL;
+ if (attr->_get_flags)
+ return attr->_get_flags (attr, pflags);
+ *pflags = attr->flags;
+ return 0;
+}
+
+int
+mu_attribute_set_flags (mu_attribute_t attr, int flags)
+{
+ int status = 0;
+ int oflags = 0;
+
+ if (attr == NULL)
+ return EINVAL;
+
+ /* If the required bits are already set, do not modify anything */
+ mu_attribute_get_flags (attr, &oflags);
+ if ((oflags & flags) == flags)
+ return 0;
+
+ if (attr->_set_flags)
+ status = attr->_set_flags (attr, flags);
+ else
+ attr->flags |= flags;
+ if (status == 0)
+ mu_attribute_set_modified (attr);
+ return 0;
+}
+
+int
+mu_attribute_unset_flags (mu_attribute_t attr, int flags)
+{
+ int status = 0;
+ int oflags = 0;
+
+ if (attr == NULL)
+ return EINVAL;
+
+ /* If the required bits are already cleared, do not modify anything */
+ mu_attribute_get_flags (attr, &oflags);
+ if ((oflags & flags) == 0)
+ return 0;
+
+ if (attr->_unset_flags)
+ status = attr->_unset_flags (attr, flags);
+ else
+ attr->flags &= ~flags;
+ if (status == 0)
+ mu_attribute_set_modified (attr);
+ return 0;
+}
+
+int
+mu_attribute_set_get_flags (mu_attribute_t attr, int (*_get_flags)
+ (mu_attribute_t, int *), void *owner)
+{
+ if (attr == NULL)
+ return EINVAL;
+ if (attr->owner != owner)
+ return EACCES;
+ attr->_get_flags = _get_flags;
+ return 0;
+}
+
+int
+mu_attribute_set_set_flags (mu_attribute_t attr, int (*_set_flags)
+ (mu_attribute_t, int), void *owner)
+{
+ if (attr == NULL)
+ return EINVAL;
+ if (attr->owner != owner)
+ return EACCES;
+ attr->_set_flags = _set_flags;
+ return 0;
+}
+
+int
+mu_attribute_set_unset_flags (mu_attribute_t attr, int (*_unset_flags)
+ (mu_attribute_t, int), void *owner)
+{
+ if (attr == NULL)
+ return EINVAL;
+ if (attr->owner != owner)
+ return EACCES;
+ attr->_unset_flags = _unset_flags;
+ return 0;
+}
+
+/* We add support for "USER" flag, it is a way for external objects
+ Not being the owner to add custom flags. */
+int
+mu_attribute_set_userflag (mu_attribute_t attr, int flag)
+{
+ if (attr == NULL)
+ return EINVAL;
+ attr->user_flags |= flag;
+ return 0;
+}
+
+int
+mu_attribute_set_seen (mu_attribute_t attr)
+{
+ return mu_attribute_set_flags (attr, MU_ATTRIBUTE_SEEN);
+}
+
+int
+mu_attribute_set_answered (mu_attribute_t attr)
+{
+ return mu_attribute_set_flags (attr, MU_ATTRIBUTE_ANSWERED);
+}
+
+int
+mu_attribute_set_flagged (mu_attribute_t attr)
+{
+ return mu_attribute_set_flags (attr, MU_ATTRIBUTE_FLAGGED);
+}
+
+int
+mu_attribute_set_read (mu_attribute_t attr)
+{
+ return mu_attribute_set_flags (attr, MU_ATTRIBUTE_READ);
+}
+
+int
+mu_attribute_set_deleted (mu_attribute_t attr)
+{
+ return mu_attribute_set_flags (attr, MU_ATTRIBUTE_DELETED);
+}
+
+int
+mu_attribute_set_draft (mu_attribute_t attr)
+{
+ return mu_attribute_set_flags (attr, MU_ATTRIBUTE_DRAFT);
+}
+
+int
+mu_attribute_set_recent (mu_attribute_t attr)
+{
+ int status = mu_attribute_unset_flags (attr, MU_ATTRIBUTE_READ);
+ if (status == 0)
+ status = mu_attribute_unset_flags (attr, MU_ATTRIBUTE_SEEN);
+ return status;
+}
+
+int
+mu_attribute_is_userflag (mu_attribute_t attr, int flag)
+{
+ if (attr == NULL)
+ return 0;
+ return attr->user_flags & flag;
+}
+
+int
+mu_attribute_is_seen (mu_attribute_t attr)
+{
+ int flags = 0;
+ if (mu_attribute_get_flags (attr, &flags) == 0)
+ return flags & MU_ATTRIBUTE_SEEN;
+ return 0;
+}
+
+int
+mu_attribute_is_answered (mu_attribute_t attr)
+{
+ int flags = 0;
+ if (mu_attribute_get_flags (attr, &flags) == 0)
+ return flags & MU_ATTRIBUTE_ANSWERED;
+ return 0;
+}
+
+int
+mu_attribute_is_flagged (mu_attribute_t attr)
+{
+ int flags = 0;
+ if (mu_attribute_get_flags (attr, &flags) == 0)
+ return flags & MU_ATTRIBUTE_FLAGGED;
+ return 0;
+}
+
+int
+mu_attribute_is_read (mu_attribute_t attr)
+{
+ int flags = 0;
+ if (mu_attribute_get_flags (attr, &flags) == 0)
+ return flags & MU_ATTRIBUTE_READ;
+ return 0;
+}
+
+int
+mu_attribute_is_deleted (mu_attribute_t attr)
+{
+ int flags = 0;
+ if (mu_attribute_get_flags (attr, &flags) == 0)
+ return flags & MU_ATTRIBUTE_DELETED;
+ return 0;
+}
+
+int
+mu_attribute_is_draft (mu_attribute_t attr)
+{
+ int flags = 0;
+ if (mu_attribute_get_flags (attr, &flags) == 0)
+ return flags & MU_ATTRIBUTE_DRAFT;
+ return 0;
+}
+
+int
+mu_attribute_is_recent (mu_attribute_t attr)
+{
+ int flags = 0;
+ if (mu_attribute_get_flags (attr, &flags) == 0)
+ return MU_ATTRIBUTE_IS_UNSEEN(flags);
+ return 0;
+}
+
+int
+mu_attribute_unset_userflag (mu_attribute_t attr, int flag)
+{
+ if (attr == NULL)
+ return 0;
+ attr->user_flags &= ~flag;
+ return 0;
+}
+
+int
+mu_attribute_unset_seen (mu_attribute_t attr)
+{
+ return mu_attribute_unset_flags (attr, MU_ATTRIBUTE_SEEN);
+}
+
+int
+mu_attribute_unset_answered (mu_attribute_t attr)
+{
+ return mu_attribute_unset_flags (attr, MU_ATTRIBUTE_ANSWERED);
+}
+
+int
+mu_attribute_unset_flagged (mu_attribute_t attr)
+{
+ return mu_attribute_unset_flags (attr, MU_ATTRIBUTE_FLAGGED);
+}
+
+int
+mu_attribute_unset_read (mu_attribute_t attr)
+{
+ return mu_attribute_unset_flags (attr, MU_ATTRIBUTE_READ);
+}
+
+int
+mu_attribute_unset_deleted (mu_attribute_t attr)
+{
+ return mu_attribute_unset_flags (attr, MU_ATTRIBUTE_DELETED);
+}
+
+int
+mu_attribute_unset_draft (mu_attribute_t attr)
+{
+ return mu_attribute_unset_flags (attr, MU_ATTRIBUTE_DRAFT);
+}
+
+int
+mu_attribute_unset_recent (mu_attribute_t attr)
+{
+ return mu_attribute_unset_flags (attr, MU_ATTRIBUTE_SEEN);
+}
+
+int
+mu_attribute_is_equal (mu_attribute_t attr, mu_attribute_t attr2)
+{
+ int flags2 = 0, flags = 0;
+ mu_attribute_get_flags (attr, &flags);
+ mu_attribute_get_flags (attr2, &flags2);
+ return flags == flags;
+}
+
+/* Miscellaneous. */
+int
+mu_attribute_copy (mu_attribute_t dest, mu_attribute_t src)
+{
+ if (dest == NULL || src == NULL)
+ return EINVAL;
+ /* Can not be a deep copy. */
+ /* memcpy (dest, src, sizeof (*dest)); */
+ dest->flags = src->flags;
+ return 0;
+}
+
+struct flagtrans
+{
+ int flag;
+ char letter;
+};
+
+/* The two macros below are taken from gnulib module verify.h */
+#define mu_verify_true(R) \
+ (!!sizeof \
+ (struct { unsigned int verify_error_if_negative_size__: (R) ? 1 : -1; }))
+#define mu_verify(R) extern int (* verify_function__ (void)) [mu_verify_true (R)]
+
+static struct flagtrans flagtrans[] = {
+ { MU_ATTRIBUTE_SEEN, 'O' },
+ { MU_ATTRIBUTE_ANSWERED, 'A' },
+ { MU_ATTRIBUTE_FLAGGED, 'F' },
+ { MU_ATTRIBUTE_READ, 'R' },
+ { MU_ATTRIBUTE_DELETED, 'd' },
+ { 0 }
+};
+
+/* If cc reports an error in this statement, fix the MU_STATUS_BUF_SIZE
+ declaration in include/mailutils/attribute.h */
+mu_verify (MU_ARRAY_SIZE (flagtrans) == MU_STATUS_BUF_SIZE);
+
+int
+mu_string_to_flags (const char *buffer, int *pflags)
+{
+ const char *sep;
+
+ if (pflags == NULL)
+ return EINVAL;
+
+ /* Set the attribute */
+ if (mu_c_strncasecmp (buffer, "Status:", 7) == 0)
+ {
+ sep = strchr(buffer, ':'); /* pass the ':' */
+ sep++;
+ }
+ else
+ sep = buffer;
+
+ for (; *sep; sep++)
+ {
+ struct flagtrans *ft;
+
+ for (ft = flagtrans; ft->flag; ft++)
+ if (ft->letter == *sep)
+ {
+ *pflags |= ft->flag;
+ break;
+ }
+ }
+ return 0;
+}
+
+/* NOTE: When adding/removing flags, make sure to update the
+ MU_STATUS_BUF_SIZE define in include/mailutils/attribute.h */
+int
+mu_attribute_to_string (mu_attribute_t attr, char *buffer, size_t len,
+ size_t *pn)
+{
+ int flags = 0;
+ char buf[MU_STATUS_BUF_SIZE];
+ int i;
+ int rc;
+ struct flagtrans *ft;
+
+ rc = mu_attribute_get_flags (attr, &flags);
+ if (rc)
+ return rc;
+
+ i = 0;
+ for (ft = flagtrans; ft->flag; ft++)
+ if (ft->flag & flags)
+ buf[i++] = ft->letter;
+ buf[i++] = 0;
+
+ i = mu_cpystr (buffer, buf, i);
+ if (pn)
+ *pn = i;
+ return 0;
+}
+
diff --git a/libmailutils/auth.c b/libmailutils/auth.c
new file mode 100644
index 000000000..2d3e9749e
--- /dev/null
+++ b/libmailutils/auth.c
@@ -0,0 +1,164 @@
+/* GNU Mailutils -- a suite of utilities for electronic mail
+ Copyright (C) 1999, 2000, 2001, 2004, 2005, 2007, 2009, 2010 Free
+ Software Foundation, Inc.
+
+ This library is free software; you can redistribute it and/or
+ modify it under the terms of the GNU Lesser General Public
+ License as published by the Free Software Foundation; either
+ version 3 of the License, or (at your option) any later version.
+
+ This library 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
+ Lesser General Public License for more details.
+
+ You should have received a copy of the GNU Lesser General
+ Public License along with this library; if not, write to the
+ Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor,
+ Boston, MA 02110-1301 USA */
+
+#ifdef HAVE_CONFIG_H
+# include <config.h>
+#endif
+
+#include <errno.h>
+#include <sys/types.h>
+#include <string.h>
+#include <stdlib.h>
+
+#include <mailutils/errno.h>
+#include <mailutils/sys/auth.h>
+
+static int
+_authenticate_null (mu_authority_t auth MU_ARG_UNUSED)
+{
+ return 0;
+}
+
+int
+mu_authority_create_null (mu_authority_t *pauthority, void *owner)
+{
+ int rc = mu_authority_create(pauthority, NULL, owner);
+ if (rc)
+ return rc;
+ mu_authority_set_authenticate (*pauthority, _authenticate_null, owner);
+ return 0;
+}
+
+int
+mu_authority_create (mu_authority_t *pauthority, mu_ticket_t ticket, void *owner)
+{
+ mu_authority_t authority;
+ if (pauthority == NULL)
+ return MU_ERR_OUT_PTR_NULL;
+ authority = calloc (1, sizeof (*authority));
+ if (authority == NULL)
+ return ENOMEM;
+ authority->ticket = ticket;
+ authority->owner = owner;
+ *pauthority = authority;
+ return 0;
+}
+
+void
+mu_authority_destroy (mu_authority_t *pauthority, void *owner)
+{
+ if (pauthority && *pauthority)
+ {
+ mu_authority_t authority = *pauthority;
+ if (authority->owner == owner)
+ {
+ mu_ticket_destroy (&authority->ticket);
+ mu_list_destroy (&authority->auth_methods);
+ free (authority);
+ }
+ *pauthority = NULL;
+ }
+}
+
+void *
+mu_authority_get_owner (mu_authority_t authority)
+{
+ return (authority) ? authority->owner : NULL;
+}
+
+int
+mu_authority_set_ticket (mu_authority_t authority, mu_ticket_t ticket)
+{
+ if (authority == NULL)
+ return EINVAL;
+ if (authority->ticket)
+ mu_ticket_destroy (&authority->ticket);
+ authority->ticket = ticket;
+ return 0;
+}
+
+int
+mu_authority_get_ticket (mu_authority_t authority, mu_ticket_t *pticket)
+{
+ if (authority == NULL)
+ return EINVAL;
+ if (pticket == NULL)
+ return MU_ERR_OUT_PTR_NULL;
+ if (authority->ticket == NULL)
+ {
+ int status = mu_ticket_create (&(authority->ticket), authority);
+ if (status != 0)
+ return status;
+ }
+ *pticket = authority->ticket;
+ return 0;
+}
+
+struct auth_cb
+{
+ int status;
+ mu_authority_t authority;
+};
+
+static int
+try_auth (void *item, void *data)
+{
+ int (*authenticate) (mu_authority_t) = item;
+ struct auth_cb *cb = data;
+ if (authenticate (cb->authority) == 0)
+ {
+ cb->status = 0;
+ return 1;
+ }
+ return 0;
+}
+
+int
+mu_authority_authenticate (mu_authority_t authority)
+{
+ if (authority && authority->auth_methods)
+ {
+ struct auth_cb cb;
+ cb.status = MU_ERR_AUTH_FAILURE;
+ cb.authority = authority;
+ mu_list_do (authority->auth_methods, try_auth, &cb);
+ return cb.status;
+ }
+ return EINVAL;
+}
+
+int
+mu_authority_set_authenticate (mu_authority_t authority,
+ int (*_authenticate) (mu_authority_t),
+ void *owner)
+{
+ if (authority == NULL)
+ return EINVAL;
+
+ if (authority->owner != owner)
+ return EACCES;
+ if (!authority->auth_methods)
+ {
+ int rc = mu_list_create (&authority->auth_methods);
+ if (rc)
+ return rc;
+ }
+ mu_list_append (authority->auth_methods, _authenticate);
+ return 0;
+}
diff --git a/libmailutils/base64.c b/libmailutils/base64.c
new file mode 100644
index 000000000..7a8c62282
--- /dev/null
+++ b/libmailutils/base64.c
@@ -0,0 +1,273 @@
+/* GNU Mailutils -- a suite of utilities for electronic mail
+ Copyright (C) 2002, 2009, 2010 Free Software Foundation, Inc.
+
+ This library is free software; you can redistribute it and/or
+ modify it under the terms of the GNU Lesser General Public
+ License as published by the Free Software Foundation; either
+ version 3 of the License, or (at your option) any later version.
+
+ This library 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
+ Lesser General Public License for more details.
+
+ You should have received a copy of the GNU Lesser General Public License
+ along with GNU Mailutils. If not, see <http://www.gnu.org/licenses/>. */
+
+#ifdef HAVE_CONFIG_H
+# include <config.h>
+#endif
+
+#include <stdlib.h>
+#include <string.h>
+#include <mailutils/errno.h>
+#include <mailutils/filter.h>
+
+static char b64tab[] =
+ "ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789+/";
+static int b64val[128] = {
+ -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1,
+ -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1,
+ -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, 62, -1, -1, -1, 63,
+ 52, 53, 54, 55, 56, 57, 58, 59, 60, 61, -1, -1, -1, -1, -1, -1,
+ -1, 0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14,
+ 15, 16, 17, 18, 19, 20, 21, 22, 23, 24, 25, -1, -1, -1, -1, -1,
+ -1, 26, 27, 28, 29, 30, 31, 32, 33, 34, 35, 36, 37, 38, 39, 40,
+ 41, 42, 43, 44, 45, 46, 47, 48, 49, 50, 51, -1, -1, -1, -1, -1
+};
+
+int
+mu_base64_encode (const unsigned char *input, size_t input_len,
+ unsigned char **output, size_t *output_len)
+{
+ size_t olen = 4 * (input_len + 2) / 3 + 1;
+ unsigned char *out = malloc (olen);
+
+ if (!out)
+ return ENOMEM;
+ *output = out;
+ while (input_len >= 3)
+ {
+ *out++ = b64tab[input[0] >> 2];
+ *out++ = b64tab[((input[0] << 4) & 0x30) | (input[1] >> 4)];
+ *out++ = b64tab[((input[1] << 2) & 0x3c) | (input[2] >> 6)];
+ *out++ = b64tab[input[2] & 0x3f];
+ input_len -= 3;
+ input += 3;
+ }
+
+ if (input_len > 0)
+ {
+ unsigned char c = (input[0] << 4) & 0x30;
+ *out++ = b64tab[input[0] >> 2];
+ if (input_len > 1)
+ c |= input[1] >> 4;
+ *out++ = b64tab[c];
+ *out++ = (input_len < 2) ? '=' : b64tab[(input[1] << 2) & 0x3c];
+ *out++ = '=';
+ }
+ *output_len = out - *output;
+ *out = 0;
+ return 0;
+}
+
+int
+mu_base64_decode (const unsigned char *input, size_t input_len,
+ unsigned char **output, size_t *output_len)
+{
+ int olen = input_len;
+ unsigned char *out = malloc (olen);
+
+ if (!out)
+ return ENOMEM;
+ *output = out;
+ do
+ {
+ if (input[0] > 127 || b64val[input[0]] == -1
+ || input[1] > 127 || b64val[input[1]] == -1
+ || input[2] > 127 || ((input[2] != '=') && (b64val[input[2]] == -1))
+ || input[3] > 127 || ((input[3] != '=')
+ && (b64val[input[3]] == -1)))
+ return EINVAL;
+ *out++ = (b64val[input[0]] << 2) | (b64val[input[1]] >> 4);
+ if (input[2] != '=')
+ {
+ *out++ = ((b64val[input[1]] << 4) & 0xf0) | (b64val[input[2]] >> 2);
+ if (input[3] != '=')
+ *out++ = ((b64val[input[2]] << 6) & 0xc0) | b64val[input[3]];
+ }
+ input += 4;
+ input_len -= 4;
+ }
+ while (input_len > 0);
+ *output_len = out - *output;
+ return 0;
+}
+
+
+static enum mu_filter_result
+_base64_decoder (void *xd MU_ARG_UNUSED,
+ enum mu_filter_command cmd,
+ struct mu_filter_io *iobuf)
+{
+ int i = 0, tmp = 0, pad = 0;
+ size_t consumed = 0;
+ unsigned char data[4];
+ size_t nbytes = 0;
+ const char *iptr;
+ size_t isize;
+ char *optr;
+ size_t osize;
+
+ switch (cmd)
+ {
+ case mu_filter_init:
+ case mu_filter_done:
+ return mu_filter_ok;
+ default:
+ break;
+ }
+
+ if (osize <= 3)
+ {
+ iobuf->osize = 3;
+ return mu_filter_moreoutput;
+ }
+
+ iptr = iobuf->input;
+ isize = iobuf->isize;
+ optr = iobuf->output;
+ osize = iobuf->osize;
+
+ while (consumed < isize && nbytes + 3 < osize)
+ {
+ while (i < 4 && consumed < isize)
+ {
+ tmp = b64val[*(const unsigned char*)iptr++];
+ consumed++;
+ if (tmp != -1)
+ data[i++] = tmp;
+ else if (*(iptr-1) == '=')
+ {
+ data[i++] = 0;
+ pad++;
+ }
+ }
+
+ /* I have a entire block of data 32 bits get the output data. */
+ if (i == 4)
+ {
+ *optr++ = (data[0] << 2) | ((data[1] & 0x30) >> 4);
+ *optr++ = ((data[1] & 0xf) << 4) | ((data[2] & 0x3c) >> 2);
+ *optr++ = ((data[2] & 0x3) << 6) | data[3];
+ nbytes += 3 - pad;
+ }
+ else
+ {
+ /* I did not get all the data. */
+ consumed -= i;
+ break;
+ }
+ i = 0;
+ }
+ iobuf->isize = consumed;
+ iobuf->osize = nbytes;
+ return mu_filter_ok;
+}
+
+static enum mu_filter_result
+_base64_encoder (void *xd MU_ARG_UNUSED,
+ enum mu_filter_command cmd,
+ struct mu_filter_io *iobuf)
+{
+ size_t consumed = 0;
+ int pad = 0;
+ const unsigned char *ptr = (const unsigned char*) iobuf->input;
+ size_t nbytes = 0;
+ size_t isize;
+ char *optr;
+ size_t osize;
+
+ switch (cmd)
+ {
+ case mu_filter_init:
+ case mu_filter_done:
+ return mu_filter_ok;
+ default:
+ break;
+ }
+
+ if (iobuf->isize <= 3)
+ {
+ if (cmd == mu_filter_lastbuf)
+ pad = 1;
+ else
+ {
+ iobuf->isize = 4;
+ return mu_filter_moreinput;
+ }
+ }
+ if (osize < 4)
+ {
+ iobuf->osize = 4;
+ return mu_filter_moreoutput;
+ }
+
+ isize = iobuf->isize;
+ optr = iobuf->output;
+ osize = iobuf->osize;
+
+ while ((consumed + 3 <= isize && nbytes + 4 <= osize) || pad)
+ {
+ unsigned char c1 = 0, c2 = 0, x = '=', y = '=';
+
+ *optr++ = b64tab[ptr[0] >> 2];
+ consumed++;
+ switch (isize - consumed)
+ {
+ default:
+ consumed++;
+ y = b64tab[ptr[2] & 0x3f];
+ c2 = ptr[2] >> 6;
+ case 1:
+ consumed++;
+ x = b64tab[((ptr[1] << 2) + c2) & 0x3f];
+ c1 = (ptr[1] >> 4);
+ case 0:
+ *optr++ = b64tab[((ptr[0] << 4) + c1) & 0x3f];
+ *optr++ = x;
+ *optr++ = y;
+ }
+
+ ptr += 3;
+ nbytes += 4;
+ pad = 0;
+ }
+
+ /* Consumed may grow bigger than isize if cmd is mu_filter_lastbuf */
+ if (consumed > iobuf->isize)
+ consumed = iobuf->isize;
+ iobuf->isize = consumed;
+ iobuf->osize = nbytes;
+ return mu_filter_ok;
+}
+
+static struct _mu_filter_record _base64_filter = {
+ "base64",
+ 76,
+ NULL,
+ _base64_encoder,
+ _base64_decoder
+};
+
+mu_filter_record_t mu_base64_filter = &_base64_filter;
+
+static struct _mu_filter_record _B_filter = {
+ "B",
+ 0,
+ NULL,
+ _base64_encoder,
+ _base64_decoder
+};
+
+mu_filter_record_t mu_rfc_2047_B_filter = &_B_filter;
diff --git a/libmailutils/binflt.c b/libmailutils/binflt.c
new file mode 100644
index 000000000..d3de5185c
--- /dev/null
+++ b/libmailutils/binflt.c
@@ -0,0 +1,113 @@
+/* GNU Mailutils -- a suite of utilities for electronic mail
+ Copyright (C) 2009, 2010 Free Software Foundation, Inc.
+
+ This library is free software; you can redistribute it and/or modify
+ it under the terms of the GNU Lesser General Public License as published by
+ the Free Software Foundation; either version 3, or (at your option)
+ any later version.
+
+ This library 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 Lesser General Public License for more details.
+
+ You should have received a copy of the GNU Lesser General Public License
+ along with GNU Mailutils. If not, see <http://www.gnu.org/licenses/>. */
+
+#ifdef HAVE_CONFIG_H
+# include <config.h>
+#endif
+#include <stdlib.h>
+#include <string.h>
+#include <mailutils/errno.h>
+#include <mailutils/filter.h>
+
+static enum mu_filter_result
+_copy_codec (void *xd MU_ARG_UNUSED,
+ enum mu_filter_command cmd,
+ struct mu_filter_io *iobuf)
+{
+ size_t osize;
+
+ switch (cmd)
+ {
+ case mu_filter_init:
+ case mu_filter_done:
+ return mu_filter_ok;
+ default:
+ break;
+ }
+
+ osize = iobuf->osize;
+ if (osize > iobuf->isize)
+ osize = iobuf->isize;
+ memcpy (iobuf->output, iobuf->input, osize);
+ iobuf->isize = osize;
+ iobuf->osize = osize;
+ return mu_filter_ok;
+}
+
+static enum mu_filter_result
+_bit7_coder (void *xd MU_ARG_UNUSED,
+ enum mu_filter_command cmd,
+ struct mu_filter_io *iobuf)
+{
+ size_t i;
+ const unsigned char *iptr;
+ size_t isize;
+ char *optr;
+ size_t osize;
+
+ switch (cmd)
+ {
+ case mu_filter_init:
+ case mu_filter_done:
+ return mu_filter_ok;
+ default:
+ break;
+ }
+
+ iptr = (const unsigned char *) iobuf->input;
+ isize = iobuf->isize;
+ optr = iobuf->output;
+ osize = iobuf->osize;
+
+ if (osize > isize)
+ osize = isize;
+ for (i = 0; i < osize; i++)
+ *optr++ = *iptr++ & 0x7f;
+ iobuf->isize = osize;
+ iobuf->osize = osize;
+ return mu_filter_ok;
+}
+
+static struct _mu_filter_record _binary_filter = {
+ "binary",
+ 0,
+ NULL,
+ _copy_codec,
+ _copy_codec
+};
+
+mu_filter_record_t mu_binary_filter = &_binary_filter;
+
+
+static struct _mu_filter_record _bit8_filter = {
+ "bit8",
+ 0,
+ NULL,
+ _copy_codec,
+ _copy_codec
+};
+
+mu_filter_record_t mu_bit8_filter = &_bit8_filter;
+
+static struct _mu_filter_record _bit7_filter = {
+ "bit7",
+ 0,
+ NULL,
+ _bit7_coder,
+ _copy_codec
+};
+
+mu_filter_record_t mu_bit7_filter = &_bit7_filter;
diff --git a/libmailutils/body.c b/libmailutils/body.c
new file mode 100644
index 000000000..513ad01ac
--- /dev/null
+++ b/libmailutils/body.c
@@ -0,0 +1,419 @@
+/* GNU Mailutils -- a suite of utilities for electronic mail
+ Copyright (C) 1999, 2000, 2001, 2004, 2005, 2007, 2010 Free Software
+ Foundation, Inc.
+
+ This library is free software; you can redistribute it and/or
+ modify it under the terms of the GNU Lesser General Public
+ License as published by the Free Software Foundation; either
+ version 3 of the License, or (at your option) any later version.
+
+ This library 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
+ Lesser General Public License for more details.
+
+ You should have received a copy of the GNU Lesser General
+ Public License along with this library; if not, write to the
+ Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor,
+ Boston, MA 02110-1301 USA */
+
+#ifdef HAVE_CONFIG_H
+# include <config.h>
+#endif
+
+#include <errno.h>
+#include <stdio.h>
+#include <stdlib.h>
+#include <string.h>
+#include <unistd.h>
+#include <sys/types.h>
+#include <sys/stat.h>
+#include <fcntl.h>
+
+#include <mailutils/stream.h>
+#include <mailutils/mutil.h>
+#include <mailutils/errno.h>
+#include <mailutils/sys/stream.h>
+#include <mailutils/sys/body.h>
+
+#define BODY_MODIFIED 0x10000
+
+static int _body_flush (mu_stream_t);
+static int _body_read (mu_stream_t, char *, size_t, size_t *);
+static int _body_truncate (mu_stream_t, mu_off_t);
+static int _body_size (mu_stream_t, mu_off_t *);
+static int _body_write (mu_stream_t, const char *, size_t, size_t *);
+static int _body_ioctl (mu_stream_t, int, void *);
+static int _body_seek (mu_stream_t, mu_off_t, mu_off_t *);
+static const char *_body_error_string (mu_stream_t, int);
+
+/* Our own defaults for the body. */
+static int _body_get_size (mu_body_t, size_t *);
+static int _body_get_lines (mu_body_t, size_t *);
+static int _body_get_size0 (mu_stream_t, size_t *);
+static int _body_get_lines0 (mu_stream_t, size_t *);
+
+int
+mu_body_create (mu_body_t *pbody, void *owner)
+{
+ mu_body_t body;
+
+ if (pbody == NULL)
+ return MU_ERR_OUT_PTR_NULL;
+ if (owner == NULL)
+ return EINVAL;
+
+ body = calloc (1, sizeof (*body));
+ if (body == NULL)
+ return ENOMEM;
+
+ body->owner = owner;
+ *pbody = body;
+ return 0;
+}
+
+void
+mu_body_destroy (mu_body_t *pbody, void *owner)
+{
+ if (pbody && *pbody)
+ {
+ mu_body_t body = *pbody;
+ if (body->owner == owner)
+ {
+ if (body->filename)
+ {
+ /* FIXME: should we do this? */
+ remove (body->filename);
+ free (body->filename);
+ }
+
+ if (body->stream)
+ mu_stream_destroy (&body->stream);
+
+ if (body->fstream)
+ {
+ mu_stream_close (body->fstream);
+ mu_stream_destroy (&body->fstream);
+ }
+
+ free (body);
+ }
+ *pbody = NULL;
+ }
+}
+
+void *
+mu_body_get_owner (mu_body_t body)
+{
+ return (body) ? body->owner : NULL;
+}
+
+/* FIXME: not implemented. */
+int
+mu_body_is_modified (mu_body_t body)
+{
+ return (body) ? (body->flags & BODY_MODIFIED) : 0;
+}
+
+/* FIXME: not implemented. */
+int
+mu_body_clear_modified (mu_body_t body)
+{
+ if (body)
+ body->flags &= ~BODY_MODIFIED;
+ return 0;
+}
+
+int
+mu_body_get_filename (mu_body_t body, char *filename, size_t len, size_t *pn)
+{
+ int n = 0;
+ if (body == NULL)
+ return EINVAL;
+ if (body->filename)
+ {
+ n = strlen (body->filename);
+ if (filename && len > 0)
+ {
+ len--; /* Space for the null. */
+ strncpy (filename, body->filename, len)[len] = '\0';
+ }
+ }
+ if (pn)
+ *pn = n;
+ return 0;
+}
+
+
+struct _mu_body_stream
+{
+ struct _mu_stream stream;
+ mu_body_t body;
+};
+
+static int
+_body_get_stream (mu_body_t body, mu_stream_t *pstream, int ref)
+{
+ if (body == NULL)
+ return EINVAL;
+ if (pstream == NULL)
+ return MU_ERR_OUT_PTR_NULL;
+
+ if (body->stream == NULL)
+ {
+ if (body->_get_stream)
+ {
+ int status = body->_get_stream (body, &body->stream);
+ if (status)
+ return status;
+ }
+ else
+ {
+ int status;
+ struct _mu_body_stream *str =
+ (struct _mu_body_stream *)
+ _mu_stream_create (sizeof (*str),
+ MU_STREAM_RDWR|MU_STREAM_SEEK);
+ if (!str)
+ return ENOMEM;
+
+ /* Create the temporary file. */
+ body->filename = mu_tempname (NULL);
+ status = mu_file_stream_create (&body->fstream,
+ body->filename, MU_STREAM_RDWR);
+ if (status != 0)
+ return status;
+ status = mu_stream_open (body->fstream);
+ if (status != 0)
+ return status;
+ str->stream.ctl = _body_ioctl;
+ str->stream.read = _body_read;
+ str->stream.write = _body_write;
+ str->stream.truncate = _body_truncate;
+ str->stream.size = _body_size;
+ str->stream.seek = _body_seek;
+ str->stream.flush = _body_flush;
+ str->body = body;
+ body->stream = (mu_stream_t) str;
+ /* Override the defaults. */
+ body->_lines = _body_get_lines;
+ body->_size = _body_get_size;
+ }
+ }
+
+ if (!ref)
+ {
+ *pstream = body->stream;
+ return 0;
+ }
+ return mu_streamref_create (pstream, body->stream);
+}
+
+int
+mu_body_get_stream (mu_body_t body, mu_stream_t *pstream)
+{
+ /* FIXME: Deprecation warning */
+ return _body_get_stream (body, pstream, 0);
+}
+
+int
+mu_body_get_streamref (mu_body_t body, mu_stream_t *pstream)
+{
+ return _body_get_stream (body, pstream, 1);
+}
+
+int
+mu_body_set_stream (mu_body_t body, mu_stream_t stream, void *owner)
+{
+ if (body == NULL)
+ return EINVAL;
+ if (body->owner != owner)
+ return EACCES;
+ /* make sure we destroy the old one if it is owned by the body */
+ mu_stream_destroy (&body->stream);
+ body->stream = stream;
+ body->flags |= BODY_MODIFIED;
+ return 0;
+}
+
+int
+mu_body_set_get_stream (mu_body_t body,
+ int (*_getstr) (mu_body_t, mu_stream_t *),
+ void *owner)
+{
+ if (body == NULL)
+ return EINVAL;
+ if (body->owner != owner)
+ return EACCES;
+ body->_get_stream = _getstr;
+ return 0;
+}
+
+int
+mu_body_set_lines (mu_body_t body, int (*_lines) (mu_body_t, size_t *),
+ void *owner)
+{
+ if (body == NULL)
+ return EINVAL;
+ if (body->owner != owner)
+ return EACCES;
+ body->_lines = _lines;
+ return 0;
+}
+
+int
+mu_body_lines (mu_body_t body, size_t *plines)
+{
+ if (body == NULL)
+ return EINVAL;
+ if (body->_lines)
+ return body->_lines (body, plines);
+ /* Fall on the stream. */
+ if (body->stream)
+ return _body_get_lines0 (body->stream, plines);
+ if (plines)
+ *plines = 0;
+ return 0;
+}
+
+int
+mu_body_size (mu_body_t body, size_t *psize)
+{
+ if (body == NULL)
+ return EINVAL;
+ if (body->_size)
+ return body->_size (body, psize);
+ /* Fall on the stream. */
+ if (body->stream)
+ return _body_get_size0 (body->stream, psize);
+ if (psize)
+ *psize = 0;
+ return 0;
+}
+
+int
+mu_body_set_size (mu_body_t body, int (*_size)(mu_body_t, size_t*) , void *owner)
+{
+ if (body == NULL)
+ return EINVAL;
+ if (body->owner != owner)
+ return EACCES;
+ body->_size = _size;
+ return 0;
+}
+
+/* Stub function for the body stream. */
+
+static int
+_body_seek (mu_stream_t stream, mu_off_t off, mu_off_t *presult)
+{
+ struct _mu_body_stream *str = (struct _mu_body_stream*) stream;
+ mu_body_t body = str->body;
+ return mu_stream_seek (body->fstream, off, MU_SEEK_SET, presult);
+}
+
+static const char *
+_body_error_string (mu_stream_t stream, int rc)
+{
+ /* FIXME: How to know if rc was returned by a body->stream? */
+ return NULL;
+}
+
+static int
+_body_ioctl (mu_stream_t stream, int code, void *ptr)
+{
+ struct _mu_body_stream *str = (struct _mu_body_stream*) stream;
+ mu_body_t body = str->body;
+ return mu_stream_ioctl (body->fstream, code, ptr);
+}
+
+static int
+_body_read (mu_stream_t stream, char *buf, size_t size, size_t *pret)
+{
+ struct _mu_body_stream *str = (struct _mu_body_stream*) stream;
+ mu_body_t body = str->body;
+ return mu_stream_read (body->fstream, buf, size, pret);
+}
+
+static int
+_body_write (mu_stream_t stream, const char *buf, size_t size, size_t *pret)
+{
+ struct _mu_body_stream *str = (struct _mu_body_stream*) stream;
+ mu_body_t body = str->body;
+ return mu_stream_write (body->fstream, buf, size, pret);
+}
+
+static int
+_body_truncate (mu_stream_t stream, mu_off_t n)
+{
+ struct _mu_body_stream *str = (struct _mu_body_stream*) stream;
+ mu_body_t body = str->body;
+ return mu_stream_truncate (body->fstream, n);
+}
+
+static int
+_body_size (mu_stream_t stream, mu_off_t *size)
+{
+ struct _mu_body_stream *str = (struct _mu_body_stream*) stream;
+ mu_body_t body = str->body;
+ return mu_stream_size (body->fstream, size);
+}
+
+static int
+_body_flush (mu_stream_t stream)
+{
+ struct _mu_body_stream *str = (struct _mu_body_stream*) stream;
+ mu_body_t body = str->body;
+ return mu_stream_flush (body->fstream);
+}
+
+/* Default function for the body. */
+static int
+_body_get_lines (mu_body_t body, size_t *plines)
+{
+ return _body_get_lines0 (body->fstream, plines);
+}
+
+static int
+_body_get_size (mu_body_t body, size_t *psize)
+{
+ return _body_get_size0 (body->fstream, psize);
+}
+
+static int
+_body_get_size0 (mu_stream_t stream, size_t *psize)
+{
+ mu_off_t off = 0;
+ int status = mu_stream_size (stream, &off);
+ if (psize)
+ *psize = off;
+ return status;
+}
+
+static int
+_body_get_lines0 (mu_stream_t stream, size_t *plines)
+{
+ int status = mu_stream_flush (stream);
+ size_t lines = 0;
+
+ if (status == 0)
+ {
+ char buf[128];
+ size_t n = 0;
+
+ status = mu_stream_seek (stream, 0, MU_SEEK_SET, NULL);
+ if (status)
+ return status;
+ while ((status = mu_stream_readline (stream, buf, sizeof buf,
+ &n)) == 0 && n > 0)
+ {
+ if (buf[n - 1] == '\n')
+ lines++;
+ }
+ }
+ if (plines)
+ *plines = lines;
+ return status;
+}
+
+
diff --git a/libmailutils/cfg_driver.c b/libmailutils/cfg_driver.c
new file mode 100644
index 000000000..f4d56f710
--- /dev/null
+++ b/libmailutils/cfg_driver.c
@@ -0,0 +1,755 @@
+/* cfg_driver.c -- Main driver for Mailutils configuration files
+ Copyright (C) 2007, 2008, 2009, 2010 Free Software Foundation, Inc.
+
+ GNU Mailutils 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 3, 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/>.
+*/
+#ifdef HAVE_CONFIG_H
+# include <config.h>
+#endif
+#include <stdio.h>
+#include <stdlib.h>
+#include <sys/types.h>
+#include <sys/stat.h>
+#include <fcntl.h>
+#include <errno.h>
+#include <unistd.h>
+#include <ctype.h>
+#include <mailutils/argcv.h>
+#include <mailutils/nls.h>
+#define MU_CFG_COMPATIBILITY /* This source uses deprecated cfg interfaces */
+#include <mailutils/cfg.h>
+#include <mailutils/errno.h>
+#include <mailutils/error.h>
+#include <mailutils/mutil.h>
+#include <mailutils/monitor.h>
+#include <mailutils/refcount.h>
+#include <mailutils/list.h>
+#include <mailutils/iterator.h>
+#include <mailutils/stream.h>
+#include <mailutils/assoc.h>
+#include <mailutils/alloc.h>
+
+
+static mu_assoc_t section_tab;
+
+static void
+alloc_section_tab ()
+{
+ if (!section_tab)
+ mu_assoc_create (&section_tab, sizeof (struct mu_cfg_cont **),
+ MU_ASSOC_COPY_KEY);
+}
+
+int
+mu_create_canned_section (char *name, struct mu_cfg_section **psection)
+{
+ int rc;
+ struct mu_cfg_cont **pcont;
+ alloc_section_tab ();
+ rc = mu_assoc_ref_install (section_tab, name, (void **)&pcont);
+ if (rc == 0)
+ {
+ mu_config_create_container (pcont, mu_cfg_cont_section);
+ *psection = &(*pcont)->v.section;
+ (*psection)->ident = name;
+ }
+ else if (rc == MU_ERR_EXISTS)
+ *psection = &(*pcont)->v.section;
+ return rc;
+}
+
+int
+mu_create_canned_param (char *name, struct mu_cfg_param **pparam)
+{
+ int rc;
+ struct mu_cfg_cont **pcont;
+ alloc_section_tab ();
+ rc = mu_assoc_ref_install (section_tab, name, (void **)&pcont);
+ if (rc == 0)
+ {
+ mu_config_create_container (pcont, mu_cfg_cont_param);
+ *pparam = &(*pcont)->v.param;
+ (*pparam)->ident = name;
+ }
+ else if (rc == MU_ERR_EXISTS)
+ *pparam = &(*pcont)->v.param;
+ return rc;
+}
+
+struct mu_cfg_cont *
+mu_get_canned_container (const char *name)
+{
+ struct mu_cfg_cont **pcont = mu_assoc_ref (section_tab, name);
+ return pcont ? *pcont : NULL;
+}
+
+
+static struct mu_cfg_cont *root_container;
+
+int
+mu_config_create_container (struct mu_cfg_cont **pcont,
+ enum mu_cfg_cont_type type)
+{
+ struct mu_cfg_cont *cont;
+ int rc;
+
+ cont = calloc (1, sizeof (*cont));
+ if (!cont)
+ return ENOMEM;
+ rc = mu_refcount_create (&cont->refcount);
+ if (rc)
+ free (cont);
+ else
+ {
+ cont->type = type;
+ *pcont = cont;
+ }
+ return rc;
+}
+
+
+struct dup_data
+{
+ struct mu_cfg_cont *cont;
+};
+
+static int dup_container (struct mu_cfg_cont **pcont);
+
+static int
+_dup_cont_action (void *item, void *cbdata)
+{
+ int rc;
+ struct mu_cfg_cont *cont = item;
+ struct dup_data *pdd = cbdata;
+
+ rc = dup_container (&cont);
+ if (rc)
+ return rc;
+
+ if (!pdd->cont->v.section.children)
+ {
+ int rc = mu_list_create (&pdd->cont->v.section.children);
+ if (rc)
+ return rc;
+ }
+ return mu_list_append (pdd->cont->v.section.children, cont);
+}
+
+static int
+dup_container (struct mu_cfg_cont **pcont)
+{
+ int rc;
+ struct mu_cfg_cont *newcont, *oldcont = *pcont;
+ struct dup_data dd;
+
+ rc = mu_config_create_container (&newcont, oldcont->type);
+ if (rc)
+ return rc;
+
+ dd.cont = newcont;
+ switch (oldcont->type)
+ {
+ case mu_cfg_cont_section:
+ newcont->v.section.ident = oldcont->v.section.ident;
+ newcont->v.section.label = oldcont->v.section.label;
+ newcont->v.section.parser = oldcont->v.section.parser;
+ newcont->v.section.target = oldcont->v.section.target;
+ newcont->v.section.offset = oldcont->v.section.offset;
+ newcont->v.section.docstring = oldcont->v.section.docstring;
+ newcont->v.section.children = NULL;
+ mu_list_do (oldcont->v.section.children, _dup_cont_action, &dd);
+ break;
+
+ case mu_cfg_cont_param:
+ newcont->v.param = oldcont->v.param;
+ break;
+ }
+ *pcont = newcont;
+ return 0;
+}
+
+
+static void
+destroy_list (mu_list_t *plist)
+{
+ mu_list_t list = *plist;
+ mu_iterator_t itr = NULL;
+
+ if (!list)
+ return;
+
+ mu_list_get_iterator (list, &itr);
+ for (mu_iterator_first (itr); !mu_iterator_is_done (itr);
+ mu_iterator_next (itr))
+ {
+ struct mu_cfg_cont *cont, *p;
+ mu_iterator_current (itr, (void**)&cont);
+ p = cont;
+ mu_config_destroy_container (&p);
+ if (!p)
+ mu_list_remove (list, cont);
+ }
+ mu_iterator_destroy (&itr);
+ if (mu_list_is_empty (list))
+ mu_list_destroy (plist);
+}
+
+void
+mu_config_destroy_container (struct mu_cfg_cont **pcont)
+{
+ struct mu_cfg_cont *cont = *pcont;
+ unsigned refcount = mu_refcount_dec (cont->refcount);
+ /* printf ("destr %p-%s: %d\n", cont, cont->v.section.ident, refcount); */
+ switch (cont->type)
+ {
+ case mu_cfg_cont_section:
+ destroy_list (&cont->v.section.children);
+ break;
+
+ case mu_cfg_cont_param:
+ break;
+ }
+
+ if (refcount == 0)
+ {
+ free (cont);
+ *pcont = 0;
+ }
+}
+
+
+int
+mu_cfg_section_add_container (struct mu_cfg_section *sect,
+ struct mu_cfg_cont *cont)
+{
+ if (!cont)
+ return 0;
+ if (!sect->children)
+ mu_list_create (&sect->children);
+ return mu_list_append (sect->children, cont);
+}
+
+int
+mu_cfg_section_add_params (struct mu_cfg_section *sect,
+ struct mu_cfg_param *param)
+{
+ if (!param)
+ return 0;
+
+ for (; param->ident; param++)
+ {
+ int rc;
+ struct mu_cfg_cont *container;
+
+ if (param->type == mu_cfg_section)
+ {
+ container = mu_get_canned_container (param->ident);
+ if (!container)
+ {
+ mu_error (_("INTERNAL ERROR: Requested unknown canned "
+ "section %s"),
+ param->ident);
+ abort ();
+ }
+ if (param->ident[0] == '.')
+ {
+ mu_iterator_t itr;
+ mu_list_get_iterator (container->v.section.children, &itr);
+ for (mu_iterator_first (itr);
+ !mu_iterator_is_done (itr);
+ mu_iterator_next (itr))
+ {
+ struct mu_cfg_cont *c;
+ mu_iterator_current (itr, (void**)&c);
+ mu_config_clone_container (c);
+ if (mu_refcount_value (c->refcount) > 1)
+ dup_container (&c);
+ switch (c->type)
+ {
+ case mu_cfg_cont_section:
+ c->v.section.offset += param->offset;
+ break;
+
+ case mu_cfg_cont_param:
+ container->v.param.offset += param->offset;
+ break;
+ }
+ mu_cfg_section_add_container (sect, c);
+ }
+ mu_iterator_destroy (&itr);
+ continue;
+ }
+ else
+ {
+ mu_config_clone_container (container);
+ if (mu_refcount_value (container->refcount) > 1)
+ dup_container (&container);
+ container->v.section.target = param->data;
+ container->v.section.offset = param->offset;
+ }
+ }
+ else
+ {
+ rc = mu_config_create_container (&container, mu_cfg_cont_param);
+ if (rc)
+ return rc;
+ container->v.param = *param;
+ }
+ mu_cfg_section_add_container (sect, container);
+ }
+ return 0;
+}
+
+static int
+_clone_action (void *item, void *cbdata)
+{
+ struct mu_cfg_cont *cont = item;
+ return mu_config_clone_container (cont);
+}
+
+int
+mu_config_clone_container (struct mu_cfg_cont *cont)
+{
+ if (!cont)
+ return 0;
+ mu_refcount_inc (cont->refcount);
+ /* printf("clone %p-%s: %d\n", cont, cont->v.section.ident, n); */
+ switch (cont->type)
+ {
+ case mu_cfg_cont_section:
+ mu_list_do (cont->v.section.children, _clone_action, NULL);
+ break;
+
+ case mu_cfg_cont_param:
+ break;
+ }
+ return 0;
+}
+
+
+int
+_mu_config_register_section (struct mu_cfg_cont **proot,
+ const char *parent_path,
+ const char *ident,
+ const char *label,
+ mu_cfg_section_fp parser,
+ struct mu_cfg_param *param,
+ struct mu_cfg_section **psection)
+{
+ int rc;
+ struct mu_cfg_section *root_section;
+ struct mu_cfg_section *parent;
+
+ if (!*proot)
+ {
+ rc = mu_config_create_container (proot, mu_cfg_cont_section);
+ if (rc)
+ return rc;
+ memset (&(*proot)->v.section, 0, sizeof (*proot)->v.section);
+ }
+
+ root_section = &(*proot)->v.section;
+
+ if (parent_path)
+ {
+ if (mu_cfg_find_section (root_section, parent_path, &parent))
+ return MU_ERR_NOENT;
+ }
+ else
+ parent = root_section;
+
+ if (mu_refcount_value ((*proot)->refcount) > 1)
+ {
+ /* It is a clone, do copy-on-write */
+ rc = dup_container (proot);
+ if (rc)
+ return rc;
+
+ root_section = &(*proot)->v.section;
+
+ if (parent_path)
+ {
+ if (mu_cfg_find_section (root_section, parent_path, &parent))
+ return MU_ERR_NOENT;
+ }
+ else
+ parent = root_section;
+ }
+
+ if (ident)
+ {
+ struct mu_cfg_cont *container;
+ struct mu_cfg_section *s;
+
+ if (!parent->children)
+ mu_list_create (&parent->children);
+ mu_config_create_container (&container, mu_cfg_cont_section);
+ mu_list_append (parent->children, container);
+ s = &container->v.section;
+
+ s->ident = strdup (ident);
+ s->label = label ? strdup (label) : NULL;
+ s->parser = parser;
+ s->children = NULL;
+ mu_cfg_section_add_params (s, param);
+ if (psection)
+ *psection = s;
+ }
+ else
+ {
+ mu_cfg_section_add_params (parent, param);
+ /* FIXME: */
+ if (!parent->parser)
+ parent->parser = parser;
+ if (psection)
+ *psection = parent;
+ }
+ return 0;
+}
+
+int
+mu_config_register_section (const char *parent_path,
+ const char *ident,
+ const char *label,
+ mu_cfg_section_fp parser,
+ struct mu_cfg_param *param)
+{
+ return _mu_config_register_section (&root_container,
+ parent_path,
+ ident, label,
+ parser, param, NULL);
+}
+
+int
+mu_config_register_plain_section (const char *parent_path, const char *ident,
+ struct mu_cfg_param *params)
+{
+ return mu_config_register_section (parent_path, ident, NULL, NULL, params);
+}
+
+static int
+prog_parser (enum mu_cfg_section_stage stage,
+ const mu_cfg_node_t *node,
+ const char *label, void **section_data,
+ void *call_data,
+ mu_cfg_tree_t *tree)
+{
+ if (stage == mu_cfg_section_start)
+ {
+ return node->label->type == MU_CFG_STRING
+ && strcmp (node->label->v.string, label);
+ }
+
+ return 0;
+}
+
+struct include_data
+{
+ const char *progname;
+ struct mu_cfg_param *progparam;
+ int flags;
+ void *target;
+};
+
+static int
+_cb_include (mu_debug_t debug, void *data, mu_config_value_t *val)
+{
+ int ret = 0;
+ struct stat sb;
+ const char *dirname;
+ struct include_data *idp = data;
+ char *tmp = NULL;
+
+ if (mu_cfg_assert_value_type (val, MU_CFG_STRING, debug))
+ return 1;
+
+ dirname = val->v.string;
+ if (dirname[0] != '/')
+ {
+ dirname = tmp = mu_make_file_name (SYSCONFDIR, dirname);
+ if (!dirname)
+ {
+ mu_error ("%s", mu_strerror (errno));
+ return 1;
+ }
+ }
+
+ if (stat (dirname, &sb) == 0)
+ {
+ if (S_ISDIR (sb.st_mode))
+ {
+ char *file = mu_make_file_name (dirname, idp->progname);
+ ret = mu_get_config (file, idp->progname, idp->progparam,
+ idp->flags & ~MU_PARSE_CONFIG_GLOBAL,
+ idp->target);
+ }
+ else
+ ret = mu_get_config (dirname, idp->progname, idp->progparam,
+ idp->flags, idp->target);
+ }
+ else if (errno == ENOENT)
+ {
+ mu_cfg_format_error (debug, MU_DEBUG_ERROR,
+ _("include file or directory does not exist"));
+ ret = 1;
+ }
+ else
+ {
+ mu_cfg_format_error (debug, MU_DEBUG_ERROR,
+ _("cannot stat include file or directory: %s"),
+ mu_strerror (errno));
+ ret = 1;
+ }
+ free (tmp);
+ return ret;
+}
+
+struct mu_cfg_cont *
+mu_build_container (const char *progname, struct include_data *idp)
+{
+ struct mu_cfg_cont *cont = root_container;
+
+ mu_config_clone_container (cont);
+
+ if (idp->flags & MU_PARSE_CONFIG_PLAIN)
+ {
+ struct mu_cfg_param mu_include_param[] = {
+ { "include", mu_cfg_callback, NULL, 0, _cb_include,
+ N_("Include contents of the given file. If a directory is given, "
+ "include contents of the file <file>/<program>, where "
+ "<program> is the name of the program. This latter form is "
+ "allowed only in the site-wide configuration file."),
+ N_("file-or-directory") },
+ { NULL }
+ };
+
+ mu_include_param[0].data = idp;
+ _mu_config_register_section (&cont, NULL, NULL, NULL,
+ (void*) progname, mu_include_param, NULL);
+
+ if (idp->flags & MU_PARSE_CONFIG_GLOBAL)
+ {
+ mu_iterator_t iter;
+ struct mu_cfg_section *prog_sect;
+ struct mu_cfg_cont *old_root = root_container;
+ static struct mu_cfg_param empty_param = { NULL };
+
+ _mu_config_register_section (&cont, NULL, "program", progname,
+ prog_parser,
+ idp->progparam ?
+ idp->progparam : &empty_param,
+ &prog_sect);
+
+ if (old_root->v.section.children)
+ {
+ if (!prog_sect->children)
+ mu_list_create (&prog_sect->children);
+ mu_list_get_iterator (old_root->v.section.children, &iter);
+ for (mu_iterator_first (iter); !mu_iterator_is_done (iter);
+ mu_iterator_next (iter))
+ {
+ struct mu_cfg_cont *c;
+ mu_iterator_current (iter, (void**)&c);
+ mu_list_append (prog_sect->children, c);
+ }
+ mu_iterator_destroy (&iter);
+ }
+ }
+ else if (idp->progparam)
+ _mu_config_register_section (&cont, NULL, NULL, NULL, NULL,
+ idp->progparam, NULL);
+ }
+ else if (idp->progparam)
+ _mu_config_register_section (&cont, NULL, NULL, NULL, NULL,
+ idp->progparam, NULL);
+
+ return cont;
+}
+
+int
+mu_cfg_tree_reduce (mu_cfg_tree_t *parse_tree, const char *progname,
+ struct mu_cfg_param *progparam, int flags,
+ void *target_ptr)
+{
+ int rc = 0;
+
+ if (!parse_tree)
+ return 0;
+ if (flags & MU_PARSE_CONFIG_DUMP)
+ {
+ mu_stream_t stream;
+ mu_stdio_stream_create (&stream, MU_STDERR_FD, 0);
+ mu_stream_open (stream);
+ mu_cfg_format_parse_tree (stream, parse_tree, MU_CFG_FMT_LOCUS);
+ mu_stream_destroy (&stream);
+ }
+
+ if (root_container)
+ {
+ struct include_data idata;
+ struct mu_cfg_cont *cont;
+
+ idata.progname = progname;
+ idata.progparam = progparam;
+ idata.flags = flags;
+ idata.target = target_ptr;
+
+ cont = mu_build_container (progname, &idata);
+
+ rc = mu_cfg_scan_tree (parse_tree, &cont->v.section, target_ptr,
+ (void*) progname);
+ mu_config_destroy_container (&cont);
+ }
+
+ return rc;
+}
+
+void
+mu_format_config_tree (mu_stream_t stream, const char *progname,
+ struct mu_cfg_param *progparam, int flags)
+{
+ struct include_data idata;
+ struct mu_cfg_cont *cont;
+
+ idata.progname = progname;
+ idata.progparam = progparam;
+ idata.flags = flags;
+ idata.target = NULL;
+ cont = mu_build_container (progname, &idata);
+ mu_cfg_format_container (stream, cont);
+ mu_config_destroy_container (&cont);
+}
+
+int
+mu_parse_config (const char *file, const char *progname,
+ struct mu_cfg_param *progparam, int flags,
+ void *target_ptr)
+{
+ int rc;
+ char *full_name = mu_tilde_expansion (file, "/", NULL);
+ if (full_name)
+ {
+ if (access (full_name, R_OK) == 0)
+ {
+ rc = mu_get_config (full_name, progname, progparam, flags,
+ target_ptr);
+ }
+ else
+ rc = ENOENT;
+ free (full_name);
+ }
+ else
+ rc = ENOMEM;
+ return rc;
+}
+
+static const char *
+_first_value_ptr (mu_config_value_t *val)
+{
+ switch (val->type)
+ {
+ case MU_CFG_STRING:
+ return val->v.string;
+
+ case MU_CFG_ARRAY:
+ return _first_value_ptr (val->v.arg.v);
+
+ case MU_CFG_LIST:
+ mu_list_get (val->v.list, 0, (void**) &val);
+ return _first_value_ptr (val);
+ }
+ return "";
+}
+
+int
+mu_cfg_assert_value_type (mu_config_value_t *val, int type, mu_debug_t debug)
+{
+ if (!val)
+ {
+ mu_cfg_format_error (debug, MU_DEBUG_ERROR,
+ _("required argument missing"));
+ return 1;
+ }
+
+ if (type == MU_CFG_ARRAY)
+ {
+ if (val->type == MU_CFG_STRING)
+ {
+ mu_config_value_t *arr = mu_calloc (1, sizeof arr[0]);
+ arr[0] = *val;
+ val->v.arg.c = 1;
+ val->v.arg.v = arr;
+ val->type = MU_CFG_ARRAY;
+ }
+ }
+
+ if (val->type != type)
+ {
+ /* FIXME */
+ mu_cfg_format_error (debug, MU_DEBUG_ERROR,
+ _("unexpected value: %s"),
+ _first_value_ptr (val));
+ return 1;
+ }
+ return 0;
+}
+
+int
+mu_cfg_string_value_cb (mu_debug_t debug, mu_config_value_t *val,
+ int (*fun) (mu_debug_t, const char *, void *),
+ void *data)
+{
+ int rc = 0;
+
+ switch (val->type)
+ {
+ case MU_CFG_STRING:
+ return fun (debug, val->v.string, data);
+ break;
+
+ case MU_CFG_ARRAY:
+ {
+ int i;
+
+ for (i = 0; i < val->v.arg.c; i++)
+ {
+ if (mu_cfg_assert_value_type (&val->v.arg.v[i],
+ MU_CFG_STRING, debug))
+ return 1;
+ fun (debug, val->v.arg.v[i].v.string, data);
+ }
+ }
+ break;
+
+ case MU_CFG_LIST:
+ {
+ mu_iterator_t itr;
+ mu_list_get_iterator (val->v.list, &itr);
+ for (mu_iterator_first (itr);
+ !mu_iterator_is_done (itr); mu_iterator_next (itr))
+ {
+ mu_config_value_t *pval;
+ mu_iterator_current (itr, (void*) &pval);
+ if (mu_cfg_assert_value_type (pval, MU_CFG_STRING, debug))
+ {
+ rc = 1;
+ break;
+ }
+ fun (debug, pval->v.string, data);
+ }
+ mu_iterator_destroy (&itr);
+ }
+ }
+ return rc;
+}
diff --git a/libmailutils/cfg_format.c b/libmailutils/cfg_format.c
new file mode 100644
index 000000000..be37bae3c
--- /dev/null
+++ b/libmailutils/cfg_format.c
@@ -0,0 +1,395 @@
+/* cfg_print.c -- convert configuration parse tree to human-readable format.
+ Copyright (C) 2007, 2008, 2009, 2010 Free Software Foundation, Inc.
+
+ GNU Mailutils 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 3, 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/>.
+*/
+
+#ifdef HAVE_CONFIG_H
+# include <config.h>
+#endif
+#include <mailutils/alloc.h>
+#include <mailutils/stream.h>
+#include <mailutils/error.h>
+#include <mailutils/cfg.h>
+#include <mailutils/argcv.h>
+#include <mailutils/nls.h>
+#include <mailutils/iterator.h>
+#include <ctype.h>
+
+struct tree_print
+{
+ int flags;
+ unsigned level;
+ mu_stream_t stream;
+ char *buf;
+ size_t bufsize;
+};
+
+static void
+format_level (mu_stream_t stream, int level)
+{
+ while (level--)
+ mu_stream_write (stream, " ", 2, NULL);
+}
+
+static void
+format_string_value (struct tree_print *tp, const char *str)
+{
+ size_t size;
+ int quote;
+ char *p;
+
+ size = mu_argcv_quoted_length (str, &quote);
+ if (quote)
+ size += 2;
+ size++;
+ if (size > tp->bufsize)
+ {
+ p = mu_realloc (tp->buf, size);
+ tp->bufsize = size;
+ tp->buf = p;
+ }
+
+ p = tp->buf;
+ if (quote)
+ {
+ tp->buf[0] = '"';
+ tp->buf[size-2] = '"';
+ p++;
+ }
+ tp->buf[size-1] = 0;
+ mu_argcv_quote_copy (p, str);
+ mu_stream_write (tp->stream, tp->buf, size - 1, NULL);
+}
+
+static void format_value (struct tree_print *tp, mu_config_value_t *val);
+
+static void
+format_list_value (struct tree_print *tp, mu_config_value_t *val)
+{
+ int i;
+ mu_iterator_t itr;
+ mu_stream_write (tp->stream, "(", 1, NULL);
+ mu_list_get_iterator (val->v.list, &itr);
+
+ for (mu_iterator_first (itr), i = 0;
+ !mu_iterator_is_done (itr); mu_iterator_next (itr), i++)
+ {
+ mu_config_value_t *p;
+ mu_iterator_current (itr, (void**)&p);
+ if (i)
+ mu_stream_write (tp->stream, ", ", 2, NULL);
+ format_value (tp, p);
+ }
+ mu_iterator_destroy (&itr);
+ mu_stream_write (tp->stream, ")", 1, NULL);
+}
+
+static void
+format_array_value (struct tree_print *tp, mu_config_value_t *val)
+{
+ int i;
+
+ for (i = 0; i < val->v.arg.c; i++)
+ {
+ if (i)
+ mu_stream_write (tp->stream, " ", 1, NULL);
+ format_value (tp, &val->v.arg.v[i]);
+ }
+}
+
+static void
+format_value (struct tree_print *tp, mu_config_value_t *val)
+{
+ switch (val->type)
+ {
+ case MU_CFG_STRING:
+ format_string_value (tp, val->v.string);
+ break;
+
+ case MU_CFG_LIST:
+ format_list_value (tp, val);
+ break;
+
+ case MU_CFG_ARRAY:
+ format_array_value (tp, val);
+ }
+}
+
+static int
+format_node (const mu_cfg_node_t *node, void *data)
+{
+ struct tree_print *tp = data;
+
+ if ((tp->flags & MU_CFG_FMT_LOCUS) && node->locus.file)
+ mu_stream_printf (tp->stream, "# %lu \"%s\"\n",
+ (unsigned long) node->locus.line,
+ node->locus.file);
+ format_level (tp->stream, tp->level);
+ switch (node->type)
+ {
+ case mu_cfg_node_undefined:
+ mu_stream_printf (tp->stream, "%s",
+ _("ERROR: undefined statement"));
+ break;
+
+ case mu_cfg_node_statement:
+ {
+ mu_stream_write (tp->stream, node->tag, strlen (node->tag), NULL);
+ if (node->label)
+ {
+ mu_stream_write (tp->stream, " ", 1, NULL);
+ format_value (tp, node->label);
+ }
+ mu_stream_write (tp->stream, " {", 2, NULL);
+ tp->level++;
+ }
+ break;
+
+ case mu_cfg_node_param:
+ mu_stream_write (tp->stream, node->tag, strlen (node->tag), NULL);
+ if (node->label)
+ {
+ mu_stream_write (tp->stream, " ", 1, NULL);
+ format_value (tp, node->label);
+ mu_stream_write (tp->stream, ";", 1, NULL);
+ }
+ break;
+ }
+ mu_stream_write (tp->stream, "\n", 1, NULL);
+ return MU_CFG_ITER_OK;
+}
+
+static int
+format_node_end (const mu_cfg_node_t *node, void *data)
+{
+ struct tree_print *tp = data;
+ tp->level--;
+ format_level (tp->stream, tp->level);
+ mu_stream_write (tp->stream, "};\n", 3, NULL);
+ return MU_CFG_ITER_OK;
+}
+
+void
+mu_cfg_format_parse_tree (mu_stream_t stream, mu_cfg_tree_t *tree, int flags)
+{
+ struct mu_cfg_iter_closure clos;
+ struct tree_print t;
+
+ t.flags = flags;
+ t.level = 0;
+ t.stream = stream;
+ t.buf = NULL;
+ t.bufsize = 0;
+ clos.beg = format_node;
+ clos.end = format_node_end;
+ clos.data = &t;
+ mu_cfg_preorder (tree->nodes, &clos);
+ free (t.buf);
+}
+
+void
+mu_cfg_format_node (mu_stream_t stream, const mu_cfg_node_t *node, int flags)
+{
+ struct tree_print t;
+
+ t.flags = flags;
+ t.level = 0;
+ t.stream = stream;
+ t.buf = NULL;
+ t.bufsize = 0;
+ format_node (node, &t);
+ if (node->type == mu_cfg_node_statement)
+ {
+ struct mu_cfg_iter_closure clos;
+ clos.beg = format_node;
+ clos.end = format_node_end;
+ clos.data = &t;
+ mu_cfg_preorder (node->nodes, &clos);
+ format_node_end (node, &t);
+ }
+}
+
+
+
+const char *
+mu_cfg_data_type_string (enum mu_cfg_param_data_type type)
+{
+ switch (type)
+ {
+ case mu_cfg_string:
+ return N_("string");
+ case mu_cfg_short:
+ case mu_cfg_ushort:
+ case mu_cfg_int:
+ case mu_cfg_uint:
+ case mu_cfg_long:
+ case mu_cfg_ulong:
+ case mu_cfg_size:
+ case mu_cfg_off:
+ return N_("number");
+ case mu_cfg_time:
+ return N_("time");
+ case mu_cfg_bool:
+ return N_("boolean");
+ case mu_cfg_ipv4:
+ return N_("ipv4");
+ case mu_cfg_cidr:
+ return N_("cidr");
+ case mu_cfg_host:
+ return N_("host");
+ case mu_cfg_callback:
+ return N_("string");
+ case mu_cfg_section:
+ return N_("section");
+ }
+ return N_("unknown");
+}
+
+void
+mu_cfg_format_docstring (mu_stream_t stream, const char *docstring, int level)
+{
+ size_t len = strlen (docstring);
+ int width = 78 - level * 2;
+
+ if (width < 0)
+ {
+ width = 78;
+ level = 0;
+ }
+
+ while (len)
+ {
+ size_t seglen;
+ const char *p;
+
+ for (seglen = 0, p = docstring; p < docstring + width && *p; p++)
+ {
+ if (*p == '\n')
+ {
+ seglen = p - docstring;
+ break;
+ }
+ if (isspace (*p))
+ seglen = p - docstring;
+ }
+ if (seglen == 0 || *p == 0)
+ seglen = p - docstring;
+
+ format_level (stream, level);
+ mu_stream_write (stream, "# ", 2, NULL);
+ mu_stream_write (stream, docstring, seglen, NULL);
+ mu_stream_write (stream, "\n", 1, NULL);
+ len -= seglen;
+ docstring += seglen;
+ if (*docstring == '\n')
+ {
+ docstring++;
+ len--;
+ }
+ else
+ while (*docstring && isspace (*docstring))
+ {
+ docstring++;
+ len--;
+ }
+ }
+}
+
+static void
+format_param (mu_stream_t stream, struct mu_cfg_param *param, int level)
+{
+ if (param->docstring)
+ mu_cfg_format_docstring (stream, gettext (param->docstring), level);
+ format_level (stream, level);
+ if (param->argname && strchr (param->argname, ':'))
+ mu_stream_printf (stream, "%s <%s>;\n",
+ param->ident,
+ gettext (param->argname));
+ else if (MU_CFG_IS_LIST (param->type))
+ mu_stream_printf (stream, "%s <%s: list of %s>;\n",
+ param->ident,
+ gettext (param->argname ?
+ param->argname : N_("arg")),
+ gettext (mu_cfg_data_type_string (MU_CFG_TYPE (param->type))));
+ else
+ mu_stream_printf (stream, "%s <%s: %s>;\n",
+ param->ident,
+ gettext (param->argname ?
+ param->argname : N_("arg")),
+ gettext (mu_cfg_data_type_string (param->type)));
+}
+
+static void format_container (mu_stream_t stream, struct mu_cfg_cont *cont,
+ int level);
+
+static int
+_f_helper (void *item, void *data)
+{
+ struct tree_print *tp = data;
+ struct mu_cfg_cont *cont = item;
+ format_container (tp->stream, cont, tp->level);
+ return 0;
+}
+
+static void
+format_section (mu_stream_t stream, struct mu_cfg_section *sect, int level)
+{
+ struct tree_print c;
+ if (sect->docstring)
+ mu_cfg_format_docstring (stream, gettext (sect->docstring), level);
+ format_level (stream, level);
+ if (sect->ident)
+ {
+ mu_stream_write (stream, sect->ident, strlen (sect->ident), NULL);
+ if (sect->label)
+ {
+ mu_stream_write (stream, " ", 1, NULL);
+ mu_stream_write (stream, sect->label, strlen (sect->label), NULL);
+ }
+ mu_stream_write (stream, " {\n", 3, NULL);
+ c.stream = stream;
+ c.level = level + 1;
+ mu_list_do (sect->children, _f_helper, &c);
+ format_level (stream, level);
+ mu_stream_write (stream, "};\n\n", 4, NULL);
+ }
+ else
+ {
+ c.stream = stream;
+ c.level = level;
+ mu_list_do (sect->children, _f_helper, &c);
+ }
+}
+
+static void
+format_container (mu_stream_t stream, struct mu_cfg_cont *cont, int level)
+{
+ switch (cont->type)
+ {
+ case mu_cfg_cont_section:
+ format_section (stream, &cont->v.section, level);
+ break;
+
+ case mu_cfg_cont_param:
+ format_param (stream, &cont->v.param, level);
+ break;
+ }
+}
+
+void
+mu_cfg_format_container (mu_stream_t stream, struct mu_cfg_cont *cont)
+{
+ format_container (stream, cont, 0);
+}
diff --git a/libmailutils/cfg_lexer.l b/libmailutils/cfg_lexer.l
new file mode 100644
index 000000000..998d84d9b
--- /dev/null
+++ b/libmailutils/cfg_lexer.l
@@ -0,0 +1,405 @@
+%top {
+/* cfg_lexer.l -- default lexer for Mailutils configuration files
+ Copyright (C) 2007, 2008, 2009, 2010 Free Software Foundation, Inc.
+
+ GNU Mailutils 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 3, 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/>.
+*/
+#ifdef HAVE_CONFIG_H
+# include <config.h>
+#endif
+}
+
+%{
+#include <stdio.h>
+#include <stdlib.h>
+#include <sys/types.h>
+#include <sys/stat.h>
+#include <fcntl.h>
+#include <errno.h>
+#include <unistd.h>
+#include <mailutils/cctype.h>
+#include <mailutils/errno.h>
+#include <mailutils/error.h>
+#include <mailutils/debug.h>
+#include <mailutils/argcv.h>
+#include <mailutils/alloc.h>
+#include <mailutils/nls.h>
+#include <mailutils/cfg.h>
+#include <mailutils/list.h>
+#include <mailutils/mutil.h>
+
+#include "cfg_parser.h"
+
+void _mu_line_begin (void);
+void _mu_line_add (char *text, size_t len);
+char *_mu_line_finish (void);
+
+extern void mu_cfg_set_debug (void);
+static void
+mu_cfg_set_lex_debug (void)
+{
+ yy_flex_debug = mu_debug_check_level (mu_cfg_get_debug (),
+ MU_DEBUG_TRACE2);
+}
+
+static void _mu_line_add_unescape_last (char *text, size_t len);
+static void multiline_begin (char *p);
+static char *multiline_strip_tabs (char *text);
+static void multiline_add (char *s);
+static char *multiline_finish (void);
+
+static char *multiline_delimiter;
+static size_t multiline_delimiter_len;
+static int multiline_unescape; /* Unescape here-document contents */
+static int (*char_to_strip)(char); /* Strip matching characters of each
+ here-document line */
+static int isemptystr(int off);
+
+static mu_opool_t pool;
+
+%}
+
+%x COMMENT ML STR
+
+WS [ \t\f][ \t\f]*
+ID [a-zA-Z_][a-zA-Z_0-9-]+
+P [1-9][0-9]*
+
+%%
+ /* C-style comments */
+"/*" BEGIN(COMMENT);
+<COMMENT>[^*\n]* /* eat anything that's not a '*' */
+<COMMENT>"*"+[^*/\n]* /* eat up '*'s not followed by '/'s */
+<COMMENT>\n ++mu_cfg_locus.line;
+<COMMENT>"*"+"/" BEGIN (INITIAL);
+ /* End-of-line comments */
+#debug=.*\n {
+ mu_log_level_t lev;
+ mu_debug_t dbg = mu_cfg_get_debug ();
+ if (mu_debug_level_from_string (yytext + 7, &lev, dbg) == 0)
+ {
+ mu_debug_set_level (dbg, lev);
+ mu_cfg_set_debug ();
+ mu_cfg_set_lex_debug ();
+ }
+ }
+#.*\n { mu_cfg_locus.line++; }
+#.* /* end-of-file comment */;
+"//".*\n { mu_cfg_locus.line++; }
+"//".* /* end-of-file comment */;
+ /* Identifiers */
+<INITIAL>{ID} {
+ _mu_line_begin ();
+ _mu_line_add (yytext, yyleng);
+ yylval.string = _mu_line_finish ();
+ return MU_TOK_IDENT; }
+ /* Strings */
+[a-zA-Z0-9_\./:\*=-]+ { _mu_line_begin ();
+ _mu_line_add (yytext, yyleng);
+ yylval.string = _mu_line_finish ();
+ return MU_TOK_STRING; }
+ /* Quoted strings */
+\"[^\\"\n]*\" { _mu_line_begin ();
+ _mu_line_add (yytext + 1, yyleng - 2);
+ yylval.string = _mu_line_finish ();
+ return MU_TOK_QSTRING; }
+\"[^\\"\n]*\\. |
+\"[^\\"\n]*\\\n { BEGIN (STR);
+ _mu_line_begin ();
+ _mu_line_add_unescape_last (yytext + 1, yyleng - 1); }
+<STR>[^\\"\n]*\\. |
+<STR>\"[^\\"\n]*\\\n { _mu_line_add_unescape_last (yytext, yyleng); }
+<STR>[^\\"\n]*\" { BEGIN (INITIAL);
+ if (yyleng > 1)
+ _mu_line_add (yytext, yyleng - 1);
+ yylval.string = _mu_line_finish ();
+ return MU_TOK_QSTRING; }
+ /* Multiline strings */
+"<<"(-" "?)?\\?{ID}[ \t]*#.*\n |
+"<<"(-" "?)?\\?{ID}[ \t]*"//".*\n |
+"<<"(-" "?)?\\?{ID}[ \t]*\n |
+"<<"(-" "?)?\"{ID}\"[ \t]*#.*\n |
+"<<"(-" "?)?\"{ID}\"[ \t]*"//".*\n |
+"<<"(-" "?)?\"{ID}\"[ \t]*\n {
+ BEGIN (ML);
+ multiline_begin (yytext+2);
+ mu_cfg_locus.line++;
+ }
+<ML>.*\n { char *p = multiline_strip_tabs (yytext);
+
+ if (!strncmp (p, multiline_delimiter, multiline_delimiter_len)
+ && isemptystr (p + multiline_delimiter_len - yytext))
+ {
+ free (multiline_delimiter);
+ multiline_delimiter = NULL;
+ BEGIN (INITIAL);
+ yylval.string = multiline_finish ();
+ return MU_TOK_MSTRING;
+ }
+ mu_cfg_locus.line++;
+ multiline_add (p); }
+{WS} ;
+ /* Other tokens */
+\n { mu_cfg_locus.line++; }
+[,;{}()] return yytext[0];
+. { if (mu_isprint (yytext[0]))
+ mu_cfg_parse_error (_("stray character %c"), yytext[0]);
+ else
+ mu_cfg_parse_error (_("stray character \\%03o"),
+ (unsigned char) yytext[0]);
+ }
+%%
+
+int
+yywrap ()
+{
+ return 1;
+}
+
+static void
+unescape_to_line (int c)
+{
+ if (c != '\n')
+ {
+ char t = mu_argcv_unquote_char (c);
+ if (t == c && t != '\\' && t != '\"')
+ mu_cfg_parse_error (_("unknown escape sequence '\\%c'"), c);
+ mu_opool_append_char (pool, t);
+ }
+}
+
+void
+_mu_line_add (char *text, size_t len)
+{
+ mu_opool_append (pool, text, len);
+}
+
+void
+_mu_line_add_unescape_last (char *text, size_t len)
+{
+ mu_opool_append (pool, text, len - 2);
+ unescape_to_line (text[len - 1]);
+}
+
+void
+_mu_line_begin ()
+{
+ if (!pool)
+ mu_opool_create (&pool, 1);
+ else
+ mu_opool_clear (pool);
+}
+
+char *
+_mu_line_finish ()
+{
+ mu_opool_append_char (pool, 0);
+ return mu_opool_finish (pool, NULL);
+}
+
+
+
+static int
+is_tab (char c)
+{
+ return c == '\t';
+}
+
+static int
+is_ws (char c)
+{
+ return c == '\t' || c == ' ';
+}
+
+static int
+isemptystr (int off)
+{
+ for (; yytext[off] && mu_isspace (yytext[off]); off++)
+ ;
+ if (yytext[off] == ';')
+ {
+ int i;
+ for (i = off + 1; yytext[i]; i++)
+ if (!mu_isspace (yytext[i]))
+ return 0;
+ yyless (off);
+ return 1;
+ }
+ return yytext[off] == 0;
+}
+
+static void
+multiline_begin (char *p)
+{
+ if (*p == '-')
+ {
+ if (*++p == ' ')
+ {
+ char_to_strip = is_ws;
+ p++;
+ }
+ else
+ char_to_strip = is_tab;
+ }
+ else
+ char_to_strip = NULL;
+ if (*p == '\\')
+ {
+ p++;
+ multiline_unescape = 0;
+ }
+ else if (*p == '"')
+ {
+ char *q;
+
+ p++;
+ multiline_unescape = 0;
+ q = strchr (p, '"');
+ multiline_delimiter_len = q - p;
+ }
+ else
+ {
+ multiline_delimiter_len = strcspn (p, " \t");
+ multiline_unescape = 1;
+ }
+
+ /* Remove trailing newline */
+ multiline_delimiter_len--;
+ multiline_delimiter = mu_alloc (multiline_delimiter_len + 1);
+ memcpy (multiline_delimiter, p, multiline_delimiter_len);
+ multiline_delimiter[multiline_delimiter_len] = 0;
+ _mu_line_begin ();
+}
+
+static char *
+multiline_strip_tabs (char *text)
+{
+ if (char_to_strip)
+ for (; *text && char_to_strip (*text); text++)
+ ;
+ return text;
+}
+
+static void
+multiline_add (char *s)
+{
+ if (multiline_unescape)
+ {
+ for (; *s; s++)
+ {
+ if (*s == '\\')
+ {
+ unescape_to_line (s[1]);
+ ++s;
+ }
+ else
+ _mu_line_add (s, 1);
+ }
+ }
+ else
+ _mu_line_add (s, strlen (s));
+}
+
+static char *
+multiline_finish ()
+{
+ return _mu_line_finish ();
+}
+
+
+int
+mu_cfg_parse_file (mu_cfg_tree_t **return_tree, const char *file, int flags)
+{
+ struct stat st;
+ FILE *fp;
+ int rc;
+ char *full_name = mu_tilde_expansion (file, "/", NULL);
+
+ if (stat (full_name, &st))
+ {
+ if (errno != ENOENT)
+ mu_error (_("cannot stat `%s': %s"), full_name, mu_strerror (errno));
+ free (full_name);
+ return ENOENT;
+ }
+ else if (!S_ISREG (st.st_mode))
+ {
+ if (flags & MU_PARSE_CONFIG_VERBOSE)
+ mu_diag_output (MU_DIAG_INFO, _("%s: not a regular file"), full_name);
+ free (full_name);
+ return ENOENT;
+ }
+
+ fp = fopen (full_name, "r");
+ if (!fp)
+ {
+ mu_error (_("cannot open config file `%s': %s"), full_name,
+ mu_strerror (errno));
+ free (full_name);
+ return errno;
+ }
+
+ if (flags & MU_PARSE_CONFIG_VERBOSE)
+ mu_diag_output (MU_DIAG_INFO, _("parsing file `%s'"), full_name);
+
+ mu_cfg_set_lex_debug ();
+
+ /* Initialize locus: */
+ /* 1. Save file name in the lexer object pool and point `file' member
+ to this copy. Free full_name: it is not used after that. */
+ _mu_line_begin ();
+ _mu_line_add (full_name, strlen (full_name));
+ mu_cfg_locus.file = _mu_line_finish ();
+ free (full_name);
+ /* 2. Initialize line number */
+ mu_cfg_locus.line = 1;
+
+ /* Parse configuration */
+ yyrestart (fp);
+ rc = mu_cfg_parse (return_tree);
+ fclose (fp);
+ if (flags & MU_PARSE_CONFIG_VERBOSE)
+ mu_diag_output (MU_DIAG_INFO, _("finished parsing file `%s'"),
+ mu_cfg_locus.file);
+
+ return rc == 0 ? 0 : MU_ERR_FAILURE;
+}
+
+/* FIXME: Deprecated interface */
+int
+mu_get_config (const char *file, const char *progname,
+ struct mu_cfg_param *progparam, int flags, void *target_ptr)
+{
+ mu_cfg_tree_t *parse_tree;
+ int rc = mu_cfg_parse_file (&parse_tree, file, flags);
+ if (rc == 0)
+ {
+ rc = mu_cfg_tree_postprocess (parse_tree, flags);
+ if (rc == 0)
+ rc = mu_cfg_tree_reduce (parse_tree, progname, progparam, flags,
+ target_ptr);
+ mu_cfg_destroy_tree (&parse_tree);
+ }
+
+ return rc == 0 ? 0 : MU_ERR_FAILURE;
+}
+
+
+mu_opool_t
+mu_cfg_lexer_pool ()
+{
+ mu_opool_t p = pool;
+ pool = NULL;
+ return p;
+}
diff --git a/libmailutils/cfg_parser.y b/libmailutils/cfg_parser.y
new file mode 100644
index 000000000..8b13ae10d
--- /dev/null
+++ b/libmailutils/cfg_parser.y
@@ -0,0 +1,1817 @@
+%{
+/* cfg_parser.y -- general-purpose configuration file parser
+ Copyright (C) 2007, 2008, 2009, 2010 Free Software Foundation, Inc.
+
+ GNU Mailutils 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 3, 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/>.
+*/
+#ifdef HAVE_CONFIG_H
+# include <config.h>
+#endif
+#include <sys/types.h>
+#include <sys/stat.h>
+#include <stdio.h>
+#include <stdlib.h>
+#include <stdarg.h>
+#include <string.h>
+#include <netdb.h>
+#include "intprops.h"
+#include <mailutils/argcv.h>
+#include <mailutils/nls.h>
+#include <mailutils/cfg.h>
+#include <mailutils/alloc.h>
+#include <mailutils/errno.h>
+#include <mailutils/error.h>
+#include <mailutils/list.h>
+#include <mailutils/iterator.h>
+#include <mailutils/debug.h>
+#include <mailutils/mutil.h>
+#include <mailutils/cctype.h>
+
+int mu_cfg_parser_verbose;
+static mu_list_t /* of mu_cfg_node_t */ parse_node_list;
+mu_cfg_locus_t mu_cfg_locus;
+size_t mu_cfg_error_count;
+
+static int _mu_cfg_errcnt;
+static mu_debug_t _mu_cfg_debug;
+
+int yylex ();
+
+void _mu_line_begin (void);
+void _mu_line_add (char *text, size_t len);
+char *_mu_line_finish (void);
+
+static int
+yyerror (char *s)
+{
+ mu_cfg_parse_error ("%s", s);
+ return 0;
+}
+
+static mu_config_value_t *
+config_value_dup (mu_config_value_t *src)
+{
+ if (!src)
+ return NULL;
+ else
+ {
+ /* FIXME: Use mu_opool_alloc */
+ mu_config_value_t *val = mu_alloc (sizeof (*val));
+ *val = *src;
+ return val;
+ }
+}
+
+static mu_cfg_node_t *
+mu_cfg_alloc_node (enum mu_cfg_node_type type, mu_cfg_locus_t *loc,
+ const char *tag, mu_config_value_t *label,
+ mu_list_t nodelist)
+{
+ char *p;
+ mu_cfg_node_t *np;
+ size_t size = sizeof *np + strlen (tag) + 1;
+ np = mu_alloc (size);
+ np->type = type;
+ np->locus = *loc;
+ p = (char*) (np + 1);
+ np->tag = p;
+ strcpy (p, tag);
+ np->label = label;
+ np->nodes = nodelist;
+ return np;
+}
+
+void
+mu_cfg_free_node (mu_cfg_node_t *node)
+{
+ free (node->label);
+ free (node);
+}
+
+void
+mu_cfg_format_error (mu_debug_t debug, size_t level, const char *fmt, ...)
+{
+ va_list ap;
+
+ if (!debug)
+ mu_diag_get_debug (&debug);
+ va_start (ap, fmt);
+ mu_debug_vprintf (debug, 0, fmt, ap);
+ mu_debug_printf (debug, 0, "\n");
+ va_end (ap);
+ if (level <= MU_DEBUG_ERROR)
+ mu_cfg_error_count++;
+}
+
+static void
+_mu_cfg_debug_set_locus (mu_debug_t debug, const mu_cfg_locus_t *loc)
+{
+ mu_debug_set_locus (debug, loc->file ? loc->file : _("unknown file"),
+ loc->line);
+}
+
+void
+mu_cfg_vperror (mu_debug_t debug, const mu_cfg_locus_t *loc,
+ const char *fmt, va_list ap)
+{
+ if (!debug)
+ mu_diag_get_debug (&debug);
+ _mu_cfg_debug_set_locus (debug, loc);
+ mu_debug_vprintf (debug, 0, fmt, ap);
+ mu_debug_printf (debug, 0, "\n");
+ mu_debug_set_locus (debug, NULL, 0);
+ mu_cfg_error_count++;
+}
+
+void
+mu_cfg_perror (mu_debug_t debug, const mu_cfg_locus_t *loc,
+ const char *fmt, ...)
+{
+ va_list ap;
+
+ va_start (ap, fmt);
+ mu_cfg_vperror (debug, loc, fmt, ap);
+ va_end (ap);
+}
+
+void
+mu_cfg_parse_error (const char *fmt, ...)
+{
+ va_list ap;
+
+ va_start (ap, fmt);
+ mu_cfg_vperror (_mu_cfg_debug, &mu_cfg_locus, fmt, ap);
+ va_end (ap);
+}
+
+#define node_type_str(t) (((t) == mu_cfg_node_statement) ? "stmt" : "param")
+
+static void
+debug_print_node (mu_cfg_node_t *node)
+{
+ if (mu_debug_check_level (_mu_cfg_debug, MU_DEBUG_TRACE0))
+ {
+ mu_debug_set_locus (_mu_cfg_debug,
+ node->locus.file, node->locus.line);
+ if (node->type == mu_cfg_node_undefined)
+ /* Stay on the safe side */
+ mu_cfg_format_error (_mu_cfg_debug, MU_DEBUG_ERROR,
+ "unknown statement type!");
+ else
+ /* FIXME: How to print label? */
+ mu_cfg_format_error (_mu_cfg_debug, MU_DEBUG_TRACE0,
+ "statement: %s, id: %s",
+ node_type_str (node->type),
+ node->tag ? node->tag : "(null)");
+
+ mu_debug_set_locus (_mu_cfg_debug, NULL, 0);
+ }
+}
+
+static void
+free_node_item (void *item)
+{
+ mu_cfg_node_t *node = item;
+
+ switch (node->type)
+ {
+ case mu_cfg_node_statement:
+ mu_list_destroy (&node->nodes);
+ break;
+
+ case mu_cfg_node_undefined: /* hmm... */
+ case mu_cfg_node_param:
+ break;
+ }
+ mu_cfg_free_node (node);
+}
+
+int
+mu_cfg_create_node_list (mu_list_t *plist)
+{
+ int rc;
+ mu_list_t list;
+
+ rc = mu_list_create (&list);
+ if (rc)
+ return rc;
+ mu_list_set_destroy_item (list, free_node_item);
+ *plist = list;
+ return 0;
+}
+
+%}
+
+%union {
+ mu_cfg_node_t node;
+ mu_cfg_node_t *pnode;
+ mu_list_t /* of mu_cfg_node_t */ nodelist;
+ char *string;
+ mu_config_value_t value, *pvalue;
+ mu_list_t list;
+ struct { const char *name; mu_cfg_locus_t locus; } ident;
+}
+
+%token <string> MU_TOK_IDENT MU_TOK_STRING MU_TOK_QSTRING MU_TOK_MSTRING
+%type <string> string slist
+%type <list> slist0
+%type <value> value
+%type <pvalue> tag vallist
+%type <list> values list vlist
+%type <ident> ident
+%type <nodelist> stmtlist
+%type <pnode> stmt simple block
+
+%%
+
+input : stmtlist
+ {
+ parse_node_list = $1;
+ }
+ ;
+
+stmtlist: stmt
+ {
+ mu_cfg_create_node_list (&$$);
+ mu_list_append ($$, $1);
+ }
+ | stmtlist stmt
+ {
+ mu_list_append ($1, $2);
+ $$ = $1;
+ debug_print_node ($2);
+ }
+ ;
+
+stmt : simple
+ | block
+ ;
+
+simple : ident vallist ';'
+ {
+ $$ = mu_cfg_alloc_node (mu_cfg_node_param, &$1.locus,
+ $1.name, $2,
+ NULL);
+ }
+ ;
+
+block : ident tag '{' '}' opt_sc
+ {
+ $$ = mu_cfg_alloc_node (mu_cfg_node_statement, &$1.locus,
+ $1.name, $2,
+ NULL);
+
+ }
+ | ident tag '{' stmtlist '}' opt_sc
+ {
+ $$ = mu_cfg_alloc_node (mu_cfg_node_statement, &$1.locus,
+ $1.name, $2, $4);
+
+ }
+ ;
+
+ident : MU_TOK_IDENT
+ {
+ $$.name = $1;
+ $$.locus = mu_cfg_locus;
+ }
+ ;
+
+tag : /* empty */
+ {
+ $$ = NULL;
+ }
+ | vallist
+ ;
+
+vallist : vlist
+ {
+ size_t n = 0;
+ mu_list_count($1, &n);
+ if (n == 1)
+ {
+ mu_list_get ($1, 0, (void**) &$$);
+ }
+ else
+ {
+ size_t i;
+ mu_config_value_t val;
+
+ val.type = MU_CFG_ARRAY;
+ val.v.arg.c = n;
+ /* FIXME: Use mu_opool_alloc */
+ val.v.arg.v = mu_alloc (n * sizeof (val.v.arg.v[0]));
+ if (!val.v.arg.v)
+ {
+ mu_cfg_parse_error (_("not enough memory"));
+ abort();
+ }
+
+ for (i = 0; i < n; i++)
+ {
+ mu_config_value_t *v;
+ mu_list_get ($1, i, (void **) &v);
+ val.v.arg.v[i] = *v;
+ }
+ $$ = config_value_dup (&val);
+ }
+ mu_list_destroy (&$1);
+ }
+ ;
+
+vlist : value
+ {
+ int rc = mu_list_create (&$$);
+ if (rc)
+ {
+ mu_cfg_parse_error (_("cannot create list: %s"),
+ mu_strerror (rc));
+ abort ();
+ }
+ mu_list_append ($$, config_value_dup (&$1)); /* FIXME */
+ }
+ | vlist value
+ {
+ mu_list_append ($1, config_value_dup (&$2));
+ }
+ ;
+
+value : string
+ {
+ $$.type = MU_CFG_STRING;
+ $$.v.string = $1;
+ }
+ | list
+ {
+ $$.type = MU_CFG_LIST;
+ $$.v.list = $1;
+ }
+ | MU_TOK_MSTRING
+ {
+ $$.type = MU_CFG_STRING;
+ $$.v.string = $1;
+ }
+ ;
+
+string : MU_TOK_STRING
+ | MU_TOK_IDENT
+ | slist
+ ;
+
+slist : slist0
+ {
+ mu_iterator_t itr;
+ mu_list_get_iterator ($1, &itr);
+
+ _mu_line_begin ();
+ for (mu_iterator_first (itr);
+ !mu_iterator_is_done (itr); mu_iterator_next (itr))
+ {
+ char *p;
+ mu_iterator_current (itr, (void**)&p);
+ _mu_line_add (p, strlen (p));
+ }
+ $$ = _mu_line_finish ();
+ mu_iterator_destroy (&itr);
+ mu_list_destroy(&$1);
+ }
+ ;
+
+slist0 : MU_TOK_QSTRING
+ {
+ mu_list_create (&$$);
+ mu_list_append ($$, $1);
+ }
+ | slist0 MU_TOK_QSTRING
+ {
+ mu_list_append ($1, $2);
+ $$ = $1;
+ }
+ ;
+
+list : '(' values ')'
+ {
+ $$ = $2;
+ }
+ | '(' values ',' ')'
+ {
+ $$ = $2;
+ }
+ ;
+
+values : value
+ {
+ mu_list_create (&$$);
+ mu_list_append ($$, config_value_dup (&$1));
+ }
+ | values ',' value
+ {
+ mu_list_append ($1, config_value_dup (&$3));
+ $$ = $1;
+ }
+ ;
+
+opt_sc : /* empty */
+ | ';'
+ ;
+
+
+%%
+
+static int
+_cfg_default_printer (void *unused, mu_log_level_t level, const char *str)
+{
+ fprintf (stderr, "%s", str);
+ return 0;
+}
+
+mu_debug_t
+mu_cfg_get_debug ()
+{
+ if (!_mu_cfg_debug)
+ {
+ mu_debug_create (&_mu_cfg_debug, NULL);
+ mu_debug_set_print (_mu_cfg_debug, _cfg_default_printer, NULL);
+ mu_debug_set_level (_mu_cfg_debug, mu_global_debug_level ("config"));
+ }
+ return _mu_cfg_debug;
+}
+
+void
+mu_cfg_set_debug ()
+{
+ if (mu_debug_check_level (mu_cfg_get_debug (), MU_DEBUG_TRACE7))
+ yydebug = 1;
+}
+
+int
+mu_cfg_parse (mu_cfg_tree_t **ptree)
+{
+ int rc;
+ mu_cfg_tree_t *tree;
+ mu_opool_t pool;
+
+ mu_cfg_set_debug ();
+ _mu_cfg_errcnt = 0;
+
+ rc = yyparse ();
+ pool = mu_cfg_lexer_pool ();
+ if (rc == 0 && _mu_cfg_errcnt)
+ {
+ mu_opool_destroy (&pool);
+ rc = 1;
+ }
+ else
+ {
+ tree = mu_alloc (sizeof (*tree));
+ tree->debug = _mu_cfg_debug;
+ _mu_cfg_debug = NULL;
+ tree->nodes = parse_node_list;
+ tree->pool = pool;
+ parse_node_list = NULL;
+ *ptree = tree;
+ }
+ return rc;
+}
+
+int
+mu_cfg_tree_union (mu_cfg_tree_t **pa, mu_cfg_tree_t **pb)
+{
+ mu_cfg_tree_t *a, *b;
+ int rc;
+
+ if (!pb)
+ return EINVAL;
+ if (!*pb)
+ return 0;
+ b = *pb;
+ if (!pa)
+ return EINVAL;
+ if (!*pa)
+ {
+ *pa = b;
+ *pb = NULL;
+ return 0;
+ }
+ else
+ a = *pa;
+
+ /* Merge opools */
+ rc = mu_opool_union (&b->pool, &a->pool);
+ if (rc)
+ return rc;
+
+ /* Link node lists */
+ if (b->nodes)
+ {
+ mu_list_append_list (a->nodes, b->nodes);
+ mu_list_destroy (&b->nodes);
+ }
+
+ mu_debug_destroy (&b->debug, mu_debug_get_owner (b->debug));
+ free (b);
+ *pb = NULL;
+ return 0;
+}
+
+static mu_cfg_tree_t *
+do_include (const char *name, int flags, mu_cfg_locus_t *loc)
+{
+ struct stat sb;
+ char *tmpname = NULL;
+ mu_cfg_tree_t *tree = NULL;
+
+ if (name[0] != '/')
+ {
+ name = tmpname = mu_make_file_name (SYSCONFDIR, name);
+ if (!name)
+ {
+ mu_error ("%s", mu_strerror (errno));
+ return NULL;
+ }
+ }
+ if (stat (name, &sb) == 0)
+ {
+ int rc = 0;
+
+ if (S_ISDIR (sb.st_mode))
+ {
+ if (flags & MU_PARSE_CONFIG_GLOBAL)
+ {
+ char *file = mu_make_file_name (name, mu_program_name);
+ rc = mu_cfg_parse_file (&tree, file, flags);
+ free (file);
+ }
+ }
+ else
+ rc = mu_cfg_parse_file (&tree, name, flags);
+
+ if (rc == 0 && tree)
+ mu_cfg_tree_postprocess (tree, flags & ~MU_PARSE_CONFIG_GLOBAL);
+ }
+ else if (errno == ENOENT)
+ mu_cfg_perror (tree->debug, loc,
+ _("include file or directory does not exist"));
+ else
+ mu_cfg_perror (tree->debug, loc,
+ _("cannot stat include file or directory: %s"),
+ mu_strerror (errno));
+ free (tmpname);
+ return tree;
+}
+
+int
+mu_cfg_tree_postprocess (mu_cfg_tree_t *tree, int flags)
+{
+ int rc;
+ mu_iterator_t itr;
+
+ if (!tree->nodes)
+ return 0;
+ rc = mu_list_get_iterator (tree->nodes, &itr);
+ if (rc)
+ return rc;
+ for (mu_iterator_first (itr); !mu_iterator_is_done (itr);
+ mu_iterator_next (itr))
+ {
+ mu_cfg_node_t *node;
+
+ mu_iterator_current (itr, (void**) &node);
+
+ if (node->type == mu_cfg_node_statement)
+ {
+ if ((flags & MU_PARSE_CONFIG_GLOBAL) &&
+ strcmp (node->tag, "program") == 0)
+ {
+ if (node->label->type == MU_CFG_STRING)
+ {
+ if (strcmp (node->label->v.string, mu_program_name) == 0)
+ {
+ /* Move all nodes from this block to the topmost
+ level */
+ mu_iterator_ctl (itr, mu_itrctl_insert_list,
+ node->nodes);
+ mu_iterator_ctl (itr, mu_itrctl_delete, NULL);
+ /*FIXME:mu_cfg_free_node (node);*/
+ }
+ }
+ else
+ {
+ mu_cfg_perror (tree->debug, &node->locus,
+ _("argument to `program' is not a string"));
+ mu_iterator_ctl (itr, mu_itrctl_delete, NULL);
+ }
+ }
+ }
+ else if (node->type == mu_cfg_node_param &&
+ strcmp (node->tag, "include") == 0)
+ {
+ if (node->label->type == MU_CFG_STRING)
+ {
+ mu_cfg_tree_t *t = do_include (node->label->v.string, flags,
+ &node->locus);
+ if (t)
+ {
+ /* Merge the new tree into the current point and
+ destroy the rest of it */
+ mu_iterator_ctl (itr, mu_itrctl_insert_list, t->nodes);
+ mu_opool_union (&tree->pool, &t->pool);
+ mu_cfg_destroy_tree (&t);
+ }
+ }
+ else
+ mu_cfg_perror (tree->debug, &node->locus,
+ _("argument to `include' is not a string"));
+ /* Remove node from the list */
+ mu_iterator_ctl (itr, mu_itrctl_delete, NULL);
+ }
+ }
+ return 0;
+}
+
+static int
+_mu_cfg_preorder_recursive (void *item, void *cbdata)
+{
+ mu_cfg_node_t *node = item;
+ struct mu_cfg_iter_closure *clos = cbdata;
+
+ switch (node->type)
+ {
+ case mu_cfg_node_undefined:
+ abort ();
+
+ case mu_cfg_node_statement:
+ switch (clos->beg (node, clos->data))
+ {
+ case MU_CFG_ITER_OK:
+ if (mu_cfg_preorder (node->nodes, clos))
+ return 1;
+ if (clos->end && clos->end (node, clos->data) == MU_CFG_ITER_STOP)
+ return 1;
+ break;
+
+ case MU_CFG_ITER_SKIP:
+ break;
+
+ case MU_CFG_ITER_STOP:
+ return 1;
+ }
+ break;
+
+ case mu_cfg_node_param:
+ return clos->beg (node, clos->data) == MU_CFG_ITER_STOP;
+ }
+ return 0;
+}
+
+int
+mu_cfg_preorder (mu_list_t nodelist, struct mu_cfg_iter_closure *clos)
+{
+ return mu_list_do (nodelist, _mu_cfg_preorder_recursive, clos);
+}
+
+
+
+void
+mu_cfg_destroy_tree (mu_cfg_tree_t **ptree)
+{
+ if (ptree && *ptree)
+ {
+ mu_cfg_tree_t *tree = *ptree;
+ mu_list_destroy (&tree->nodes);
+ mu_opool_destroy (&tree->pool);
+ *ptree = NULL;
+ }
+}
+
+
+
+struct mu_cfg_section_list
+{
+ struct mu_cfg_section_list *next;
+ struct mu_cfg_section *sec;
+};
+
+struct scan_tree_data
+{
+ struct mu_cfg_section_list *list;
+ void *target;
+ void *call_data;
+ mu_cfg_tree_t *tree;
+ int error;
+};
+
+static struct mu_cfg_cont *
+find_container (mu_list_t list, enum mu_cfg_cont_type type,
+ const char *ident, size_t len)
+{
+ mu_iterator_t iter;
+ struct mu_cfg_cont *ret = NULL;
+
+ if (len == 0)
+ len = strlen (ident);
+
+ mu_list_get_iterator (list, &iter);
+ for (mu_iterator_first (iter); !mu_iterator_is_done (iter);
+ mu_iterator_next (iter))
+ {
+ struct mu_cfg_cont *cont;
+ mu_iterator_current (iter, (void**) &cont);
+
+ if (cont->type == type
+ && strlen (cont->v.ident) == len
+ && memcmp (cont->v.ident, ident, len) == 0)
+ {
+ ret = cont;
+ break;
+ }
+ }
+ mu_iterator_destroy (&iter);
+ return ret;
+}
+
+static struct mu_cfg_section *
+find_subsection (struct mu_cfg_section *sec, const char *ident, size_t len)
+{
+ if (sec)
+ {
+ if (sec->children)
+ {
+ struct mu_cfg_cont *cont = find_container (sec->children,
+ mu_cfg_cont_section,
+ ident, len);
+ if (cont)
+ return &cont->v.section;
+ }
+ }
+ return NULL;
+}
+
+static struct mu_cfg_param *
+find_param (struct mu_cfg_section *sec, const char *ident, size_t len)
+{
+ if (sec)
+ {
+ if (sec->children)
+ {
+ struct mu_cfg_cont *cont = find_container (sec->children,
+ mu_cfg_cont_param,
+ ident, len);
+ if (cont)
+ return &cont->v.param;
+ }
+ }
+ return NULL;
+}
+
+static int
+push_section (struct scan_tree_data *dat, struct mu_cfg_section *sec)
+{
+ struct mu_cfg_section_list *p = mu_alloc (sizeof *p);
+ if (!p)
+ {
+ mu_cfg_perror (dat->tree->debug, NULL, _("not enough memory"));
+ return 1;
+ }
+ p->sec = sec;
+ p->next = dat->list;
+ dat->list = p;
+ return 0;
+}
+
+static struct mu_cfg_section *
+pop_section (struct scan_tree_data *dat)
+{
+ struct mu_cfg_section_list *p = dat->list;
+ struct mu_cfg_section *sec = p->sec;
+ dat->list = p->next;
+ free (p);
+ return sec;
+}
+
+#define STRTONUM(s, type, base, res, limit, d, loc) \
+ { \
+ type sum = 0; \
+ \
+ for (; *s; s++) \
+ { \
+ type x; \
+ \
+ if ('0' <= *s && *s <= '9') \
+ x = sum * base + *s - '0'; \
+ else if (base == 16 && 'a' <= *s && *s <= 'f') \
+ x = sum * base + *s - 'a'; \
+ else if (base == 16 && 'A' <= *s && *s <= 'F') \
+ x = sum * base + *s - 'A'; \
+ else \
+ break; \
+ if (x <= sum) \
+ { \
+ mu_cfg_perror (d, loc, _("numeric overflow")); \
+ return 1; \
+ } \
+ else if (limit && x > limit) \
+ { \
+ mu_cfg_perror (d, loc, \
+ _("value out of allowed range")); \
+ return 1; \
+ } \
+ sum = x; \
+ } \
+ res = sum; \
+ }
+
+#define STRxTONUM(s, type, res, limit, d, loc) \
+ { \
+ int base; \
+ if (*s == '0') \
+ { \
+ s++; \
+ if (*s == 0) \
+ base = 10; \
+ else if (*s == 'x' || *s == 'X') \
+ { \
+ s++; \
+ base = 16; \
+ } \
+ else \
+ base = 8; \
+ } else \
+ base = 10; \
+ STRTONUM (s, type, base, res, limit, d, loc); \
+ }
+
+#define GETUNUM(str, type, res, d, loc) \
+ { \
+ type tmpres; \
+ const char *s = str; \
+ STRxTONUM (s, type, tmpres, 0, d, loc); \
+ if (*s) \
+ { \
+ mu_cfg_perror (d, loc, \
+ _("not a number (stopped near `%s')"), \
+ s); \
+ return 1; \
+ } \
+ res = tmpres; \
+ }
+
+#define GETSNUM(str, type, res, d, loc) \
+ { \
+ unsigned type tmpres; \
+ const char *s = str; \
+ int sign; \
+ unsigned type limit; \
+ \
+ if (*s == '-') \
+ { \
+ sign++; \
+ s++; \
+ limit = TYPE_MINIMUM (type); \
+ limit = - limit; \
+ } \
+ else \
+ { \
+ sign = 0; \
+ limit = TYPE_MAXIMUM (type); \
+ } \
+ \
+ STRxTONUM (s, unsigned type, tmpres, limit, d, loc); \
+ if (*s) \
+ { \
+ mu_cfg_perror (d, loc, \
+ _("not a number (stopped near `%s')"), \
+ s); \
+ return 1; \
+ } \
+ res = sign ? - tmpres : tmpres; \
+ }
+
+static int
+parse_ipv4 (struct scan_tree_data *sdata, const mu_cfg_locus_t *locus,
+ const char *str, struct in_addr *res)
+{
+ struct in_addr addr;
+ if (inet_aton (str, &addr) == 0)
+ {
+ mu_cfg_perror (sdata->tree->debug, locus, _("not an IPv4"));
+ return 1;
+ }
+ addr.s_addr = ntohl (addr.s_addr);
+ *res = addr;
+ return 0;
+}
+
+static int
+parse_host (struct scan_tree_data *sdata, const mu_cfg_locus_t *locus,
+ const char *str, struct in_addr *res)
+{
+ struct in_addr addr;
+ struct hostent *hp = gethostbyname (str);
+ if (hp)
+ {
+ addr.s_addr = *(unsigned long *)hp->h_addr;
+ }
+ else if (inet_aton (str, &addr) == 0)
+ {
+ mu_cfg_perror (sdata->tree->debug, locus,
+ _("cannot resolve hostname `%s'"),
+ str);
+ return 1;
+ }
+ addr.s_addr = ntohl (addr.s_addr);
+ *res = addr;
+ return 0;
+}
+
+static int
+parse_cidr (struct scan_tree_data *sdata, const mu_cfg_locus_t *locus,
+ const char *str, mu_cfg_cidr_t *res)
+{
+ struct in_addr addr;
+ unsigned long mask;
+ char astr[16];
+ const char *p, *s;
+
+ p = strchr (str, '/');
+ if (p)
+ {
+ int len = p - str;
+ if (len > sizeof astr - 1) {
+ mu_cfg_perror (sdata->tree->debug, locus,
+ _("not a valid IPv4 address in CIDR"));
+ return 1;
+ }
+ memcpy (astr, str, len);
+ astr[len] = 0;
+ if (inet_aton (astr, &addr) == 0)
+ {
+ mu_cfg_perror (sdata->tree->debug, locus,
+ _("not a valid IPv4 address in CIDR"));
+ return 1;
+ }
+ addr.s_addr = ntohl (addr.s_addr);
+
+ p++;
+ s = p;
+ STRxTONUM (s, unsigned long, mask, 0, sdata->tree->debug, locus);
+ if (*s == '.')
+ {
+ struct in_addr a;
+ if (inet_aton (p, &a) == 0)
+ {
+ mu_cfg_perror (sdata->tree->debug, locus,
+ _("not a valid network in CIDR"));
+ return 1;
+ }
+ a.s_addr = ntohl (a.s_addr);
+ for (mask = 0; (a.s_addr & 1) == 0 && mask < 32; )
+ {
+ mask++;
+ a.s_addr >>= 1;
+ }
+ mask = 32 - mask;
+ }
+ else if (mask > 32)
+ {
+ mu_cfg_perror (sdata->tree->debug, locus,
+ _("not a valid network mask in CIDR"));
+ return 1;
+ }
+ }
+ else
+ {
+ int i;
+ unsigned short x;
+ addr.s_addr = 0;
+
+ p = str;
+ for (i = 0; i < 3; i++)
+ {
+ STRxTONUM (p, unsigned short, x, 255, sdata->tree->debug, locus);
+ if (*p != '.')
+ break;
+ addr.s_addr = (addr.s_addr << 8) + x;
+ }
+
+ if (*p)
+ {
+ mu_cfg_perror (sdata->tree->debug, locus,
+ _("not a CIDR (stopped near `%s')"),
+ p);
+ return 1;
+ }
+
+ mask = i * 8;
+
+ addr.s_addr <<= (4 - i) * 8;
+ }
+
+ res->addr = addr;
+ res->mask = mask;
+ return 0;
+}
+
+int
+mu_cfg_parse_boolean (const char *str, int *res)
+{
+ if (strcmp (str, "yes") == 0
+ || strcmp (str, "on") == 0
+ || strcmp (str, "t") == 0
+ || strcmp (str, "true") == 0
+ || strcmp (str, "1") == 0)
+ *res = 1;
+ else if (strcmp (str, "no") == 0
+ || strcmp (str, "off") == 0
+ || strcmp (str, "nil") == 0
+ || strcmp (str, "false") == 0
+ || strcmp (str, "0") == 0)
+ *res = 0;
+ else
+ return 1;
+ return 0;
+}
+
+static int
+parse_bool (struct scan_tree_data *sdata, const mu_cfg_locus_t *locus,
+ const char *str, int *res)
+{
+ if (mu_cfg_parse_boolean (str, res))
+ {
+ mu_cfg_perror (sdata->tree->debug, locus, _("not a boolean"));
+ return 1;
+ }
+ return 0;
+}
+
+static int
+valcvt (struct scan_tree_data *sdata, const mu_cfg_locus_t *locus,
+ void *tgt,
+ enum mu_cfg_param_data_type type, mu_config_value_t *val)
+{
+ if (val->type != MU_CFG_STRING)
+ {
+ mu_cfg_perror (sdata->tree->debug, locus, _("expected string value"));
+ return 1;
+ }
+ switch (type)
+ {
+ case mu_cfg_string:
+ {
+ char *s = mu_strdup (val->v.string);
+ /* FIXME: free tgt? */
+ *(char**)tgt = s;
+ break;
+ }
+
+ case mu_cfg_short:
+ GETSNUM (val->v.string, short, *(short*)tgt, sdata->tree->debug, locus);
+ break;
+
+ case mu_cfg_ushort:
+ GETUNUM (val->v.string, unsigned short, *(unsigned short*)tgt,
+ sdata->tree->debug, locus);
+ break;
+
+ case mu_cfg_int:
+ GETSNUM (val->v.string, int, *(int*)tgt, sdata->tree->debug, locus);
+ break;
+
+ case mu_cfg_uint:
+ GETUNUM (val->v.string, unsigned int, *(unsigned int*)tgt,
+ sdata->tree->debug, locus);
+ break;
+
+ case mu_cfg_long:
+ GETSNUM (val->v.string, long, *(long*)tgt,
+ sdata->tree->debug, locus);
+ break;
+
+ case mu_cfg_ulong:
+ GETUNUM (val->v.string, unsigned long, *(unsigned long*)tgt,
+ sdata->tree->debug, locus);
+ break;
+
+ case mu_cfg_size:
+ GETUNUM (val->v.string, size_t, *(size_t*)tgt,
+ sdata->tree->debug, locus);
+ break;
+
+ case mu_cfg_off:
+ mu_cfg_perror (sdata->tree->debug, locus, _("not implemented yet"));
+ /* GETSNUM(node->tag_label, off_t, *(off_t*)tgt); */
+ return 1;
+
+ case mu_cfg_time:
+ GETUNUM (val->v.string, time_t, *(time_t*)tgt,
+ sdata->tree->debug, locus);
+ break;
+
+ case mu_cfg_bool:
+ if (parse_bool (sdata, locus, val->v.string, (int*) tgt))
+ return 1;
+ break;
+
+ case mu_cfg_ipv4:
+ if (parse_ipv4 (sdata, locus, val->v.string, (struct in_addr *)tgt))
+ return 1;
+ break;
+
+ case mu_cfg_cidr:
+ if (parse_cidr (sdata, locus, val->v.string, (mu_cfg_cidr_t *)tgt))
+ return 1;
+ break;
+
+ case mu_cfg_host:
+ if (parse_host (sdata, locus, val->v.string, (struct in_addr *)tgt))
+ return 1;
+ break;
+
+ default:
+ return 1;
+ }
+ return 0;
+}
+
+struct set_closure
+{
+ mu_list_t list;
+ enum mu_cfg_param_data_type type;
+ struct scan_tree_data *sdata;
+ const mu_cfg_locus_t *locus;
+};
+
+static size_t config_type_size[] = {
+ sizeof (char*), /* mu_cfg_string */
+ sizeof (short), /* mu_cfg_short */
+ sizeof (unsigned short), /* mu_cfg_ushort */
+ sizeof (int), /* mu_cfg_int */
+ sizeof (unsigned), /* mu_cfg_uint */
+ sizeof (long), /* mu_cfg_long */
+ sizeof (unsigned long), /* mu_cfg_ulong */
+ sizeof (size_t), /* mu_cfg_size */
+ sizeof (mu_off_t), /* mu_cfg_off */
+ sizeof (time_t), /* mu_cfg_time */
+ sizeof (int), /* mu_cfg_bool */
+ sizeof (struct in_addr), /* mu_cfg_ipv4 */
+ sizeof (mu_cfg_cidr_t), /* mu_cfg_cidr */
+ sizeof (struct in_addr), /* mu_cfg_host */
+ 0, /* mu_cfg_callback */
+ 0, /* mu_cfg_section */
+} ;
+
+static int
+_set_fun (void *item, void *data)
+{
+ mu_config_value_t *val = item;
+ struct set_closure *clos = data;
+ void *tgt;
+ size_t size;
+
+ if (clos->type >= MU_ARRAY_SIZE(config_type_size)
+ || (size = config_type_size[clos->type]) == 0)
+ {
+ mu_cfg_perror (clos->sdata->tree->debug, clos->locus,
+ _("INTERNAL ERROR at %s:%d: unhandled data type %d"),
+ __FILE__, __LINE__, clos->type);
+ return 1;
+ }
+
+ tgt = mu_alloc (size);
+ if (!tgt)
+ {
+ mu_cfg_perror (clos->sdata->tree->debug, clos->locus,
+ _("not enough memory"));
+ return 1;
+ }
+
+ if (valcvt (clos->sdata, clos->locus, &tgt, clos->type, val) == 0)
+ mu_list_append (clos->list, tgt);
+ return 0;
+}
+
+static int
+parse_param (struct scan_tree_data *sdata, const mu_cfg_node_t *node)
+{
+ void *tgt;
+ struct set_closure clos;
+ struct mu_cfg_param *param = find_param (sdata->list->sec, node->tag,
+ 0);
+
+ if (!param)
+ {
+ mu_cfg_perror (sdata->tree->debug, &node->locus,
+ _("unknown keyword `%s'"),
+ node->tag);
+ return 1;
+ }
+
+ if (param->data)
+ tgt = param->data;
+ else if (sdata->list->sec->target)
+ tgt = (char*)sdata->list->sec->target + param->offset;
+ else if (sdata->target)
+ tgt = (char*)sdata->target + param->offset;
+ else if (param->type == mu_cfg_callback)
+ tgt = NULL;
+ else
+ {
+ mu_cfg_perror (sdata->tree->debug, &node->locus,
+ _("INTERNAL ERROR: cannot determine target offset for "
+ "%s"), param->ident);
+ abort ();
+ }
+
+ memset (&clos, 0, sizeof clos);
+ clos.type = MU_CFG_TYPE (param->type);
+ if (MU_CFG_IS_LIST (param->type))
+ {
+ clos.sdata = sdata;
+ clos.locus = &node->locus;
+ switch (node->label->type)
+ {
+ case MU_CFG_LIST:
+ break;
+
+ case MU_CFG_STRING:
+ {
+ mu_list_t list;
+ mu_list_create (&list);
+ mu_list_append (list, config_value_dup (node->label));
+ node->label->type = MU_CFG_LIST;
+ node->label->v.list = list;
+ }
+ break;
+
+ case MU_CFG_ARRAY:
+ mu_cfg_perror (sdata->tree->debug, &node->locus,
+ _("expected list, but found array"));
+ return 1;
+ }
+
+ mu_list_create (&clos.list);
+ mu_list_do (node->label->v.list, _set_fun, &clos);
+ *(mu_list_t*)tgt = clos.list;
+ }
+ else if (clos.type == mu_cfg_callback)
+ {
+ mu_debug_set_locus (sdata->tree->debug, node->locus.file,
+ node->locus.line);
+ if (!param->callback)
+ {
+ mu_cfg_perror (sdata->tree->debug, &node->locus,
+ _("INTERNAL ERROR: %s: callback not defined"),
+ node->tag);
+ abort ();
+ }
+ if (param->callback (sdata->tree->debug, tgt, node->label))
+ return 1;
+
+ }
+ else
+ return valcvt (sdata, &node->locus, tgt, clos.type, node->label);
+
+ return 0;
+}
+
+
+static int
+_scan_tree_helper (const mu_cfg_node_t *node, void *data)
+{
+ struct scan_tree_data *sdata = data;
+ struct mu_cfg_section *sec;
+
+ switch (node->type)
+ {
+ case mu_cfg_node_undefined:
+ abort ();
+
+ case mu_cfg_node_statement:
+ sec = find_subsection (sdata->list->sec, node->tag, 0);
+ if (!sec)
+ {
+ if (mu_cfg_parser_verbose)
+ {
+ _mu_cfg_debug_set_locus (sdata->tree->debug, &node->locus);
+ mu_cfg_format_error (sdata->tree->debug, MU_DIAG_WARNING,
+ _("unknown section `%s'"),
+ node->tag);
+ }
+ return MU_CFG_ITER_SKIP;
+ }
+ if (!sec->children)
+ return MU_CFG_ITER_SKIP;
+ if (sdata->list->sec->target)
+ sec->target = (char*)sdata->list->sec->target + sec->offset;
+ else if (sdata->target)
+ sec->target = (char*)sdata->target + sec->offset;
+ if (sec->parser)
+ {
+ mu_debug_set_locus (sdata->tree->debug,
+ node->locus.file ?
+ node->locus.file : _("unknown file"),
+ node->locus.line);
+ if (sec->parser (mu_cfg_section_start, node,
+ sec->label, &sec->target,
+ sdata->call_data, sdata->tree))
+ {
+ sdata->error++;
+ return MU_CFG_ITER_SKIP;
+ }
+ }
+ push_section(sdata, sec);
+ break;
+
+ case mu_cfg_node_param:
+ if (parse_param (sdata, node))
+ {
+ sdata->error++;
+ return MU_CFG_ITER_SKIP;
+ }
+ break;
+ }
+ return MU_CFG_ITER_OK;
+}
+
+static int
+_scan_tree_end_helper (const mu_cfg_node_t *node, void *data)
+{
+ struct scan_tree_data *sdata = data;
+ struct mu_cfg_section *sec;
+
+ switch (node->type)
+ {
+ default:
+ abort ();
+
+ case mu_cfg_node_statement:
+ sec = pop_section (sdata);
+ if (sec && sec->parser)
+ {
+ if (sec->parser (mu_cfg_section_end, node,
+ sec->label, &sec->target,
+ sdata->call_data, sdata->tree))
+ {
+ sdata->error++;
+ return MU_CFG_ITER_SKIP;
+ }
+ }
+ }
+ return MU_CFG_ITER_OK;
+}
+
+int
+mu_cfg_scan_tree (mu_cfg_tree_t *tree, struct mu_cfg_section *sections,
+ void *target, void *data)
+{
+ mu_debug_t debug = NULL;
+ struct scan_tree_data dat;
+ struct mu_cfg_iter_closure clos;
+
+ dat.tree = tree;
+ dat.list = NULL;
+ dat.error = 0;
+ dat.call_data = data;
+ dat.target = target;
+
+ if (!tree->debug)
+ {
+ mu_diag_get_debug (&debug);
+ tree->debug = debug;
+ }
+ if (push_section (&dat, sections))
+ return 1;
+ clos.beg = _scan_tree_helper;
+ clos.end = _scan_tree_end_helper;
+ clos.data = &dat;
+ mu_cfg_preorder (tree->nodes, &clos);
+ if (debug)
+ {
+ mu_debug_set_locus (debug, NULL, 0);
+ tree->debug = NULL;
+ }
+ pop_section (&dat);
+ return dat.error;
+}
+
+int
+mu_cfg_find_section (struct mu_cfg_section *root_sec,
+ const char *path, struct mu_cfg_section **retval)
+{
+ while (path[0])
+ {
+ struct mu_cfg_section *sec;
+ size_t len;
+ const char *p;
+
+ while (*path == MU_CFG_PATH_DELIM)
+ path++;
+
+ if (*path == 0)
+ return MU_ERR_NOENT;
+
+ p = strchr (path, MU_CFG_PATH_DELIM);
+ if (p)
+ len = p - path;
+ else
+ len = strlen (path);
+
+ sec = find_subsection (root_sec, path, len);
+ if (!sec)
+ return MU_ERR_NOENT;
+ root_sec = sec;
+ path += len;
+ }
+ if (retval)
+ *retval = root_sec;
+ return 0;
+}
+
+
+int
+mu_cfg_tree_create (struct mu_cfg_tree **ptree)
+{
+ struct mu_cfg_tree *tree = calloc (1, sizeof *tree);
+ if (!tree)
+ return errno;
+ mu_opool_create (&tree->pool, 1);
+ *ptree = tree;
+ return 0;
+}
+
+void
+mu_cfg_tree_set_debug (struct mu_cfg_tree *tree, mu_debug_t debug)
+{
+ tree->debug = debug;
+}
+
+mu_cfg_node_t *
+mu_cfg_tree_create_node (struct mu_cfg_tree *tree,
+ enum mu_cfg_node_type type,
+ const mu_cfg_locus_t *loc,
+ const char *tag, const char *label,
+ mu_list_t nodelist)
+{
+ char *p;
+ mu_cfg_node_t *np;
+ size_t size = sizeof *np + strlen (tag) + 1;
+ mu_config_value_t val;
+
+ np = mu_alloc (size);
+ np->type = type;
+ if (loc)
+ np->locus = *loc;
+ else
+ memset (&np->locus, 0, sizeof np->locus);
+ p = (char*) (np + 1);
+ np->tag = p;
+ strcpy (p, tag);
+ p += strlen (p) + 1;
+ val.type = MU_CFG_STRING;
+ if (label)
+ {
+ mu_opool_clear (tree->pool);
+ mu_opool_appendz (tree->pool, label);
+ val.v.string = mu_opool_finish (tree->pool, NULL);
+ np->label = config_value_dup (&val);
+ }
+ else
+ np->label = NULL;
+ np->nodes = nodelist;
+ return np;
+}
+
+void
+mu_cfg_tree_add_node (mu_cfg_tree_t *tree, mu_cfg_node_t *node)
+{
+ if (!node)
+ return;
+ if (!tree->nodes)
+ /* FIXME: return code? */
+ mu_cfg_create_node_list (&tree->nodes);
+ mu_list_append (tree->nodes, node);
+}
+
+void
+mu_cfg_tree_add_nodelist (mu_cfg_tree_t *tree, mu_list_t nodelist)
+{
+ if (!nodelist)
+ return;
+ if (!tree->nodes)
+ /* FIXME: return code? */
+ mu_cfg_create_node_list (&tree->nodes);
+ mu_list_append_list (tree->nodes, nodelist);
+}
+
+
+/* Return 1 if configuration value A equals B */
+int
+mu_cfg_value_eq (mu_config_value_t *a, mu_config_value_t *b)
+{
+ if (a->type != b->type)
+ return 0;
+ switch (a->type)
+ {
+ case MU_CFG_STRING:
+ if (a->v.string == NULL)
+ return b->v.string == NULL;
+ return strcmp (a->v.string, b->v.string) == 0;
+
+ case MU_CFG_LIST:
+ {
+ int ret = 1;
+ size_t cnt;
+ size_t i;
+ mu_iterator_t aitr, bitr;
+
+ mu_list_count (a->v.list, &cnt);
+ mu_list_count (b->v.list, &i);
+ if (i != cnt)
+ return 1;
+
+ mu_list_get_iterator (a->v.list, &aitr);
+ mu_list_get_iterator (b->v.list, &bitr);
+ for (i = 0,
+ mu_iterator_first (aitr),
+ mu_iterator_first (bitr);
+ !mu_iterator_is_done (aitr) && !mu_iterator_is_done (bitr);
+ mu_iterator_next (aitr),
+ mu_iterator_next (bitr),
+ i++)
+ {
+ mu_config_value_t *ap, *bp;
+ mu_iterator_current (aitr, (void**)&ap);
+ mu_iterator_current (bitr, (void**)&bp);
+ ret = mu_cfg_value_eq (ap, bp);
+ if (!ret)
+ break;
+ }
+ mu_iterator_destroy (&aitr);
+ mu_iterator_destroy (&bitr);
+ return ret && i == cnt;
+ }
+
+ case MU_CFG_ARRAY:
+ if (a->v.arg.c == b->v.arg.c)
+ {
+ size_t i;
+ for (i = 0; i < a->v.arg.c; i++)
+ if (!mu_cfg_value_eq (&a->v.arg.v[i], &b->v.arg.v[i]))
+ return 0;
+ return 1;
+ }
+ }
+ return 0;
+}
+
+
+struct find_data
+{
+ int argc;
+ char **argv;
+ int tag;
+ mu_config_value_t *label;
+ const mu_cfg_node_t *node;
+};
+
+static void
+free_value_mem (mu_config_value_t *p)
+{
+ switch (p->type)
+ {
+ case MU_CFG_STRING:
+ free ((char*)p->v.string);
+ break;
+
+ case MU_CFG_LIST:
+ /* FIXME */
+ break;
+
+ case MU_CFG_ARRAY:
+ {
+ size_t i;
+ for (i = 0; i < p->v.arg.c; i++)
+ free_value_mem (&p->v.arg.v[i]);
+ }
+ }
+}
+
+static void
+destroy_value (void *p)
+{
+ mu_config_value_t *val = p;
+ if (val)
+ {
+ free_value_mem (val);
+ free (val);
+ }
+}
+
+static mu_config_value_t *
+parse_label (const char *str)
+{
+ mu_config_value_t *val = NULL;
+ int count, i;
+ char **vect;
+ size_t len = strlen (str);
+
+ if (len > 1 && str[0] == '(' && str[len-1] == ')')
+ {
+ mu_list_t lst;
+ mu_argcv_get_np (str + 1, len - 2,
+ ", \t", NULL,
+ 0,
+ &count, &vect, NULL);
+ mu_list_create (&lst);
+ mu_list_set_destroy_item (lst, destroy_value);
+ for (i = 0; i < count; i++)
+ {
+ mu_config_value_t *p = mu_alloc (sizeof (*p));
+ p->type = MU_CFG_STRING;
+ p->v.string = vect[i];
+ mu_list_append (lst, p);
+ }
+ free (vect);
+ val = mu_alloc (sizeof (*val));
+ val->type = MU_CFG_LIST;
+ val->v.list = lst;
+ }
+ else
+ {
+ mu_argcv_get (str, NULL, NULL, &count, &vect);
+ val = mu_alloc (sizeof (*val));
+ if (count == 1)
+ {
+ val->type = MU_CFG_STRING;
+ val->v.string = vect[0];
+ free (vect);
+ }
+ else
+ {
+ val->type = MU_CFG_ARRAY;
+ val->v.arg.c = count;
+ val->v.arg.v = mu_alloc (count * sizeof (val->v.arg.v[0]));
+ for (i = 0; i < count; i++)
+ {
+ val->v.arg.v[i].type = MU_CFG_STRING;
+ val->v.arg.v[i].v.string = vect[i];
+ }
+ free (vect);
+ }
+ }
+ return val;
+}
+
+static void
+parse_tag (struct find_data *fptr)
+{
+ char *p = strchr (fptr->argv[fptr->tag], '=');
+ if (p)
+ {
+ *p++ = 0;
+ fptr->label = parse_label (p);
+ }
+ else
+ fptr->label = NULL;
+}
+
+static int
+node_finder (const mu_cfg_node_t *node, void *data)
+{
+ struct find_data *fdptr = data;
+ if (strcmp (fdptr->argv[fdptr->tag], node->tag) == 0
+ && (!fdptr->label || mu_cfg_value_eq (fdptr->label, node->label)))
+ {
+ fdptr->tag++;
+ if (fdptr->tag == fdptr->argc)
+ {
+ fdptr->node = node;
+ return MU_CFG_ITER_STOP;
+ }
+ parse_tag (fdptr);
+ return MU_CFG_ITER_OK;
+ }
+
+ return node->type == mu_cfg_node_statement ?
+ MU_CFG_ITER_SKIP : MU_CFG_ITER_OK;
+}
+
+int
+mu_cfg_find_node (mu_cfg_tree_t *tree, const char *path, mu_cfg_node_t **pval)
+{
+ int rc;
+ struct find_data data;
+ struct mu_cfg_iter_closure clos;
+
+ rc = mu_argcv_get_np (path, strlen (path),
+ MU_CFG_PATH_DELIM_STR, NULL,
+ 0, &data.argc, &data.argv, NULL);
+ if (rc)
+ return rc;
+ data.tag = 0;
+ parse_tag (&data);
+
+ clos.beg = node_finder;
+ clos.end = NULL;
+ clos.data = &data;
+ rc = mu_cfg_preorder (tree->nodes, &clos);
+ destroy_value (data.label);
+ if (rc)
+ {
+ *pval = (mu_cfg_node_t *) data.node;
+ return 0;
+ }
+ return MU_ERR_NOENT;
+}
+
+
+
+int
+mu_cfg_create_subtree (const char *path, mu_cfg_node_t **pnode)
+{
+ int rc;
+ int argc, i;
+ char *p;
+ char **argv;
+ mu_cfg_locus_t locus;
+ enum mu_cfg_node_type type;
+ mu_cfg_node_t *node = NULL;
+ char *delim = MU_CFG_PATH_DELIM_STR;
+ char static_delim[2] = { 0, 0 };
+
+ locus.file = "<int>";
+ locus.line = 0;
+
+ if (path[0] == '\\')
+ {
+ argv = calloc (2, sizeof (*argv));
+ if (!argv)
+ return ENOMEM;
+ argv[0] = strdup (path + 1);
+ if (!argv[0])
+ {
+ free (argv);
+ return ENOMEM;
+ }
+ argv[1] = NULL;
+ argc = 1;
+ rc = 0;
+ }
+ else
+ {
+ if (mu_ispunct (path[0]))
+ {
+ delim = static_delim;
+ delim[0] = path[0];
+ path++;
+ }
+ rc = mu_argcv_get_np (path, strlen (path), delim, NULL, 0,
+ &argc, &argv, NULL);
+ }
+
+ if (rc)
+ return rc;
+
+ for (i = argc - 1; i >= 0; i--)
+ {
+ mu_list_t nodelist = NULL;
+ mu_config_value_t *label = NULL;
+
+ p = strrchr (argv[i], '=');
+ type = mu_cfg_node_statement;
+ if (p)
+ {
+ *p++ = 0;
+ label = parse_label (p);
+ if (i == argc - 1)
+ type = mu_cfg_node_param;
+ }
+
+ if (node)
+ {
+ mu_cfg_create_node_list (&nodelist);
+ mu_list_append (nodelist, node);
+ }
+ node = mu_cfg_alloc_node (type, &locus, argv[i], label, nodelist);
+ }
+
+ mu_argcv_free (argc, argv);
+ *pnode = node;
+ return 0;
+}
+
+
+
+
+
diff --git a/libmailutils/crlfdot.c b/libmailutils/crlfdot.c
new file mode 100644
index 000000000..eed4519ac
--- /dev/null
+++ b/libmailutils/crlfdot.c
@@ -0,0 +1,336 @@
+/* GNU Mailutils -- a suite of utilities for electronic mail
+ Copyright (C) 2003, 2007, 2010 Free Software Foundation, Inc.
+
+ This library is free software; you can redistribute it and/or
+ modify it under the terms of the GNU Lesser General Public
+ License as published by the Free Software Foundation; either
+ version 3 of the License, or (at your option) any later version.
+
+ This library 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
+ Lesser General Public License for more details.
+
+ You should have received a copy of the GNU Lesser General
+ Public License along with this library; if not, write to the
+ Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor,
+ Boston, MA 02110-1301 USA */
+
+/* This source implements a CRLFDOT filter, useful for data I/O in
+ such protocols as POP3 and SMTP. When encoding, this filter
+ replaces each '\n' not following '\r' by "\r\n" and "byte-stuffs"
+ the input by outputting an additional '.' in front of any '.' appearing
+ at the beginning of a line. Upon closing the filter in this mode, it
+ outputs additional ".\r\n".
+
+ When decoding, the reverse is performed: each "\r\n" is replaced by a
+ '\n', and additional '.' are removed from beginning of lines. A single
+ dot on a line by itself marks end of the stream.
+ */
+
+#ifdef HAVE_CONFIG_H
+# include <config.h>
+#endif
+
+#include <stdlib.h>
+#include <string.h>
+#include <mailutils/errno.h>
+#include <mailutils/filter.h>
+#include <mailutils/stream.h>
+
+enum crlfdot_decode_state
+ {
+ crlfdot_decode_init, /* initial state */
+ crlfdot_decode_char, /* Any character excepting [\r\n.] */
+ crlfdot_decode_cr, /* prev. char was \r */
+ crlfdot_decode_crlf, /* 2 prev. char were \r\n */
+ crlfdot_decode_dot, /* 3 prev. chars were \r\n. */
+ crlfdot_decode_dotcr, /* 4 prev. chars were \r\n.\r */
+ crlfdot_decode_end /* final state, a \r\n.\r\n seen. */
+ };
+
+static enum crlfdot_decode_state
+new_decode_state (enum crlfdot_decode_state state, int c)
+{
+ switch (state)
+ {
+ case crlfdot_decode_init:
+ switch (c)
+ {
+ case '\r':
+ return crlfdot_decode_cr;
+ case '.':
+ return crlfdot_decode_dot;
+ }
+ break;
+
+ case crlfdot_decode_char:
+ switch (c)
+ {
+ case '\r':
+ return crlfdot_decode_cr;
+ }
+ break;
+
+ case crlfdot_decode_cr:
+ switch (c)
+ {
+ case '\r':
+ return crlfdot_decode_cr;
+ case '\n':
+ return crlfdot_decode_crlf;
+ }
+ break;
+
+ case crlfdot_decode_crlf:
+ switch (c)
+ {
+ case '\r':
+ return crlfdot_decode_cr;
+ case '.':
+ return crlfdot_decode_dot;
+ }
+
+ case crlfdot_decode_dot:
+ switch (c)
+ {
+ case '\r':
+ return crlfdot_decode_dotcr;
+ }
+ break;
+
+ case crlfdot_decode_dotcr:
+ switch (c)
+ {
+ case '\n':
+ return crlfdot_decode_end;
+ }
+
+ case crlfdot_decode_end:
+ break;
+ }
+ return crlfdot_decode_char;
+}
+
+/* Move min(isize,osize) bytes from iptr to optr, replacing each \r\n
+ with \n. */
+static enum mu_filter_result
+_crlfdot_decoder (void *xd,
+ enum mu_filter_command cmd,
+ struct mu_filter_io *iobuf)
+{
+ int *pstate = xd;
+ size_t i, j;
+ const unsigned char *iptr;
+ size_t isize;
+ char *optr;
+ size_t osize;
+
+ switch (cmd)
+ {
+ case mu_filter_init:
+ *pstate = crlfdot_decode_init;
+ return mu_filter_ok;
+
+ case mu_filter_done:
+ return mu_filter_ok;
+
+ default:
+ break;
+ }
+
+ iptr = (const unsigned char *) iobuf->input;
+ isize = iobuf->isize;
+ optr = iobuf->output;
+ osize = iobuf->osize;
+
+ for (i = j = 0; *pstate != crlfdot_decode_end && i < isize && j < osize; i++)
+ {
+ unsigned char c = *iptr++;
+
+ if (c == '\r')
+ {
+ if (i + 1 == isize)
+ break;
+ *pstate = new_decode_state (*pstate, c);
+ if (*iptr == '\n')
+ continue;
+ }
+ else if (c == '.' &&
+ (*pstate == crlfdot_decode_init ||
+ *pstate == crlfdot_decode_crlf))
+ {
+ /* Make sure we have two more characters in the buffer */
+ if (i + 2 == isize)
+ break;
+ *pstate = new_decode_state (*pstate, c);
+ if (*iptr != '\r')
+ continue;
+ }
+ else
+ *pstate = new_decode_state (*pstate, c);
+ optr[j++] = c;
+ }
+
+ if (*pstate == crlfdot_decode_end)
+ {
+ j -= 2; /* remove the trailing .\n */
+ iobuf->eof = 1;
+ }
+ iobuf->isize = i;
+ iobuf->osize = j;
+ return mu_filter_ok;
+}
+
+enum crlfdot_encode_state
+ {
+ crlfdot_encode_init, /* initial state */
+ crlfdot_encode_char, /* Any character excepting [\r\n] */
+ crlfdot_encode_cr, /* prev. char was \r */
+ crlfdot_encode_lf, /* prev. char was \n */
+ };
+
+static enum crlfdot_encode_state
+new_encode_state (enum crlfdot_encode_state state, int c)
+{
+ switch (c)
+ {
+ case '\r':
+ return crlfdot_encode_cr;
+
+ case '\n':
+ return crlfdot_encode_lf;
+ }
+ return crlfdot_encode_char;
+}
+
+/* Move min(isize,osize) bytes from iptr to optr, replacing each \n
+ with \r\n. Any input \r\n sequences remain untouched. */
+static enum mu_filter_result
+_crlfdot_encoder (void *xd,
+ enum mu_filter_command cmd,
+ struct mu_filter_io *iobuf)
+{
+ enum mu_filter_result result;
+ size_t i, j;
+ const unsigned char *iptr;
+ size_t isize;
+ char *optr;
+ size_t osize;
+ int *state = xd;
+
+ switch (cmd)
+ {
+ case mu_filter_init:
+ *state = crlfdot_encode_init;
+ return mu_filter_ok;
+
+ case mu_filter_done:
+ return mu_filter_ok;
+
+ default:
+ break;
+ }
+
+ iptr = (const unsigned char *) iobuf->input;
+ isize = iobuf->isize;
+ optr = iobuf->output;
+ osize = iobuf->osize;
+
+ for (i = j = 0; i < isize && j < osize; i++, iptr++)
+ {
+ unsigned char c = *iptr;
+ int curstate = *state;
+
+ if (c == '\n')
+ {
+ if (curstate == crlfdot_encode_cr)
+ optr[j++] = c;
+ else if (j + 1 == osize)
+ {
+ if (i == 0)
+ {
+ iobuf->osize = 2;
+ return mu_filter_moreoutput;
+ }
+ break;
+ }
+ else
+ {
+ optr[j++] = '\r';
+ optr[j++] = '\n';
+ }
+ }
+ else if (c == '.' &&
+ (curstate == crlfdot_encode_init ||
+ curstate == crlfdot_encode_lf))
+ {
+ if (j + 2 > osize)
+ {
+ if (i == 0)
+ {
+ iobuf->osize = 2;
+ return mu_filter_moreoutput;
+ }
+ break;
+ }
+ optr[j++] = '.';
+ optr[j++] = '.';
+ }
+ else
+ optr[j++] = c;
+
+ *state = new_encode_state (curstate, c);
+ }
+
+ result = mu_filter_ok;
+ if (cmd == mu_filter_lastbuf)
+ {
+ switch (*state)
+ {
+ case crlfdot_encode_lf:
+ if (j + 3 > osize)
+ result = mu_filter_again;
+ break;
+
+ default:
+ if (j + 5 > osize)
+ result = mu_filter_again;
+ else
+ {
+ optr[j++] = '\r';
+ optr[j++] = '\n';
+ }
+ }
+
+ if (result == mu_filter_ok)
+ {
+ optr[j++] = '.';
+ optr[j++] = '\r';
+ optr[j++] = '\n';
+ }
+ }
+
+ iobuf->isize = i;
+ iobuf->osize = j;
+ return result;
+}
+
+static int
+alloc_state (void **pret, int mode MU_ARG_UNUSED, void *data MU_ARG_UNUSED)
+{
+ *pret = malloc (sizeof (int));
+ if (!*pret)
+ return ENOMEM;
+ return 0;
+}
+
+static struct _mu_filter_record _crlfdot_filter = {
+ "CRLFDOT",
+ 0,
+ alloc_state,
+ _crlfdot_encoder,
+ _crlfdot_decoder
+};
+
+mu_filter_record_t mu_crlfdot_filter = &_crlfdot_filter;
diff --git a/libmailutils/crlfflt.c b/libmailutils/crlfflt.c
new file mode 100644
index 000000000..9dc147c9e
--- /dev/null
+++ b/libmailutils/crlfflt.c
@@ -0,0 +1,182 @@
+/* GNU Mailutils -- a suite of utilities for electronic mail
+ Copyright (C) 2009, 2010 Free Software Foundation, Inc.
+
+ This library is free software; you can redistribute it and/or modify
+ it under the terms of the GNU Lesser General Public License as published by
+ the Free Software Foundation; either version 3, or (at your option)
+ any later version.
+
+ This library 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 Lesser General Public License for more details.
+
+ You should have received a copy of the GNU Lesser General Public License
+ along with GNU Mailutils. If not, see <http://www.gnu.org/licenses/>. */
+
+#ifdef HAVE_CONFIG_H
+# include <config.h>
+#endif
+#include <stdlib.h>
+#include <string.h>
+#include <mailutils/errno.h>
+#include <mailutils/filter.h>
+
+enum crlf_state
+{
+ state_init,
+ state_cr
+};
+
+/* Move min(isize,osize) bytes from iptr to optr, replacing each \n
+ with \r\n. Any input \r\n sequences remain untouched. */
+static enum mu_filter_result
+_crlf_encoder (void *xd,
+ enum mu_filter_command cmd,
+ struct mu_filter_io *iobuf)
+{
+ size_t i, j;
+ const unsigned char *iptr;
+ size_t isize;
+ char *optr;
+ size_t osize;
+ enum crlf_state *state = xd;
+
+ switch (cmd)
+ {
+ case mu_filter_init:
+ *state = state_init;
+ case mu_filter_done:
+ return mu_filter_ok;
+ default:
+ break;
+ }
+
+ iptr = (const unsigned char *) iobuf->input;
+ isize = iobuf->isize;
+ optr = iobuf->output;
+ osize = iobuf->osize;
+
+ for (i = j = 0; i < isize && j < osize; i++)
+ {
+ unsigned char c = *iptr++;
+ if (c == '\n')
+ {
+ if (*state == state_cr)
+ {
+ *state = state_init;
+ optr[j++] = c;
+ }
+ else if (j + 1 == osize)
+ {
+ if (i == 0)
+ {
+ iobuf->osize = 2;
+ return mu_filter_moreoutput;
+ }
+ break;
+ }
+ else
+ {
+ optr[j++] = '\r';
+ optr[j++] = '\n';
+ }
+ }
+ else if (c == '\r')
+ {
+ *state = state_cr;
+ optr[j++] = c;
+ }
+ else
+ optr[j++] = c;
+ }
+ iobuf->isize = i;
+ iobuf->osize = j;
+ return mu_filter_ok;
+}
+
+/* Move min(isize,osize) bytes from iptr to optr, replacing each \r\n
+ with \n. */
+static enum mu_filter_result
+_crlf_decoder (void *xd MU_ARG_UNUSED,
+ enum mu_filter_command cmd,
+ struct mu_filter_io *iobuf)
+{
+ size_t i, j;
+ const unsigned char *iptr;
+ size_t isize;
+ char *optr;
+ size_t osize;
+
+ switch (cmd)
+ {
+ case mu_filter_init:
+ case mu_filter_done:
+ return mu_filter_ok;
+ default:
+ break;
+ }
+
+ iptr = (const unsigned char *) iobuf->input;
+ isize = iobuf->isize;
+ optr = iobuf->output;
+ osize = iobuf->osize;
+
+ for (i = j = 0; i < isize && j < osize; i++)
+ {
+ unsigned char c = *iptr++;
+ if (c == '\r')
+ {
+ if (i + 1 == isize)
+ break;
+ if (*iptr == '\n')
+ continue;
+ }
+ optr[j++] = c;
+ }
+ iobuf->isize = i;
+ iobuf->osize = j;
+ return mu_filter_ok;
+}
+
+static int
+alloc_state (void **pret, int mode, void *data MU_ARG_UNUSED)
+{
+ switch (mode)
+ {
+ case MU_FILTER_ENCODE:
+ *pret = malloc (sizeof (int));
+ if (!*pret)
+ return ENOMEM;
+ break;
+
+ case MU_FILTER_DECODE:
+ *pret = NULL;
+ }
+ return 0;
+}
+
+
+static struct _mu_filter_record _crlf_filter = {
+ "CRLF",
+ 0,
+ alloc_state,
+ _crlf_encoder,
+ _crlf_decoder
+};
+
+mu_filter_record_t mu_crlf_filter = &_crlf_filter;
+
+
+/* For compatibility with previous versions */
+static struct _mu_filter_record _rfc822_filter = {
+ "RFC822",
+ 0,
+ alloc_state,
+ _crlf_encoder,
+ _crlf_decoder
+};
+
+mu_filter_record_t mu_rfc822_filter = &_rfc822_filter;
+
+
diff --git a/libmailutils/cstrcasecmp.c b/libmailutils/cstrcasecmp.c
new file mode 100644
index 000000000..4baeae220
--- /dev/null
+++ b/libmailutils/cstrcasecmp.c
@@ -0,0 +1,58 @@
+/* This file is part of GNU Mailutils
+ Copyright (C) 2009, 2010 Free Software Foundation, Inc.
+
+ This library is free software; you can redistribute it and/or
+ modify it under the terms of the GNU Lesser General Public
+ License as published by the Free Software Foundation; either
+ version 3 of the License, or (at your option) any later version.
+
+ This library 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
+ Lesser General Public License for more details.
+
+ You should have received a copy of the GNU Lesser General
+ Public License along with this library. If not, see
+ <http://www.gnu.org/licenses/>. */
+
+#ifdef HAVE_CONFIG_H
+# include <config.h>
+#endif
+#include <stddef.h>
+#include <mailutils/cctype.h>
+
+int
+mu_c_strcasecmp (const char *a, const char *b)
+{
+ int d = 0;
+ for (; d == 0; a++, b++)
+ {
+ int ac = (int) *a;
+ int bc = (int) *b;
+ if (ac == 0 || bc == 0)
+ return ac - bc;
+ if (mu_isascii (ac) && mu_isascii (bc))
+ d = mu_toupper (ac) - mu_toupper (bc);
+ else
+ d = ac - bc;
+ }
+ return d;
+}
+
+int
+mu_c_strncasecmp (const char *a, const char *b, size_t n)
+{
+ int d = 0;
+ for (; d == 0 && n > 0; a++, b++, n--)
+ {
+ int ac = (int) *a;
+ int bc = (int) *b;
+ if (ac == 0 || bc == 0)
+ return ac - bc;
+ if (mu_isascii (ac) && mu_isascii (bc))
+ d = mu_toupper (ac) - mu_toupper (bc);
+ else
+ d = ac - bc;
+ }
+ return d;
+}
diff --git a/libmailutils/cstrlower.c b/libmailutils/cstrlower.c
new file mode 100644
index 000000000..98afb5f35
--- /dev/null
+++ b/libmailutils/cstrlower.c
@@ -0,0 +1,31 @@
+/* This file is part of GNU Mailutils
+ Copyright (C) 2009, 2010 Free Software Foundation, Inc.
+
+ This library is free software; you can redistribute it and/or
+ modify it under the terms of the GNU Lesser General Public
+ License as published by the Free Software Foundation; either
+ version 3 of the License, or (at your option) any later version.
+
+ This library 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
+ Lesser General Public License for more details.
+
+ You should have received a copy of the GNU Lesser General
+ Public License along with this library. If not, see
+ <http://www.gnu.org/licenses/>. */
+
+#ifdef HAVE_CONFIG_H
+# include <config.h>
+#endif
+#include <mailutils/cctype.h>
+
+int
+mu_strlower (char *s)
+{
+ if (!s)
+ return 0;
+ for (; *s; s++)
+ *s = mu_tolower (*s);
+ return 0;
+}
diff --git a/libmailutils/cstrupper.c b/libmailutils/cstrupper.c
new file mode 100644
index 000000000..965977d44
--- /dev/null
+++ b/libmailutils/cstrupper.c
@@ -0,0 +1,31 @@
+/* This file is part of GNU Mailutils
+ Copyright (C) 2009, 2010 Free Software Foundation, Inc.
+
+ This library is free software; you can redistribute it and/or
+ modify it under the terms of the GNU Lesser General Public
+ License as published by the Free Software Foundation; either
+ version 3 of the License, or (at your option) any later version.
+
+ This library 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
+ Lesser General Public License for more details.
+
+ You should have received a copy of the GNU Lesser General
+ Public License along with this library. If not, see
+ <http://www.gnu.org/licenses/>. */
+
+#ifdef HAVE_CONFIG_H
+# include <config.h>
+#endif
+#include <mailutils/cctype.h>
+
+int
+mu_strupper (char *s)
+{
+ if (!s)
+ return 0;
+ for (; *s; s++)
+ *s = mu_toupper (*s);
+ return 0;
+}
diff --git a/libmailutils/daemon.c b/libmailutils/daemon.c
new file mode 100644
index 000000000..b7fca61a5
--- /dev/null
+++ b/libmailutils/daemon.c
@@ -0,0 +1,189 @@
+/* GNU Mailutils -- a suite of utilities for electronic mail
+ Copyright (C) 2004, 2005, 2007, 2008, 2009, 2010 Free Software
+ Foundation, Inc.
+
+ This library is free software; you can redistribute it and/or
+ modify it under the terms of the GNU Lesser General Public
+ License as published by the Free Software Foundation; either
+ version 3 of the License, or (at your option) any later version.
+
+ This library 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
+ Lesser General Public License for more details.
+
+ You should have received a copy of the GNU Lesser General
+ Public License along with this library; if not, write to the
+ Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor,
+ Boston, MA 02110-1301 USA */
+
+#ifdef HAVE_CONFIG_H
+# include <config.h>
+#endif
+
+#include <stdio.h>
+#include <stdlib.h>
+#include <unistd.h>
+#include <fcntl.h>
+#include <sys/types.h>
+#include <sys/stat.h>
+#include <string.h>
+
+#include <mailutils/daemon.h>
+#include <mailutils/errno.h>
+#include <mailutils/error.h>
+#include <mailutils/nls.h>
+
+static char *pidfile;
+static pid_t current_pid;
+
+/* Return 0 if DIR is writable for EUID/EGID.
+ Otherwise, return error code. */
+static int
+ewraccess (const char *dir)
+{
+ struct stat st;
+ if (stat (dir, &st))
+ return errno;
+ if ((st.st_mode & S_IWOTH)
+ || (st.st_gid == getegid () && (st.st_mode & S_IWGRP))
+ || (st.st_uid == geteuid () && (st.st_mode & S_IWUSR)))
+ return 0;
+ else
+ return EACCES;
+}
+
+/* Return 0 if DIR is writable. If necessary and possible, raise to
+ EUID 0, in that case return prior EUID in the memory location pointed to
+ by PUID. */
+static int
+access_dir (const char *dir, uid_t *puid)
+{
+ int ec = ewraccess (dir);
+ if (ec)
+ {
+ if (ec == EACCES && access (dir, W_OK) == 0)
+ {
+ uid_t uid = geteuid ();
+ /* See if we can become root */
+ if (uid && getuid () == 0 && seteuid (0) == 0)
+ {
+ *puid = uid;
+ return 0;
+ }
+ }
+ }
+ return ec;
+}
+
+int
+mu_daemon_create_pidfile (const char *filename)
+{
+ char *p;
+ int fd;
+ uid_t uid = 0;
+ int rc;
+
+ if (filename[0] != '/')
+ return EINVAL;
+
+ if (pidfile)
+ free (pidfile);
+ pidfile = strdup (filename);
+ if (!pidfile)
+ return ENOMEM;
+
+ /* Determine the hosting directory name */
+ p = strrchr (pidfile, '/');
+ if (pidfile == p)
+ {
+ free (pidfile);
+ pidfile = NULL;
+ /* Sorry, pidfiles in root dir are not allowed */
+ return EINVAL;
+ }
+ /* Check if we have write access to the directory */
+ *p = 0;
+ rc = access_dir (pidfile, &uid);
+ if (rc)
+ {
+ /* Nope, clean up and return */
+ free (pidfile);
+ pidfile = NULL;
+ return rc;
+ }
+
+ /* Restore directory separator */
+ *p = '/';
+
+ unlink (pidfile);
+ current_pid = getpid ();
+
+ if ((fd = open (pidfile, O_WRONLY | O_CREAT | O_TRUNC | O_EXCL, 0644)) != -1)
+ {
+ FILE *fp = fdopen (fd, "w");
+ if (!fp)
+ {
+ rc = errno;
+ free (pidfile);
+ close (fd);
+ }
+ else
+ {
+ fprintf (fp, "%lu", (unsigned long) current_pid);
+ fclose (fp);
+ atexit (mu_daemon_remove_pidfile);
+ }
+ }
+ else
+ {
+ rc = errno;
+ free (pidfile);
+ pidfile = NULL;
+ }
+
+ /* Restore previous EUID value. */
+ if (uid)
+ seteuid (uid);
+
+ return rc;
+}
+
+void
+mu_daemon_remove_pidfile (void)
+{
+ if (getpid () == current_pid)
+ {
+ int rc;
+ uid_t uid = 0;
+
+ /* Determine the hosting directory name */
+ char *p = strrchr (pidfile, '/');
+ if (pidfile == p)
+ {
+ /* Should not happen */
+ abort ();
+ }
+ /* Check if we have write access to the directory */
+ *p = 0;
+ rc = access_dir (pidfile, &uid);
+ *p = '/';
+ if (rc == 0)
+ {
+ if (unlink (pidfile) && errno != ENOENT)
+ rc = errno;
+ else
+ rc = 0;
+ }
+
+ if (rc)
+ mu_error (_("cannot remove pidfile %s: %s"),
+ pidfile, mu_strerror (rc));
+
+ free (pidfile);
+ pidfile = NULL;
+ }
+}
+
+
+
diff --git a/libmailutils/date.c b/libmailutils/date.c
new file mode 100644
index 000000000..d667e345b
--- /dev/null
+++ b/libmailutils/date.c
@@ -0,0 +1,245 @@
+/* GNU Mailutils -- a suite of utilities for electronic mail
+ Copyright (C) 1999, 2000, 2001, 2002, 2007, 2009, 2010 Free Software
+ Foundation, Inc.
+
+ This library is free software; you can redistribute it and/or
+ modify it under the terms of the GNU Lesser General Public
+ License as published by the Free Software Foundation; either
+ version 3 of the License, or (at your option) any later version.
+
+ This library 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
+ Lesser General Public License for more details.
+
+ You should have received a copy of the GNU Lesser General
+ Public License along with this library; if not, write to the
+ Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor,
+ Boston, MA 02110-1301 USA */
+
+#ifdef HAVE_CONFIG_H
+# include <config.h>
+#endif
+#include <stdio.h>
+#include <stdlib.h>
+#include <string.h>
+#include <mailutils/mutil.h>
+#include <mailutils/cstr.h>
+
+#define SECS_PER_DAY 86400
+#define ADJUSTMENT -719162L
+
+static time_t
+jan1st (int year)
+{
+ year--; /* Do not consider the current year */
+ return year*365L
+ + year/4L /* Years divisible by 4 are leap years */
+ + year/400L /* Years divisible by 400 are always leap years */
+ - year/100L; /* Years divisible by 100 but not 400 aren't */
+}
+
+static int month_start[]=
+ { 0, 31, 59, 90, 120, 151, 181, 212, 243, 273, 304, 334, 365 };
+ /* Jan Feb Mar Apr May Jun Jul Aug Sep Oct Nov Dec
+ 31 28 31 30 31 30 31 31 30 31 30 31
+ */
+
+/* NOTE: ignore GCC warning. The precedence of operators is OK here */
+#define leap_year(y) ((y) % 4 == 0 && (y) % 100 != 0 || (y) % 400 == 0)
+
+static int
+dayofyear (time_t *pday, int year, int month, int day)
+{
+ int leap, month_days;
+
+ if (year < 0 || month < 0 || month > 11)
+ return -1;
+
+ leap = leap_year (year);
+
+ month_days = month_start[month + 1] - month_start[month]
+ + ((month == 2) ? leap : 0);
+
+ if (day < 0 || day > month_days)
+ return -1; /* Illegal Date */
+
+ if (month <= 2)
+ leap = 0;
+
+ *pday = month_start[month] + day + leap;
+ return 0;
+}
+
+
+/* Convert struct tm into time_t, taking into account timezone offset. */
+/* FIXME: It does not take DST into account */
+time_t
+mu_tm2time (struct tm *tm, mu_timezone *tz)
+{
+ time_t t;
+
+ if (dayofyear (&t, tm->tm_year, tm->tm_mon, tm->tm_mday - 1))
+ return -1;
+ t = (t + ADJUSTMENT + jan1st (1900 + tm->tm_year)) * SECS_PER_DAY
+ + (tm->tm_hour * 60 + tm->tm_min) * 60 + tm->tm_sec
+ - tz->utc_offset;
+ return t;
+}
+
+/* Convert time 0 at UTC to our localtime, that tells us the offset
+ of our current timezone from UTC. */
+time_t
+mu_utc_offset (void)
+{
+ time_t t = 0;
+ struct tm *tm = gmtime (&t);
+
+ return - mktime (tm);
+}
+
+static const char *months[] =
+{
+ "Jan", "Feb", "Mar", "Apr", "May", "Jun",
+ "Jul", "Aug", "Sep", "Oct", "Nov", "Dec", NULL
+};
+
+static const char *wdays[] =
+{
+ "Sun", "Mon", "Tue", "Wed", "Thu", "Fri", "Sat", NULL
+};
+
+int
+mu_parse_imap_date_time (const char **p, struct tm *tm, mu_timezone *tz)
+{
+ int year, mon, day, hour, min, sec;
+ char zone[6] = "+0000"; /* ( "+" / "-" ) hhmm */
+ char month[5] = "";
+ int hh = 0;
+ int mm = 0;
+ int sign = 1;
+ int scanned = 0, scanned3;
+ int i;
+ int tzoffset;
+
+ day = mon = year = hour = min = sec = 0;
+
+ memset (tm, 0, sizeof (*tm));
+
+ switch (sscanf (*p,
+ "%2d-%3s-%4d%n %2d:%2d:%2d %5s%n",
+ &day, month, &year, &scanned3, &hour, &min, &sec, zone,
+ &scanned))
+ {
+ case 3:
+ scanned = scanned3;
+ break;
+ case 7:
+ break;
+ default:
+ return -1;
+ }
+
+ tm->tm_sec = sec;
+ tm->tm_min = min;
+ tm->tm_hour = hour;
+ tm->tm_mday = day;
+
+ for (i = 0; i < 12; i++)
+ {
+ if (mu_c_strncasecmp (month, months[i], 3) == 0)
+ {
+ mon = i;
+ break;
+ }
+ }
+ tm->tm_mon = mon;
+ tm->tm_year = (year > 1900) ? year - 1900 : year;
+ tm->tm_yday = 0; /* unknown. */
+ tm->tm_wday = 0; /* unknown. */
+#if HAVE_STRUCT_TM_TM_ISDST
+ tm->tm_isdst = -1; /* unknown. */
+#endif
+
+ hh = (zone[1] - '0') * 10 + (zone[2] - '0');
+ mm = (zone[3] - '0') * 10 + (zone[4] - '0');
+ sign = (zone[0] == '-') ? -1 : +1;
+ tzoffset = sign * (hh * 60 * 60 + mm * 60);
+
+#if HAVE_STRUCT_TM_TM_GMTOFF
+ tm->tm_gmtoff = tzoffset;
+#endif
+
+ if (tz)
+ {
+ tz->utc_offset = tzoffset;
+ tz->tz_name = NULL;
+ }
+
+ *p += scanned;
+
+ return 0;
+}
+
+/* "ctime" format is: Thu Jul 01 15:58:27 1999, with no trailing \n. */
+int
+mu_parse_ctime_date_time (const char **p, struct tm *tm, mu_timezone * tz)
+{
+ int wday = 0;
+ int year = 0;
+ int mon = 0;
+ int day = 0;
+ int hour = 0;
+ int min = 0;
+ int sec = 0;
+ int n = 0;
+ int i;
+ char weekday[5] = "";
+ char month[5] = "";
+
+ if (sscanf (*p, "%3s %3s %2d %2d:%2d:%2d %d%n\n",
+ weekday, month, &day, &hour, &min, &sec, &year, &n) != 7)
+ return -1;
+
+ *p += n;
+
+ for (i = 0; i < 7; i++)
+ {
+ if (mu_c_strncasecmp (weekday, wdays[i], 3) == 0)
+ {
+ wday = i;
+ break;
+ }
+ }
+
+ for (i = 0; i < 12; i++)
+ {
+ if (mu_c_strncasecmp (month, months[i], 3) == 0)
+ {
+ mon = i;
+ break;
+ }
+ }
+
+ if (tm)
+ {
+ memset (tm, 0, sizeof (struct tm));
+
+ tm->tm_sec = sec;
+ tm->tm_min = min;
+ tm->tm_hour = hour;
+ tm->tm_mday = day;
+ tm->tm_wday = wday;
+ tm->tm_mon = mon;
+ tm->tm_year = (year > 1900) ? year - 1900 : year;
+#ifdef HAVE_STRUCT_TM_TM_ISDST
+ tm->tm_isdst = -1; /* unknown. */
+#endif
+ }
+
+ /* ctime has no timezone information, set tz to UTC if they ask. */
+ if (tz)
+ memset (tz, 0, sizeof (struct mu_timezone));
+
+ return 0;
+}
diff --git a/libmailutils/dbgstderr.c b/libmailutils/dbgstderr.c
new file mode 100644
index 000000000..9a2e16441
--- /dev/null
+++ b/libmailutils/dbgstderr.c
@@ -0,0 +1,36 @@
+/* GNU Mailutils -- a suite of utilities for electronic mail
+ Copyright (C) 1999, 2000, 2001, 2004, 2005, 2007, 2010 Free Software
+ Foundation, Inc.
+
+ This library is free software; you can redistribute it and/or
+ modify it under the terms of the GNU Lesser General Public
+ License as published by the Free Software Foundation; either
+ version 3 of the License, or (at your option) any later version.
+
+ This library 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
+ Lesser General Public License for more details.
+
+ You should have received a copy of the GNU Lesser General
+ Public License along with this library; if not, write to the
+ Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor,
+ Boston, MA 02110-1301 USA */
+
+#ifdef HAVE_CONFIG_H
+# include <config.h>
+#endif
+
+#include <mailutils/debug.h>
+#include <stdio.h>
+
+int
+mu_debug_stderr_printer (void *unused, mu_log_level_t level, const char *str)
+{
+ fprintf (stderr, "%s: %s",
+ (level == MU_DEBUG_ERROR) ? "ERROR" : "DEBUG",
+ str);
+ return 0;
+}
+
+
diff --git a/libmailutils/dbgstream.c b/libmailutils/dbgstream.c
new file mode 100644
index 000000000..b03f29915
--- /dev/null
+++ b/libmailutils/dbgstream.c
@@ -0,0 +1,86 @@
+/* GNU Mailutils -- a suite of utilities for electronic mail
+ Copyright (C) 1999, 2000, 2001, 2002, 2004,
+ 2005, 2006, 2007, 2008, 2009, 2010 Free Software Foundation, Inc.
+
+ This library is free software; you can redistribute it and/or modify
+ it under the terms of the GNU Lesser General Public License as published by
+ the Free Software Foundation; either version 3, or (at your option)
+ any later version.
+
+ This library 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 Lesser General Public License for more details.
+
+ You should have received a copy of the GNU Lesser General Public License
+ along with GNU Mailutils. If not, see <http://www.gnu.org/licenses/>. */
+
+#ifdef HAVE_CONFIG_H
+# include <config.h>
+#endif
+#include <stdlib.h>
+#include <string.h>
+#include <errno.h>
+
+#include <mailutils/types.h>
+#include <mailutils/alloc.h>
+#include <mailutils/errno.h>
+
+#include <mailutils/nls.h>
+#include <mailutils/stream.h>
+#include <mailutils/sys/stream.h>
+#include <mailutils/sys/dbgstream.h>
+#include <mailutils/debug.h>
+
+static int
+_dbg_write (struct _mu_stream *str, const char *buf, size_t size,
+ size_t *pnwrite)
+{
+ struct _mu_dbgstream *sp = (struct _mu_dbgstream *)str;
+ if (pnwrite)
+ *pnwrite = size;
+ while (size > 0 && (buf[size-1] == '\n' || buf[size-1] == '\r'))
+ size--;
+ if (size)
+ mu_debug_printf (sp->debug, sp->level, "%.*s\n", size, buf);
+ return 0;
+}
+
+static int
+_dbg_flush (struct _mu_stream *str)
+{
+ struct _mu_dbgstream *sp = (struct _mu_dbgstream *)str;
+ mu_debug_printf (sp->debug, sp->level, "\n");
+ return 0;
+}
+
+static void
+_dbg_done (struct _mu_stream *str)
+{
+ struct _mu_dbgstream *sp = (struct _mu_dbgstream *)str;
+ if (str->flags & MU_STREAM_AUTOCLOSE)
+ mu_debug_destroy (&sp->debug, NULL);
+}
+
+int
+mu_dbgstream_create(mu_stream_t *pref, mu_debug_t debug, mu_log_level_t level,
+ int flags)
+{
+ struct _mu_dbgstream *sp;
+
+ sp = (struct _mu_dbgstream *)
+ _mu_stream_create (sizeof (*sp), MU_STREAM_WRITE |
+ (flags & MU_STREAM_AUTOCLOSE));
+ if (!sp)
+ return ENOMEM;
+ sp->stream.write = _dbg_write;
+ sp->stream.flush = _dbg_flush;
+ sp->stream.done = _dbg_done;
+
+ sp->debug = debug;
+ sp->level = level;
+ mu_stream_set_buffer ((mu_stream_t) sp, mu_buffer_line, 1024);
+ *pref = (mu_stream_t) sp;
+ return 0;
+}
+
diff --git a/libmailutils/dbgsyslog.c b/libmailutils/dbgsyslog.c
new file mode 100644
index 000000000..f906cd3a5
--- /dev/null
+++ b/libmailutils/dbgsyslog.c
@@ -0,0 +1,34 @@
+/* GNU Mailutils -- a suite of utilities for electronic mail
+ Copyright (C) 1999, 2000, 2001, 2004, 2005, 2007, 2010 Free Software
+ Foundation, Inc.
+
+ This library is free software; you can redistribute it and/or
+ modify it under the terms of the GNU Lesser General Public
+ License as published by the Free Software Foundation; either
+ version 3 of the License, or (at your option) any later version.
+
+ This library 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
+ Lesser General Public License for more details.
+
+ You should have received a copy of the GNU Lesser General
+ Public License along with this library; if not, write to the
+ Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor,
+ Boston, MA 02110-1301 USA */
+
+#ifdef HAVE_CONFIG_H
+# include <config.h>
+#endif
+
+#include <mailutils/debug.h>
+#include <syslog.h>
+
+int
+mu_debug_syslog_printer (void *unused, mu_log_level_t level, const char *str)
+{
+ syslog ((level == MU_DEBUG_ERROR) ? LOG_ERR : LOG_DEBUG, "%s", str);
+ return 0;
+}
+
+
diff --git a/libmailutils/debug.c b/libmailutils/debug.c
new file mode 100644
index 000000000..7570f2387
--- /dev/null
+++ b/libmailutils/debug.c
@@ -0,0 +1,298 @@
+/* GNU Mailutils -- a suite of utilities for electronic mail
+ Copyright (C) 1999, 2000, 2001, 2004, 2005, 2007, 2008, 2010 Free
+ Software Foundation, Inc.
+
+ This library is free software; you can redistribute it and/or
+ modify it under the terms of the GNU Lesser General Public
+ License as published by the Free Software Foundation; either
+ version 3 of the License, or (at your option) any later version.
+
+ This library 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
+ Lesser General Public License for more details.
+
+ You should have received a copy of the GNU Lesser General
+ Public License along with this library; if not, write to the
+ Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor,
+ Boston, MA 02110-1301 USA */
+
+#ifdef HAVE_CONFIG_H
+# include <config.h>
+#endif
+
+#include <errno.h>
+#include <stdlib.h>
+#include <stdio.h>
+#include <string.h>
+
+#include <mailutils/errno.h>
+#include <mailutils/nls.h>
+#include <mailutils/sys/debug.h>
+
+mu_debug_printer_fp mu_debug_default_printer = mu_debug_stderr_printer;
+
+int
+mu_debug_create (mu_debug_t *pdebug, void *owner)
+{
+ mu_debug_t debug;
+ if (pdebug == NULL)
+ return MU_ERR_OUT_PTR_NULL;
+ debug = calloc (sizeof (*debug), 1);
+ if (debug == NULL)
+ return ENOMEM;
+ debug->printer = NULL;
+ debug->owner = owner;
+ *pdebug = debug;
+ return 0;
+}
+
+void
+mu_debug_destroy (mu_debug_t *pdebug, void *owner)
+{
+ if (pdebug && *pdebug)
+ {
+ mu_debug_t debug = *pdebug;
+ if (debug->owner == owner)
+ {
+ if (debug->stream)
+ {
+ mu_off_t len = 0;
+ int rc = mu_stream_size (debug->stream, &len);
+ if (rc == 0 && len)
+ /* Flush leftover data */
+ mu_debug_printf (debug, 0, "\n");
+
+ mu_stream_destroy (&debug->stream);
+ }
+ if (debug->destroy)
+ debug->destroy (debug->data);
+ free (*pdebug);
+ *pdebug = NULL;
+ }
+ }
+}
+
+void *
+mu_debug_get_owner (mu_debug_t debug)
+{
+ return (debug) ? debug->owner : NULL;
+}
+
+int
+mu_debug_set_level (mu_debug_t debug, mu_log_level_t level)
+{
+ if (debug == NULL)
+ return EINVAL;
+ debug->level = level;
+ return 0;
+}
+
+int
+mu_debug_get_level (mu_debug_t debug, mu_log_level_t *plevel)
+{
+ if (debug == NULL)
+ return EINVAL;
+ if (plevel)
+ *plevel = debug->level;
+ return 0;
+}
+
+int
+mu_debug_set_print (mu_debug_t debug, mu_debug_printer_fp printer, void *owner)
+{
+ if (debug == NULL)
+ return EINVAL;
+ if (debug->owner != owner)
+ return EACCES;
+ debug->printer = printer;
+ return 0;
+}
+
+int
+mu_debug_set_data (mu_debug_t debug, void *data, void (*destroy) (void*),
+ void *owner)
+{
+ if (!debug)
+ return EINVAL;
+ if (debug->owner != owner)
+ return EACCES;
+ debug->data = data;
+ debug->destroy = destroy;
+ return 0;
+}
+
+static void
+debug_format_prefix (mu_debug_t debug)
+{
+ int need_space = 0;
+ if (debug->locus.file)
+ {
+ mu_stream_printf (debug->stream, "%s:%d:",
+ debug->locus.file, debug->locus.line);
+ need_space = 1;
+ }
+
+ if (debug->function)
+ {
+ mu_stream_printf (debug->stream, "%s:", debug->function);
+ need_space = 1;
+ }
+
+ if (need_space)
+ mu_stream_write (debug->stream, " ", 1, NULL);
+}
+
+int
+mu_debug_vprintf (mu_debug_t debug, mu_log_level_t level,
+ const char *format, va_list ap)
+{
+ mu_debug_printer_fp printer;
+
+ if (debug == NULL || format == NULL)
+ return EINVAL;
+
+ printer = debug->printer ? debug->printer : mu_debug_default_printer;
+ if (printer)
+ {
+ mu_off_t len;
+ mu_transport_t tbuf[2];
+ char *ptr, *start, *p;
+ size_t nseg;
+
+ if (debug->stream == NULL)
+ {
+ int rc = mu_memory_stream_create (&debug->stream, 0);
+ if (rc)
+ {
+ fprintf (stderr,
+ _("cannot create memory stream for debugging "
+ "output: %s\n"),
+ mu_strerror (rc));
+ vfprintf (stderr, format, ap);
+ return rc;
+ }
+ }
+
+ if (mu_stream_size (debug->stream, &len) == 0 && len == 0)
+ debug_format_prefix (debug);
+
+ mu_stream_vprintf (debug->stream, format, ap);
+
+ mu_stream_ioctl (debug->stream, MU_IOCTL_GET_TRANSPORT, tbuf);
+ start = (char*) tbuf[0];
+ mu_stream_size (debug->stream, &len);
+ ptr = start;
+ nseg = 0;
+ for (p = ptr = start; p < start + len; p++)
+ {
+ if (*p == '\n')
+ {
+ int c = *++p;
+ *p = 0;
+ printer (debug->data, level, ptr);
+ *p = c;
+ ptr = p;
+ nseg++;
+ }
+ }
+
+ if (nseg)
+ {
+ if (start[len - 1] != '\n')
+ {
+ size_t s = len - (ptr - start);
+ memmove (start, ptr, s);
+ }
+ else
+ len = 0;
+
+ mu_stream_truncate (debug->stream, len);
+ mu_stream_seek (debug->stream, len, SEEK_SET, NULL);
+ }
+ }
+ else
+ vfprintf (stderr, format, ap);
+
+ return 0;
+}
+
+int
+mu_debug_printf (mu_debug_t debug, mu_log_level_t level,
+ const char *format, ...)
+{
+ va_list ap;
+
+ va_start (ap, format);
+ mu_debug_vprintf (debug, level, format, ap);
+ va_end (ap);
+ return 0;
+}
+
+
+int
+mu_debug_print (mu_debug_t debug, mu_log_level_t level,
+ const char *format, ...)
+{
+ va_list ap;
+ mu_debug_printv (debug, level, format, ap);
+ va_end (ap);
+ return 0;
+}
+
+int
+mu_debug_printv (mu_debug_t debug, mu_log_level_t level, const char *format,
+ va_list ap)
+{
+ if (debug == NULL || format == NULL)
+ return EINVAL;
+ if (debug->level & MU_DEBUG_LEVEL_MASK (level))
+ return 0;
+ return mu_debug_vprintf (debug, level, format, ap);
+}
+
+int
+mu_debug_check_level (mu_debug_t debug, mu_log_level_t level)
+{
+ if (!debug)
+ return 0;
+ return debug->level & MU_DEBUG_LEVEL_MASK (level);
+}
+
+int
+mu_debug_set_locus (mu_debug_t debug, const char *file, int line)
+{
+ if (!debug)
+ return EINVAL;
+ debug->locus.file = file;
+ debug->locus.line = line;
+ return 0;
+}
+
+int
+mu_debug_get_locus (mu_debug_t debug, struct mu_debug_locus *ploc)
+{
+ if (!debug)
+ return EINVAL;
+ *ploc = debug->locus;
+ return 0;
+}
+
+int
+mu_debug_set_function (mu_debug_t debug, const char *function)
+{
+ if (!debug)
+ return EINVAL;
+ debug->function = function;
+ return 0;
+}
+
+int
+mu_debug_get_function (mu_debug_t debug, const char **pfunction)
+{
+ if (!debug)
+ return EINVAL;
+ *pfunction = debug->function;
+ return 0;
+}
+
diff --git a/libmailutils/diag.c b/libmailutils/diag.c
new file mode 100644
index 000000000..7497c82c0
--- /dev/null
+++ b/libmailutils/diag.c
@@ -0,0 +1,179 @@
+/* GNU Mailutils -- a suite of utilities for electronic mail
+ Copyright (C) 1999, 2000, 2001, 2005, 2007, 2009, 2010 Free Software
+ Foundation, Inc.
+
+ This library is free software; you can redistribute it and/or
+ modify it under the terms of the GNU Lesser General Public
+ License as published by the Free Software Foundation; either
+ version 3 of the License, or (at your option) any later version.
+
+ This library 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
+ Lesser General Public License for more details.
+
+ You should have received a copy of the GNU Lesser General
+ Public License along with this library; if not, write to the
+ Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor,
+ Boston, MA 02110-1301 USA */
+
+#ifdef HAVE_CONFIG_H
+# include <config.h>
+#endif
+
+#include <stdlib.h>
+#include <stdio.h>
+#include <syslog.h>
+#include <string.h>
+#include <mailutils/diag.h>
+#include <mailutils/nls.h>
+#include <mailutils/errno.h>
+
+const char *mu_program_name;
+mu_debug_t mu_diag_debug;
+
+void
+mu_set_program_name (const char *name)
+{
+ const char *progname;
+
+ if (!name)
+ progname = name;
+ else
+ {
+ progname = strrchr (name, '/');
+ if (progname)
+ progname++;
+ else
+ progname = name;
+
+ if (strlen (progname) > 3 && memcmp (progname, "lt-", 3) == 0)
+ progname += 3;
+ }
+
+ mu_program_name = progname;
+}
+
+void
+mu_diag_init ()
+{
+ if (!mu_diag_debug)
+ {
+ int rc = mu_debug_create (&mu_diag_debug, NULL);
+ if (rc)
+ {
+ fprintf (stderr,
+ _("cannot initialize debug object for diagnostics: %s\n"),
+ mu_strerror (rc));
+ /* That's a fatal error */
+ abort ();
+ }
+ mu_debug_set_print (mu_diag_debug, mu_diag_stderr_printer, NULL);
+ }
+}
+
+void
+mu_diag_get_debug (mu_debug_t *pdebug)
+{
+ mu_diag_init ();
+ *pdebug = mu_diag_debug;
+}
+
+void
+mu_diag_set_debug (mu_debug_t debug)
+{
+ if (mu_diag_debug)
+ mu_debug_destroy (&mu_diag_debug, NULL);
+ mu_diag_debug = debug;
+}
+
+void
+mu_diag_vprintf (mu_log_level_t level, const char *fmt, va_list ap)
+{
+ mu_diag_init ();
+ mu_debug_vprintf (mu_diag_debug, level, fmt, ap);
+}
+
+void
+mu_diag_printf (mu_log_level_t level, const char *fmt, ...)
+{
+ va_list ap;
+ va_start (ap, fmt);
+ mu_diag_vprintf (level, fmt, ap);
+ va_end (ap);
+}
+
+void
+mu_diag_voutput (mu_log_level_t level, const char *fmt, va_list ap)
+{
+ mu_diag_init ();
+ mu_debug_vprintf (mu_diag_debug, level, fmt, ap);
+ mu_debug_printf (mu_diag_debug, level, "\n");
+}
+
+void
+mu_diag_output (mu_log_level_t level, const char *fmt, ...)
+{
+ va_list ap;
+ va_start (ap, fmt);
+ mu_diag_voutput (level, fmt, ap);
+ va_end (ap);
+}
+
+const char *
+mu_diag_level_to_string (mu_log_level_t level)
+{
+ switch (level)
+ {
+ case MU_DIAG_EMERG:
+ return _("emergency");
+
+ case MU_DIAG_ALERT:
+ return _("alert");
+
+ case MU_DIAG_CRIT:
+ return _("critical");
+
+ case MU_DIAG_ERROR:
+ return _("error");
+
+ case MU_DIAG_WARNING:
+ return _("warning");
+
+ case MU_DIAG_NOTICE:
+ return _("notice");
+
+ case MU_DIAG_INFO:
+ return _("info");
+
+ case MU_DIAG_DEBUG:
+ return _("debug");
+ }
+ return _("unknown");
+}
+
+int
+mu_diag_stderr_printer (void *data, mu_log_level_t level, const char *buf)
+{
+ if (mu_program_name)
+ fprintf (stderr, "%s: ", mu_program_name);
+ if (level != MU_DIAG_ERROR)
+ fprintf (stderr, "%s: ", mu_diag_level_to_string (level));
+ fputs (buf, stderr);
+ return 0;
+}
+
+void
+mu_diag_funcall (mu_log_level_t level, const char *func,
+ const char *arg, int err)
+{
+ if (err)
+ /* TRANSLATORS: First %s stands for function name, second for its
+ arguments, third one for the actual error message. */
+ mu_diag_output (level, _("%s(%s) failed: %s"), func, arg ? arg : "",
+ mu_strerror (err));
+ else
+ /* TRANSLATORS: First %s stands for function name, second for its
+ arguments. */
+ mu_diag_output (level, _("%s(%s) failed"), func, arg ? arg : "");
+}
diff --git a/libmailutils/dot.c b/libmailutils/dot.c
new file mode 100644
index 000000000..a0d43fad7
--- /dev/null
+++ b/libmailutils/dot.c
@@ -0,0 +1,268 @@
+/* GNU Mailutils -- a suite of utilities for electronic mail
+ Copyright (C) 2003, 2007, 2010 Free Software Foundation, Inc.
+
+ This library is free software; you can redistribute it and/or
+ modify it under the terms of the GNU Lesser General Public
+ License as published by the Free Software Foundation; either
+ version 3 of the License, or (at your option) any later version.
+
+ This library 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
+ Lesser General Public License for more details.
+
+ You should have received a copy of the GNU Lesser General
+ Public License along with this library; if not, write to the
+ Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor,
+ Boston, MA 02110-1301 USA */
+
+/* This file implements a DOT filter, useful for data I/O in
+ such protocols as POP3 and SMTP. When encoding, this filter
+ "byte-stuffs" the input by outputting an additional '.' in front
+ of any '.' appearing at the beginning of a line. Upon closing the
+ filter in this mode, it outputs additional ".\n".
+
+ When decoding, the reverse is performed: any '.' appearing at the
+ beginning of a line is removed. A single dot on a line by itself
+ marks end of the stream.
+
+ MU also provides a hairy version of this filter, called CRLFDOT.
+ In addition to byte-stuffing, CRLFDOT also performs CRLF/LF translation.
+ */
+
+#ifdef HAVE_CONFIG_H
+# include <config.h>
+#endif
+
+#include <stdlib.h>
+#include <string.h>
+#include <mailutils/errno.h>
+#include <mailutils/filter.h>
+#include <mailutils/stream.h>
+
+enum dot_decode_state
+ {
+ dot_decode_init, /* initial state */
+ dot_decode_char, /* Any character excepting [\r\n.] */
+ dot_decode_lf, /* prev. char was \n */
+ dot_decode_dot, /* 2 prev. chars were \n. */
+ dot_decode_end /* final state, a \n.\n seen. */
+ };
+
+static enum dot_decode_state
+new_decode_state (enum dot_decode_state state, int c)
+{
+ switch (state)
+ {
+ case dot_decode_init:
+ switch (c)
+ {
+ case '.':
+ return dot_decode_dot;
+ }
+ break;
+
+ case dot_decode_char:
+ switch (c)
+ {
+ case '\n':
+ return dot_decode_lf;
+ }
+ break;
+
+ case dot_decode_lf:
+ switch (c)
+ {
+ case '.':
+ return dot_decode_dot;
+ }
+
+ case dot_decode_dot:
+ switch (c)
+ {
+ case '\n':
+ return dot_decode_end;
+ }
+ break;
+
+ case dot_decode_end:
+ return dot_decode_end;
+ }
+ return dot_decode_char;
+}
+
+/* Move min(isize,osize) bytes from iptr to optr, replacing each \r\n
+ with \n. */
+static enum mu_filter_result
+_dot_decoder (void *xd,
+ enum mu_filter_command cmd,
+ struct mu_filter_io *iobuf)
+{
+ int *pstate = xd;
+ size_t i, j;
+ const unsigned char *iptr;
+ size_t isize;
+ char *optr;
+ size_t osize;
+
+ switch (cmd)
+ {
+ case mu_filter_init:
+ *pstate = dot_decode_init;
+ return mu_filter_ok;
+
+ case mu_filter_done:
+ return mu_filter_ok;
+
+ default:
+ break;
+ }
+
+ iptr = (const unsigned char *) iobuf->input;
+ isize = iobuf->isize;
+ optr = iobuf->output;
+ osize = iobuf->osize;
+
+ for (i = j = 0; *pstate != dot_decode_end && i < isize && j < osize; i++)
+ {
+ unsigned char c = *iptr++;
+ int curstate = *pstate;
+
+ *pstate = new_decode_state (curstate, c);
+ if (!(c == '.' && (curstate == dot_decode_init ||
+ curstate == dot_decode_lf)))
+ optr[j++] = c;
+ }
+
+ if (*pstate == dot_decode_end)
+ iobuf->eof = 1;
+
+ iobuf->isize = i;
+ iobuf->osize = j;
+ return mu_filter_ok;
+}
+
+enum dot_encode_state
+ {
+ dot_encode_init, /* initial state */
+ dot_encode_char, /* Any character excepting \n */
+ dot_encode_lf, /* prev. char was \n */
+ };
+
+static enum dot_encode_state
+new_encode_state (int c)
+{
+ switch (c)
+ {
+ case '\n':
+ return dot_encode_lf;
+ }
+ return dot_encode_char;
+}
+
+/* Move min(isize,osize) bytes from iptr to optr, replacing each \n
+ with \r\n. Any input \r\n sequences remain untouched. */
+static enum mu_filter_result
+_dot_encoder (void *xd,
+ enum mu_filter_command cmd,
+ struct mu_filter_io *iobuf)
+{
+ enum mu_filter_result result;
+ size_t i, j;
+ const unsigned char *iptr;
+ size_t isize;
+ char *optr;
+ size_t osize;
+ int *state = xd;
+
+ switch (cmd)
+ {
+ case mu_filter_init:
+ *state = dot_encode_init;
+ return mu_filter_ok;
+
+ case mu_filter_done:
+ return mu_filter_ok;
+
+ default:
+ break;
+ }
+
+ iptr = (const unsigned char *) iobuf->input;
+ isize = iobuf->isize;
+ optr = iobuf->output;
+ osize = iobuf->osize;
+
+ for (i = j = 0; i < isize && j < osize; i++, iptr++)
+ {
+ unsigned char c = *iptr;
+ int curstate = *state;
+
+ if (c == '.' && (curstate == dot_encode_init ||
+ curstate == dot_encode_lf))
+ {
+ if (j + 2 > osize)
+ {
+ if (i == 0)
+ {
+ iobuf->osize = 2;
+ return mu_filter_moreoutput;
+ }
+ break;
+ }
+ optr[j++] = '.';
+ optr[j++] = '.';
+ }
+ else
+ optr[j++] = c;
+
+ *state = new_encode_state (c);
+ }
+
+ result = mu_filter_ok;
+ if (cmd == mu_filter_lastbuf)
+ {
+ switch (*state)
+ {
+ case dot_encode_lf:
+ if (j + 2 > osize)
+ result = mu_filter_again;
+ break;
+
+ default:
+ if (j + 3 > osize)
+ result = mu_filter_again;
+ else
+ optr[j++] = '\n';
+ }
+
+ if (result == mu_filter_ok)
+ {
+ optr[j++] = '.';
+ optr[j++] = '\n';
+ }
+ }
+
+ iobuf->isize = i;
+ iobuf->osize = j;
+ return result;
+}
+
+static int
+alloc_state (void **pret, int mode MU_ARG_UNUSED, void *data MU_ARG_UNUSED)
+{
+ *pret = malloc (sizeof (int));
+ if (!*pret)
+ return ENOMEM;
+ return 0;
+}
+
+static struct _mu_filter_record _dot_filter = {
+ "DOT",
+ 0,
+ alloc_state,
+ _dot_encoder,
+ _dot_decoder
+};
+
+mu_filter_record_t mu_dot_filter = &_dot_filter;
diff --git a/libmailutils/envelope.c b/libmailutils/envelope.c
new file mode 100644
index 000000000..1e5aa4aee
--- /dev/null
+++ b/libmailutils/envelope.c
@@ -0,0 +1,181 @@
+/* GNU Mailutils -- a suite of utilities for electronic mail
+ Copyright (C) 1999, 2000, 2004, 2005, 2007, 2010 Free Software
+ Foundation, Inc.
+
+ This library is free software; you can redistribute it and/or
+ modify it under the terms of the GNU Lesser General Public
+ License as published by the Free Software Foundation; either
+ version 3 of the License, or (at your option) any later version.
+
+ This library 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
+ Lesser General Public License for more details.
+
+ You should have received a copy of the GNU Lesser General
+ Public License along with this library; if not, write to the
+ Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor,
+ Boston, MA 02110-1301 USA */
+
+#ifdef HAVE_CONFIG_H
+# include <config.h>
+#endif
+#include <errno.h>
+#include <stdlib.h>
+#include <string.h>
+#include <mailutils/errno.h>
+#include <mailutils/mutil.h>
+#include <mailutils/sys/envelope.h>
+
+int
+mu_envelope_create (mu_envelope_t *penvelope, void *owner)
+{
+ mu_envelope_t envelope;
+ if (penvelope == NULL)
+ return MU_ERR_OUT_PTR_NULL;
+ envelope = calloc (1, sizeof (*envelope));
+ if (envelope == NULL)
+ return ENOMEM;
+ envelope->owner = owner;
+ *penvelope = envelope;
+ return 0;
+}
+
+void
+mu_envelope_destroy (mu_envelope_t *penvelope, void *owner)
+{
+ if (penvelope && *penvelope)
+ {
+ mu_envelope_t envelope = *penvelope;
+ if (envelope->owner == owner)
+ {
+ if (envelope->_destroy)
+ envelope->_destroy (envelope);
+ free (envelope->date);
+ free (envelope->sender);
+ free (envelope);
+ }
+ *penvelope = NULL;
+ }
+}
+
+void *
+mu_envelope_get_owner (mu_envelope_t envelope)
+{
+ return (envelope) ? envelope->owner : NULL;
+}
+
+int
+mu_envelope_set_sender (mu_envelope_t envelope,
+ int (*_sender) (mu_envelope_t, char *, size_t,
+ size_t*),
+ void *owner)
+{
+ if (envelope == NULL)
+ return EINVAL;
+ if (envelope->owner != owner)
+ return EACCES;
+ envelope->_get_sender = _sender;
+ return 0;
+}
+
+int
+mu_envelope_set_date (mu_envelope_t envelope,
+ int (*_date) (mu_envelope_t, char *, size_t , size_t *),
+ void *owner)
+{
+ if (envelope == NULL)
+ return EINVAL;
+ if (envelope->owner != owner)
+ return EACCES;
+ envelope->_get_date = _date;
+ return 0;
+}
+
+
+/* General accessors: */
+#define AC2(a,b) a ## b
+#define AC4(a,b,c,d) a ## b ## c ## d
+#define ACCESSOR(action,field) AC4(mu_envelope_,action,_,field)
+
+#define DECL_SGET(field) \
+int \
+ACCESSOR(sget,field) (mu_envelope_t env, char const **sptr) \
+{ \
+ if (env == NULL) \
+ return EINVAL; \
+ if (!env->field) \
+ { \
+ if (env->AC2(_get_,field)) \
+ { \
+ size_t n; \
+ char *buf; \
+ int status; \
+ \
+ status = env->AC2(_get_,field) (env, NULL, 0, &n); \
+ if (status) \
+ return status; \
+ \
+ buf = malloc (n + 1); \
+ if (!buf) \
+ return ENOMEM; \
+ \
+ status = env->AC2(_get_,field) (env, buf, n + 1, NULL); \
+ if (status) \
+ return status; \
+ \
+ env->field = buf; \
+ } \
+ else \
+ return MU_ERR_NOENT; \
+ } \
+ *sptr = env->field; \
+ return 0; \
+}
+
+#define DECL_GET(field) \
+int \
+ACCESSOR(get,field) (mu_envelope_t env, char *buf, size_t len, size_t *n) \
+{ \
+ size_t i; \
+ const char *str; \
+ int status = ACCESSOR(sget, field) (env, &str); \
+ \
+ if (status) \
+ return status; \
+ \
+ i = mu_cpystr (buf, str, len); \
+ if (n) \
+ *n = i; \
+ return 0; \
+}
+
+#define DECL_AGET(field) \
+int \
+ACCESSOR(aget, field) (mu_envelope_t env, char **buf) \
+{ \
+ const char *str; \
+ int status = ACCESSOR(sget, field) (env, &str); \
+ \
+ if (status) \
+ return status; \
+ \
+ if (str) \
+ { \
+ *buf = strdup (str); \
+ if (!*buf) \
+ status = ENOMEM; \
+ } \
+ else \
+ *buf = NULL; \
+ return status; \
+}
+
+#define DECL_ACCESSORS(field) \
+DECL_SGET(field) \
+DECL_GET(field) \
+DECL_AGET(field)
+
+DECL_ACCESSORS(sender)
+DECL_ACCESSORS(date)
+
diff --git a/libmailutils/errors b/libmailutils/errors
new file mode 100644
index 000000000..06eb98b48
--- /dev/null
+++ b/libmailutils/errors
@@ -0,0 +1,87 @@
+# Error messages for GNU Mailutils
+# Copyright (C) 2005, 2006, 2007, 2010 Free Software Foundation, Inc.
+#
+# This library is free software; you can redistribute it and/or
+# modify it under the terms of the GNU Lesser General Public
+# License as published by the Free Software Foundation; either
+# version 3 of the License, or (at your option) any later version.
+#
+# This library 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
+# Lesser General Public License for more details.
+#
+# You should have received a copy of the GNU Lesser General Public
+# License along with this library; if not, write to the Free Software
+# Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston,
+# MA 02110-1301 USA
+
+MU_ERR_FAILURE _("Operation failed")
+MU_ERR_CANCELED _("Operation canceled")
+
+MU_ERR_NO_HANDLER _("No registered handler")
+MU_ERR_EMPTY_VFN _("Empty virtual function")
+
+MU_ERR_OUT_NULL _("Pointer to output null")
+MU_ERR_OUT_PTR_NULL _("Pointer to output pointer null")
+
+MU_ERR_MBX_NULL _("Mailbox null")
+
+MU_ERR_BAD_822_FORMAT _("Format of RFC822 object is bad")
+MU_ERR_EMPTY_ADDRESS _("Address contains no addr specs")
+
+MU_ERR_LOCKER_NULL _("Locker null")
+MU_ERR_LOCK_CONFLICT _("Conflict with previous locker")
+MU_ERR_LOCK_BAD_LOCK _("Lock file check failed")
+MU_ERR_LOCK_BAD_FILE _("File check failed")
+MU_ERR_LOCK_NOT_HELD _("Lock not held on file")
+MU_ERR_LOCK_EXT_FAIL _("Failed to execute external locker")
+MU_ERR_LOCK_EXT_ERR _("External locker failed")
+MU_ERR_LOCK_EXT_KILLED _("External locker killed")
+
+MU_ERR_NO_SUCH_USER _("No such user name")
+
+MU_ERR_GETHOSTBYNAME _("DNS name resolution failed")
+MU_ERR_BAD_RESUMPTION _("State busy must resume operation")
+MU_ERR_MAILER_BAD_FROM _("Not a valid mailer from address")
+MU_ERR_MAILER_BAD_TO _("Not a valid mailer to address")
+MU_ERR_MAILER_NO_RCPT_TO _("No receipt addresses found")
+MU_ERR_MAILER_BAD_URL _("Malformed or unsupported mailer URL")
+MU_ERR_SMTP_RCPT_FAILED _("SMTP rcpt to command failed")
+MU_ERR_TCP_NO_HOST _("Tcp connections need a host")
+MU_ERR_TCP_NO_PORT _("Tcp connections need a postive port")
+
+MU_ERR_BAD_2047_INPUT _("Input string is not RFC 2047 encoded")
+MU_ERR_BAD_2047_ENCODING _("Not a valid RFC 2047 encoding")
+
+MU_ERR_NOUSERNAME _("User name is not supplied")
+MU_ERR_NOPASSWORD _("User password is not supplied")
+
+MU_ERR_UNSAFE_PERMS _("Unsafe file permissions. Set 0600")
+MU_ERR_BAD_AUTH_SCHEME _("Unsupported authentication scheme")
+MU_ERR_AUTH_FAILURE _("Authentication failed")
+
+MU_ERR_PROCESS_NOEXEC _("Cannot execute")
+MU_ERR_PROCESS_EXITED _("Process exited with a non-zero status")
+MU_ERR_PROCESS_SIGNALED _("Process exited on signal")
+MU_ERR_PROCESS_UNKNOWN_FAILURE _("Unknown failure while executing subprocess")
+MU_ERR_CONN_CLOSED _("Connection closed by remote host")
+MU_ERR_PARSE _("Parse error")
+MU_ERR_NOENT _("Requested item not found")
+MU_ERR_EXISTS _("Item already exists")
+MU_ERR_BUFSPACE _("Not enough buffer space")
+
+MU_ERR_SQL _("SQL error")
+MU_ERR_DB_ALREADY_CONNECTED _("Already connected to the database")
+MU_ERR_DB_NOT_CONNECTED _("Not connected to the database")
+MU_ERR_RESULT_NOT_RELEASED _("Result of the previous query is not released")
+MU_ERR_NO_QUERY _("No query was yet executed")
+MU_ERR_BAD_COLUMN _("Bad column address")
+MU_ERR_NO_RESULT _("No result from the previous query available")
+MU_ERR_NO_INTERFACE _("No such interface")
+
+MU_ERR_BADOP _("Inappropriate operation for this mode")
+MU_ERR_BAD_FILENAME _("Badly formed file or directory name")
+MU_ERR_READ _("Read error")
+
+MU_ERR_NO_TRANSPORT _("Transport stream not set")
diff --git a/libmailutils/fgetpwent.c b/libmailutils/fgetpwent.c
new file mode 100644
index 000000000..99ec9dae9
--- /dev/null
+++ b/libmailutils/fgetpwent.c
@@ -0,0 +1,163 @@
+/* GNU Mailutils -- a suite of utilities for electronic mail
+ Copyright (C) 1999, 2000, 2001, 2007, 2010 Free Software Foundation,
+ Inc.
+
+ This library is free software; you can redistribute it and/or
+ modify it under the terms of the GNU Lesser General Public
+ License as published by the Free Software Foundation; either
+ version 3 of the License, or (at your option) any later version.
+
+ This library 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
+ Lesser General Public License for more details.
+
+ You should have received a copy of the GNU Lesser General
+ Public License along with this library; if not, write to the
+ Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor,
+ Boston, MA 02110-1301 USA */
+
+#ifdef HAVE_CONFIG_H
+# include <config.h>
+#endif
+
+#include <stdio.h>
+#include <pwd.h>
+#include <string.h>
+#include <stdlib.h>
+
+/*
+ Written by Alain Magloire.
+ Simple replacement for fgetpwent(), it is not :
+ - thread safe;
+ - static buffer was not use since it will limit the size
+ of the entry. But rather memory is allocated and __never__
+ release. The memory will grow if need be.
+ - no support for shadow
+ - no support for NIS(+)
+*/
+
+static char *buffer;
+static size_t buflen;
+static struct passwd pw;
+
+static char *
+parse_line (char *s, char **p)
+{
+ if (*s)
+ {
+ char *sep = strchr (s, ':');
+ if (sep)
+ {
+ *sep++ = '\0';
+ *p = sep;
+ }
+ else
+ *p = s + strlen (s);
+ }
+ else
+ *p = s;
+ return s;
+}
+
+static struct passwd *
+getentry (char *s)
+{
+ char *p;
+ pw.pw_name = parse_line (s, &p);
+ s = p;
+ pw.pw_passwd = parse_line (s, &p);
+ s = p;
+ pw.pw_uid = strtoul (parse_line (s, &p), NULL, 10);
+ s = p;
+ pw.pw_gid = strtoul (parse_line (s, &p), NULL, 10);
+ s = p;
+ pw.pw_gecos = parse_line (s, &p);
+ s = p;
+ pw.pw_dir = parse_line (s, &p);
+ s = p;
+ pw.pw_shell = parse_line (s, &p);
+ return &pw;
+}
+
+struct passwd *
+mu_fgetpwent (FILE *fp)
+{
+ size_t pos = 0;
+ int done = 0;
+ struct passwd *pw = NULL;
+
+ /* Allocate buffer if not yet available. */
+ /* This buffer will be never free(). */
+ if (buffer == NULL)
+ {
+ buflen = 1024;
+ buffer = malloc (buflen);
+ if (buffer == NULL)
+ return NULL;
+ }
+
+ do
+ {
+ if (fgets (buffer + pos, buflen, fp) != NULL)
+ {
+ /* Need a full line. */
+ if (buffer[strlen (buffer) - 1] == '\n')
+ {
+ /* reset marker position. */
+ pos = 0;
+ /* Nuke trailing newline. */
+ buffer[strlen (buffer) - 1] = '\0';
+
+ /* Skip comments. */
+ if (buffer[0] != '#')
+ {
+ done = 1;
+ pw = getentry (buffer);
+ }
+ }
+ else
+ {
+ /* Line is too long reallocate the buffer. */
+ char *tmp;
+ pos = strlen (buffer);
+ buflen *= 2;
+ tmp = realloc (buffer, buflen);
+ if (tmp)
+ buffer = tmp;
+ else
+ done = 1;
+ }
+ }
+ else
+ done = 1;
+ } while (!done);
+
+ return pw;
+
+}
+
+#ifdef STANDALONE
+int
+main ()
+{
+ FILE *fp = fopen ("/etc/passwd", "r");
+ if (fp)
+ {
+ struct passwd *pwd;
+ while ((pwd = fgetpwent (fp)))
+ {
+ printf ("--------------------------------------\n");
+ printf ("name %s\n", pwd->pw_name);
+ printf ("passwd %s\n", pwd->pw_passwd);
+ printf ("uid %d\n", pwd->pw_uid);
+ printf ("gid %d\n", pwd->pw_gid);
+ printf ("gecos %s\n", pwd->pw_gecos);
+ printf ("dir %s\n", pwd->pw_dir);
+ printf ("shell %s\n", pwd->pw_shell);
+ }
+ }
+ return 0;
+}
+
+#endif
diff --git a/libmailutils/file_stream.c b/libmailutils/file_stream.c
new file mode 100644
index 000000000..0df1254e1
--- /dev/null
+++ b/libmailutils/file_stream.c
@@ -0,0 +1,312 @@
+/* GNU Mailutils -- a suite of utilities for electronic mail
+ Copyright (C) 1999, 2000, 2001, 2002, 2004,
+ 2005, 2006, 2007, 2008, 2009, 2010 Free Software Foundation, Inc.
+
+ This library is free software; you can redistribute it and/or modify
+ it under the terms of the GNU Lesser General Public License as published by
+ the Free Software Foundation; either version 3, or (at your option)
+ any later version.
+
+ This library 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 Lesser General Public License for more details.
+
+ You should have received a copy of the GNU Lesser General Public License
+ along with GNU Mailutils. If not, see <http://www.gnu.org/licenses/>. */
+
+#ifdef HAVE_CONFIG_H
+# include <config.h>
+#endif
+#include <string.h>
+#include <stdlib.h>
+#include <unistd.h>
+#include <fcntl.h>
+#include <sys/stat.h>
+
+#include <mailutils/types.h>
+#include <mailutils/alloc.h>
+#include <mailutils/error.h>
+#include <mailutils/errno.h>
+#include <mailutils/nls.h>
+#include <mailutils/stream.h>
+#include <mailutils/sys/stream.h>
+#include <mailutils/sys/file_stream.h>
+#include <mailutils/mutil.h>
+
+#define MU_FSTR_ERROR_NOREG (MU_ERR_LAST+1)
+
+static int
+fd_read (struct _mu_stream *str, char *buf, size_t size, size_t *pret)
+{
+ struct _mu_file_stream *fstr = (struct _mu_file_stream *) str;
+ ssize_t n = read (fstr->fd, buf, size);
+ if (n == -1)
+ return errno;
+ *pret = n;
+ return 0;
+}
+
+static int
+fd_write (struct _mu_stream *str, const char *buf, size_t size, size_t *pret)
+{
+ struct _mu_file_stream *fstr = (struct _mu_file_stream *) str;
+ ssize_t n = write (fstr->fd, (char*) buf, size);
+ if (n == -1)
+ return errno;
+ *pret = n;
+ return 0;
+}
+
+static int
+fd_close (struct _mu_stream *str)
+{
+ struct _mu_file_stream *fstr = (struct _mu_file_stream *) str;
+ if (fstr->fd != -1)
+ {
+ if ((str->flags & MU_STREAM_AUTOCLOSE) && close (fstr->fd))
+ return errno;
+ fstr->fd = -1;
+ }
+ return 0;
+}
+
+static int
+fd_open (struct _mu_stream *str)
+{
+ struct _mu_file_stream *fstr = (struct _mu_file_stream *) str;
+ int oflg;
+ int fd;
+
+ if (!fstr->filename)
+ return EINVAL;
+ if (fstr->fd != -1)
+ fd_close (str);
+
+ /* Map the flags to the system equivalent. */
+ if ((fstr->stream.flags & MU_STREAM_RDWR) == MU_STREAM_RDWR)
+ oflg = O_RDWR;
+ else if (fstr->stream.flags & (MU_STREAM_WRITE|MU_STREAM_APPEND))
+ oflg = O_WRONLY;
+ else /* default */
+ oflg = O_RDONLY;
+
+ if (fstr->stream.flags & MU_STREAM_APPEND)
+ oflg |= O_APPEND;
+
+ /* Handle CREAT with care, not to follow symlinks. */
+ if (fstr->stream.flags & MU_STREAM_CREAT)
+ {
+ /* First see if the file already exists. */
+ fd = open (fstr->filename, oflg);
+ if (fd == -1)
+ {
+ /* Oops bail out. */
+ if (errno != ENOENT)
+ return errno;
+ /* Race condition here when creating the file ??. */
+ fd = open (fstr->filename, oflg|O_CREAT|O_EXCL,
+ 0600 | mu_stream_flags_to_mode (fstr->stream.flags, 0));
+ }
+ }
+ else
+ fd = open (fstr->filename, oflg);
+
+ if (fd == -1)
+ return errno;
+
+ if (!(fstr->stream.flags & MU_STREAM_ALLOW_LINKS)
+ && (fstr->stream.flags & (MU_STREAM_CREAT | MU_STREAM_RDWR |
+ MU_STREAM_APPEND)))
+ {
+ struct stat fdbuf, filebuf;
+
+ /* The next two stats should never fail. */
+ if (fstat (fd, &fdbuf) == -1
+ || lstat (fstr->filename, &filebuf) == -1)
+ {
+ close (fd);
+ return errno;
+ }
+
+ /* Now check that: file and fd reference the same file,
+ file only has one link, file is plain file. */
+ if ((fdbuf.st_dev != filebuf.st_dev
+ || fdbuf.st_ino != filebuf.st_ino
+ || fdbuf.st_nlink != 1
+ || filebuf.st_nlink != 1
+ || (fdbuf.st_mode & S_IFMT) != S_IFREG))
+ {
+ /* FIXME: Be silent */
+ close (fd);
+ return MU_FSTR_ERROR_NOREG;
+ }
+ }
+
+ if (fd < 0)
+ return errno;
+
+ /* Make sure it will be closed */
+ fstr->flags |= MU_STREAM_AUTOCLOSE;
+
+ fstr->fd = fd;
+ return 0;
+}
+
+int
+fd_seek (struct _mu_stream *str, mu_off_t off, mu_off_t *presult)
+{
+ struct _mu_file_stream *fstr = (struct _mu_file_stream *) str;
+ off = lseek (fstr->fd, off, SEEK_SET);
+ if (off < 0)
+ return errno;
+ *presult = off;
+ return 0;
+}
+
+int
+fd_size (struct _mu_stream *str, mu_off_t *psize)
+{
+ struct _mu_file_stream *fstr = (struct _mu_file_stream *) str;
+ struct stat st;
+ if (fstat (fstr->fd, &st))
+ return errno;
+ *psize = st.st_size;
+ return 0;
+}
+
+void
+fd_done (struct _mu_stream *str)
+{
+ struct _mu_file_stream *fstr = (struct _mu_file_stream *) str;
+ if (fstr->fd != -1)
+ fd_close (str);
+ if (fstr->filename)
+ free (fstr->filename);
+}
+
+const char *
+fd_error_string (struct _mu_stream *str, int rc)
+{
+ if (rc == MU_FSTR_ERROR_NOREG)
+ return _("must be a plain file with one link");
+ else
+ return mu_strerror (rc);
+}
+
+static int
+fd_ioctl (struct _mu_stream *str, int code, void *ptr)
+{
+ struct _mu_file_stream *fstr = (struct _mu_file_stream *) str;
+ mu_transport_t *ptrans;
+
+ switch (code)
+ {
+ case MU_IOCTL_GET_TRANSPORT:
+ if (!ptr)
+ return EINVAL;
+ ptrans = ptr;
+ ptrans[0] = (mu_transport_t) fstr->fd;
+ ptrans[1] = NULL;
+ break;
+
+ case MU_IOCTL_SET_TRANSPORT:
+ if (!ptr)
+ return EINVAL;
+ ptrans = ptr;
+ fstr->fd = (int) ptrans[0];
+ break;
+
+ case MU_IOCTL_GET_TRANSPORT_BUFFER:
+ {
+ struct mu_buffer_query *qp = ptr;
+ return mu_stream_get_buffer (str, qp);
+ }
+
+ case MU_IOCTL_SET_TRANSPORT_BUFFER:
+ {
+ struct mu_buffer_query *qp = ptr;
+ return mu_stream_set_buffer (str, qp->buftype, qp->bufsize);
+ }
+
+ default:
+ return ENOSYS;
+ }
+ return 0;
+}
+
+int
+fd_wait (mu_stream_t stream, int *pflags, struct timeval *tvp)
+{
+ struct _mu_file_stream *fstr = (struct _mu_file_stream *) stream;
+
+ if (fstr->fd == -1)
+ return EINVAL;
+ return mu_fd_wait (fstr->fd, pflags, tvp);
+}
+
+int
+fd_truncate (mu_stream_t stream, mu_off_t size)
+{
+ struct _mu_file_stream *fstr = (struct _mu_file_stream *) stream;
+ if (ftruncate (fstr->fd, size))
+ return errno;
+ return 0;
+}
+
+int
+_mu_file_stream_create (struct _mu_file_stream **pstream, size_t size,
+ const char *filename, int fd, int flags)
+{
+ struct _mu_file_stream *str =
+ (struct _mu_file_stream *) _mu_stream_create (size, flags);
+ if (!str)
+ return ENOMEM;
+
+ str->stream.read = fd_read;
+ str->stream.write = fd_write;
+ str->stream.open = fd_open;
+ str->stream.close = fd_close;
+ str->stream.done = fd_done;
+ str->stream.seek = fd_seek;
+ str->stream.size = fd_size;
+ str->stream.ctl = fd_ioctl;
+ str->stream.wait = fd_wait;
+ str->stream.truncate = fd_truncate;
+ str->stream.error_string = fd_error_string;
+
+ if (filename)
+ str->filename = mu_strdup (filename);
+ else
+ str->filename = NULL;
+ str->fd = fd;
+ str->flags = 0;
+ *pstream = str;
+ return 0;
+}
+
+int
+mu_file_stream_create (mu_stream_t *pstream, const char *filename, int flags)
+{
+ struct _mu_file_stream *fstr;
+ int rc = _mu_file_stream_create (&fstr,
+ sizeof (struct _mu_file_stream),
+ filename, -1,
+ flags | MU_STREAM_SEEK | MU_STREAM_AUTOCLOSE);
+ if (rc == 0)
+ *pstream = (mu_stream_t) fstr;
+ return rc;
+}
+
+int
+mu_fd_stream_create (mu_stream_t *pstream, char *filename, int fd, int flags)
+{
+ struct _mu_file_stream *fstr;
+ int rc = _mu_file_stream_create (&fstr,
+ sizeof (struct _mu_file_stream),
+ filename, fd, flags);
+ if (rc == 0)
+ *pstream = (mu_stream_t) fstr;
+ return rc;
+}
+
diff --git a/libmailutils/filter.c b/libmailutils/filter.c
new file mode 100644
index 000000000..8fdc25f90
--- /dev/null
+++ b/libmailutils/filter.c
@@ -0,0 +1,193 @@
+/* GNU Mailutils -- a suite of utilities for electronic mail
+ Copyright (C) 1999, 2000, 2001, 2004, 2005, 2007, 2009, 2010 Free
+ Software Foundation, Inc.
+
+ This library is free software; you can redistribute it and/or
+ modify it under the terms of the GNU Lesser General Public
+ License as published by the Free Software Foundation; either
+ version 3 of the License, or (at your option) any later version.
+
+ This library 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
+ Lesser General Public License for more details.
+
+ You should have received a copy of the GNU Lesser General
+ Public License along with this library; if not, write to the
+ Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor,
+ Boston, MA 02110-1301 USA */
+
+/* Notes:
+ First draft: Alain Magloire.
+ Complete rewrite: Sergey Poznyakoff.
+*/
+
+#ifdef HAVE_CONFIG_H
+# include <config.h>
+#endif
+
+#include <errno.h>
+#include <string.h>
+#include <stdlib.h>
+
+#ifdef HAVE_STRINGS_H
+# include <strings.h>
+#endif
+
+#include <mailutils/filter.h>
+#include <mailutils/monitor.h>
+#include <mailutils/list.h>
+#include <mailutils/stream.h>
+#include <mailutils/errno.h>
+#include <mailutils/cstr.h>
+
+/* NOTE: We will leak here since the monitor of the filter will never
+ be release. That's ok we can leave with this, it's only done once. */
+static mu_list_t filter_list;
+struct mu_monitor filter_monitor = MU_MONITOR_INITIALIZER;
+
+static int
+filter_name_cmp (const void *item, const void *data)
+{
+ struct _mu_filter_record const *rec = item;
+ char const *name = data;
+ return mu_c_strcasecmp (rec->name, name);
+}
+
+int
+mu_filter_get_list (mu_list_t *plist)
+{
+ if (plist == NULL)
+ return MU_ERR_OUT_PTR_NULL;
+ mu_monitor_wrlock (&filter_monitor);
+ if (filter_list == NULL)
+ {
+ int status = mu_list_create (&filter_list);
+ if (status != 0)
+ return status;
+ mu_list_set_comparator (filter_list, filter_name_cmp);
+ /* Default filters. */
+ mu_list_append (filter_list, mu_base64_filter);
+ mu_list_append (filter_list, mu_qp_filter);
+ mu_list_append (filter_list, mu_binary_filter);
+ mu_list_append (filter_list, mu_bit8_filter);
+ mu_list_append (filter_list, mu_bit7_filter);
+ mu_list_append (filter_list, mu_rfc822_filter);
+ mu_list_append (filter_list, mu_crlf_filter);
+ mu_list_append (filter_list, mu_crlfdot_filter);
+ mu_list_append (filter_list, mu_dot_filter);
+ mu_list_append (filter_list, mu_rfc_2047_Q_filter);
+ mu_list_append (filter_list, mu_rfc_2047_B_filter);
+ /* FIXME: add the default encodings? */
+ }
+ *plist = filter_list;
+ mu_monitor_unlock (&filter_monitor);
+ return 0;
+}
+
+static int
+filter_create_rd (mu_stream_t *pstream, mu_stream_t stream,
+ size_t max_line_length,
+ int mode,
+ mu_filter_xcode_t xcode, void *xdata,
+ int flags)
+{
+ int status;
+ mu_stream_t fltstream;
+
+ flags &= ~MU_STREAM_AUTOCLOSE;
+
+ status = mu_filter_stream_create (&fltstream, stream,
+ mode, xcode, xdata,
+ flags);
+ if (status == 0)
+ {
+ if (max_line_length)
+ {
+ status = mu_linelen_filter_create (pstream, fltstream,
+ max_line_length,
+ flags);
+ mu_stream_unref (fltstream);
+ if (status)
+ return status;
+ }
+ else
+ *pstream = fltstream;
+
+ if (flags & MU_STREAM_AUTOCLOSE)
+ mu_stream_unref (stream);
+ }
+ return status;
+}
+
+static int
+filter_create_wr (mu_stream_t *pstream, mu_stream_t stream,
+ size_t max_line_length,
+ int mode,
+ mu_filter_xcode_t xcode, void *xdata,
+ int flags)
+{
+ int status;
+ mu_stream_t fltstream, instream = NULL, tmpstr;
+
+ flags &= ~MU_STREAM_AUTOCLOSE;
+
+ if (max_line_length)
+ {
+ status = mu_linelen_filter_create (&instream, stream,
+ max_line_length,
+ flags);
+ if (status)
+ return status;
+ tmpstr = instream;
+ }
+ else
+ tmpstr = stream;
+
+ status = mu_filter_stream_create (&fltstream, tmpstr,
+ mode, xcode, xdata,
+ flags);
+ mu_stream_unref (instream);
+ if (status)
+ return status;
+ *pstream = fltstream;
+ if (flags & MU_STREAM_AUTOCLOSE)
+ mu_stream_unref (stream);
+ return status;
+}
+
+int
+mu_filter_create (mu_stream_t *pstream, mu_stream_t stream, const char *name,
+ int mode, int flags)
+{
+ int status;
+ mu_filter_record_t frec;
+ mu_list_t list;
+ void *xdata = NULL;
+
+ if ((flags & MU_STREAM_RDWR) == MU_STREAM_RDWR)
+ return EINVAL;
+
+ mu_filter_get_list (&list);
+ status = mu_list_locate (list, (void*)name, (void**)&frec);
+ if (status)
+ return status;
+
+ if (frec->newdata)
+ {
+ status = frec->newdata (&xdata, mode, NULL);
+ if (status)
+ return status;
+ }
+
+ status = ((flags & MU_STREAM_WRITE) ? filter_create_wr : filter_create_rd)
+ (pstream, stream,
+ frec->max_line_length,
+ mode,
+ mode == MU_FILTER_ENCODE ? frec->encoder : frec->decoder,
+ xdata,
+ flags);
+ if (status)
+ free (xdata);
+ return status;
+}
diff --git a/libmailutils/filter_iconv.c b/libmailutils/filter_iconv.c
new file mode 100644
index 000000000..88c397dbd
--- /dev/null
+++ b/libmailutils/filter_iconv.c
@@ -0,0 +1,474 @@
+/* GNU Mailutils -- a suite of utilities for electronic mail
+ Copyright (C) 2004, 2005, 2007, 2010 Free Software Foundation, Inc.
+
+ This library is free software; you can redistribute it and/or
+ modify it under the terms of the GNU Lesser General Public
+ License as published by the Free Software Foundation; either
+ version 3 of the License, or (at your option) any later version.
+
+ This library 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
+ Lesser General Public License for more details.
+
+ You should have received a copy of the GNU Lesser General
+ Public License along with this library; if not, write to the
+ Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor,
+ Boston, MA 02110-1301 USA */
+
+#ifdef HAVE_CONFIG_H
+# include <config.h>
+#endif
+
+#include <unistd.h>
+#include <stdio.h>
+#include <stdlib.h>
+#include <errno.h>
+#include <mailutils/stream.h>
+#include <mailutils/sys/stream.h>
+#include <mailutils/filter.h>
+#include <mailutils/errno.h>
+#include <mailutils/nls.h>
+
+#ifdef HAVE_ICONV_H
+# include <iconv.h>
+#endif
+
+#ifndef ICONV_CONST
+# define ICONV_CONST
+#endif
+
+#ifndef HAVE_ICONV
+# undef iconv_open
+# define iconv_open(tocode, fromcode) ((iconv_t) -1)
+
+# undef iconv
+# define iconv(cd, inbuf, inbytesleft, outbuf, outbytesleft) (errno = EILSEQ, (size_t) -1)
+
+# undef iconv_close
+# define iconv_close(cd) 0
+
+#endif
+
+enum _icvt_state
+ {
+ state_closed, /* Filter is closed */
+ state_open, /* Filter is open and running in conversion mode */
+ state_copy_pass, /* Filter is open and running in copy-pass mode */
+ state_copy_octal, /* Filter is open and running in copy-octal mode */
+ state_iconv_error, /* A fatal iconv error has occurred */
+ state_transport_error /* A fatal transport error has occurred */
+ };
+
+struct icvt_stream
+{
+ struct _mu_stream stream;
+ mu_stream_t transport;/* I/O stream */
+ int fallback_mode;
+ iconv_t cd; /* Conversion descriptor */
+ char *buf; /* Conversion buffer */
+ size_t bufsize; /* Size of buf */
+ size_t bufpos; /* Current position in buf */
+ enum _icvt_state state;
+ int ec; /* Error code */
+ char errbuf[128]; /* Error message buffer */
+};
+
+static int
+_icvt_open (mu_stream_t stream)
+{
+ struct icvt_stream *s = (struct icvt_stream *)stream;
+ if (s->cd == (iconv_t) -1)
+ return EINVAL;
+ s->state = state_open;
+ return 0;
+}
+
+static int
+_icvt_close (mu_stream_t stream)
+{
+ struct icvt_stream *s = (struct icvt_stream *)stream;
+ if (s->state != state_closed)
+ {
+ mu_stream_close (s->transport);
+ iconv_close (s->cd);
+ s->cd = (iconv_t) -1;
+ s->state = state_closed;
+ }
+ return 0;
+}
+
+static void
+_icvt_done (mu_stream_t stream)
+{
+ struct icvt_stream *s = (struct icvt_stream *)stream;
+
+ if (s->state != state_closed)
+ _icvt_close (stream);
+ mu_stream_destroy (&s->transport);
+ free (s->buf);
+}
+
+static int _icvt_read (mu_stream_t stream, char *optr, size_t osize,
+ size_t *pnbytes);
+
+static int
+internal_icvt_read (mu_stream_t stream, char *optr, size_t osize,
+ size_t *pnbytes)
+{
+ struct icvt_stream *s = (struct icvt_stream *)stream;
+ size_t nbytes = 0;
+ int rc, status = 0;
+ char *ob = optr;
+ size_t olen = osize;
+
+ if (s->bufpos == 0)
+ {
+ status = mu_stream_read (s->transport, s->buf, s->bufsize, &nbytes);
+ if (status)
+ {
+ s->state = state_transport_error;
+ s->ec = rc;
+ return MU_ERR_FAILURE;
+ }
+ else if (nbytes == 0)
+ {
+ if (pnbytes)
+ *pnbytes = 0;
+ return 0;
+ }
+ }
+
+ do
+ {
+ char ICONV_CONST *ib = s->buf;
+ size_t inlen = s->bufpos + nbytes;
+
+ rc = iconv (s->cd, &ib, &inlen, &ob, &olen);
+ if (ib > s->buf)
+ {
+ memmove (s->buf, ib, inlen);
+ s->bufpos = inlen;
+ }
+ else
+ s->bufpos += nbytes;
+
+ if (rc == -1)
+ {
+ if (errno == E2BIG)
+ {
+ if (ob > optr)
+ break;
+ else
+ {
+ s->ec = MU_ERR_BUFSPACE;
+ return MU_ERR_BUFSPACE;
+ }
+ }
+ else if (errno == EILSEQ)
+ {
+ switch (s->fallback_mode)
+ {
+ case mu_fallback_none:
+ s->state = state_iconv_error;
+ s->ec = errno;
+ if (ob == optr)
+ return MU_ERR_FAILURE;
+ break;
+
+ case mu_fallback_copy_pass:
+ s->state = state_copy_pass;
+ if (ob == optr)
+ return _icvt_read (stream, optr, osize, pnbytes);
+ break;
+
+ case mu_fallback_copy_octal:
+ s->state = state_copy_octal;
+ if (ob == optr)
+ return _icvt_read (stream, optr, osize, pnbytes);
+ break;
+ }
+ }
+ else if (errno == EINVAL)
+ {
+ if (inlen == s->bufsize)
+ {
+ /* Try to reallocate temp buffer */
+ char *p = realloc (s->buf, s->bufsize + 128);
+ if (!p)
+ return ENOMEM;
+ s->buf = p;
+ s->bufsize += 128;
+ }
+ continue;
+ }
+ else
+ {
+ s->ec = errno;
+ s->state = state_iconv_error;
+ return MU_ERR_FAILURE;
+ }
+ }
+ }
+ while (olen > 0
+ && (status = mu_stream_read (s->transport,
+ s->buf + s->bufpos,
+ s->bufsize - s->bufpos,
+ &nbytes)) == 0
+ && nbytes);
+
+ if (status)
+ {
+ s->state = state_transport_error;
+ s->ec = status;
+ if (ob == optr)
+ return MU_ERR_FAILURE;
+ }
+
+ if (pnbytes)
+ *pnbytes = ob - optr;
+ return 0;
+}
+
+#define ISPRINT(c) (((c)>=' '&&(c)<127)||(c)=='\n')
+
+static int
+copy_octal (struct icvt_stream *s, char *optr, size_t osize, size_t *pnbytes)
+{
+ size_t i, j;
+ int status;
+
+ if (osize == 0)
+ return MU_ERR_BUFSPACE;
+
+ if (s->bufpos < osize)
+ {
+ size_t rdcount = osize;
+ if (s->bufsize < rdcount)
+ {
+ /* Try to reallocate temp buffer */
+ char *p = realloc (s->buf, rdcount);
+ if (p)
+ {
+ s->bufsize = rdcount;
+ s->buf = p;
+ }
+ else
+ rdcount = s->bufsize;
+ }
+
+ status = mu_stream_read (s->transport,
+ s->buf + s->bufpos,
+ rdcount - s->bufpos,
+ &rdcount);
+ if (status)
+ {
+ s->state = state_transport_error;
+ s->ec = status;
+ if (s->bufpos == 0)
+ return MU_ERR_FAILURE;
+ }
+ else
+ s->bufpos += rdcount;
+ }
+
+ for (i = j = 0; j < osize && i < s->bufpos; i++)
+ {
+ if (ISPRINT (*(unsigned char*)(s->buf+i)))
+ optr[j++] = s->buf[i];
+ else if (j + 4 >= osize)
+ break;
+ else
+ {
+ sprintf (optr + j, "\\%03o", *(unsigned char*)(s->buf+i));
+ j += 4;
+ }
+ }
+ s->bufpos -= i;
+ memmove (s->buf, s->buf + i, s->bufpos);
+ if (pnbytes)
+ *pnbytes = j;
+ return 0;
+}
+
+static int
+copy_pass (struct icvt_stream *s, char *optr, size_t osize, size_t *pnbytes)
+{
+ int status;
+ size_t nbytes;
+
+ if (s->bufpos)
+ {
+ size_t sz = s->bufpos < osize ? s->bufpos : osize;
+ memcpy (optr, s->buf, sz);
+ s->bufpos -= sz;
+ if (s->bufpos)
+ memmove (s->buf, s->buf + sz, s->bufpos);
+ if (pnbytes)
+ *pnbytes = sz;
+ return 0;
+ }
+
+ status = mu_stream_read (s->transport, optr, osize, &nbytes);
+ if (status)
+ {
+ s->state = state_transport_error;
+ s->ec = status;
+ if (s->bufpos == 0)
+ return MU_ERR_FAILURE;
+ }
+ if (pnbytes)
+ *pnbytes = nbytes;
+ return 0;
+}
+
+static int
+_icvt_read (mu_stream_t stream, char *optr, size_t osize, size_t *pnbytes)
+{
+ struct icvt_stream *s = (struct icvt_stream *)stream;
+
+ switch (s->state)
+ {
+ case state_open:
+ return internal_icvt_read (stream, optr, osize, pnbytes);
+
+ case state_closed:
+ return EINVAL;
+
+ case state_copy_pass:
+ return copy_pass (s, optr, osize, pnbytes);
+
+ case state_copy_octal:
+ return copy_octal (s, optr, osize, pnbytes);
+
+ default:
+ break;
+ }
+ return MU_ERR_FAILURE;
+}
+
+const char *
+_icvt_strerror (mu_stream_t stream, int rc)
+{
+ struct icvt_stream *s = (struct icvt_stream *)stream;
+
+ switch (s->state)
+ {
+ case state_transport_error:
+ snprintf (s->errbuf, sizeof s->errbuf,
+ _("Transport error: %s"), mu_strerror (s->ec));
+ break;
+
+ case state_iconv_error:
+ switch (s->ec)
+ {
+ case EILSEQ:
+ snprintf (s->errbuf, sizeof s->errbuf,
+ _("Illegal multibyte sequence near %*.*s"),
+ (int) s->bufpos, (int) s->bufpos, s->buf);
+ break;
+
+ default:
+ snprintf (s->errbuf, sizeof s->errbuf,
+ _("Iconv error: %s"), mu_strerror (s->ec));
+ }
+ break;
+
+ case state_closed:
+ return _("Stream is closed");
+
+ default:
+ return mu_strerror (s->ec);
+ }
+
+ return s->errbuf;
+}
+
+static int
+_icvt_ioctl (mu_stream_t stream, int code, void *ptr)
+{
+ struct icvt_stream *s = (struct icvt_stream *)stream;
+ mu_transport_t *ptrans;
+
+ switch (code)
+ {
+ case MU_IOCTL_GET_TRANSPORT:
+ if (!ptr)
+ return EINVAL;
+ ptrans = ptr;
+ ptrans[0] = (mu_transport_t) s->transport;
+ ptrans[1] = NULL;
+ break;
+
+ case MU_IOCTL_SWAP_STREAM:
+ case MU_IOCTL_GET_TRANSPORT_BUFFER:
+ case MU_IOCTL_SET_TRANSPORT_BUFFER:
+ return mu_stream_ioctl (s->transport, code, ptr);
+
+ default:
+ return ENOSYS;
+ }
+ return 0;
+}
+
+int
+_icvt_wait (mu_stream_t stream, int *pflags, struct timeval *tvp)
+{
+ struct icvt_stream *s = (struct icvt_stream *)stream;
+ return mu_stream_wait (s->transport, pflags, tvp);
+}
+
+/* FIXME: Seeks in the *transport* stream. */
+int
+_icvt_seek (mu_stream_t stream, mu_off_t off, mu_off_t *presult)
+{
+ struct icvt_stream *s = (struct icvt_stream *)stream;
+ return mu_stream_seek (s->transport, off, MU_SEEK_SET, presult);
+}
+
+int
+mu_filter_iconv_create (mu_stream_t *s, mu_stream_t transport,
+ const char *fromcode, const char *tocode, int flags,
+ enum mu_iconv_fallback_mode fallback_mode)
+{
+ struct icvt_stream *iptr;
+ iconv_t cd;
+
+ cd = iconv_open (tocode, fromcode);
+ if (cd == (iconv_t) -1)
+ return MU_ERR_FAILURE;
+
+ iptr = (struct icvt_stream *) _mu_stream_create (sizeof (*iptr), flags);
+ if (!iptr)
+ {
+ iconv_close (cd);
+ return ENOMEM;
+ }
+
+ if (!(flags & MU_STREAM_AUTOCLOSE))
+ mu_stream_ref (transport);
+ iptr->transport = transport;
+ iptr->fallback_mode = fallback_mode;
+ iptr->cd = cd;
+ iptr->state = state_closed;
+ iptr->bufsize = 128;
+ iptr->buf = malloc (iptr->bufsize);
+ if (!iptr->buf)
+ {
+ free (iptr);
+ return ENOMEM;
+ }
+ iptr->bufpos = 0;
+
+ iptr->stream.open = _icvt_open;
+ iptr->stream.close = _icvt_close;
+ iptr->stream.read = _icvt_read;
+ iptr->stream.done = _icvt_done;
+ iptr->stream.error_string = _icvt_strerror;
+ iptr->stream.ctl = _icvt_ioctl;
+ iptr->stream.wait = _icvt_wait;
+ iptr->stream.seek = _icvt_seek;
+ iptr->stream.flags = MU_STREAM_READ | MU_STREAM_SEEK;
+ *s = (mu_stream_t)iptr;
+ return 0;
+}
diff --git a/libmailutils/fltstream.c b/libmailutils/fltstream.c
new file mode 100644
index 000000000..4359e03ea
--- /dev/null
+++ b/libmailutils/fltstream.c
@@ -0,0 +1,533 @@
+/* GNU Mailutils -- a suite of utilities for electronic mail
+ Copyright (C) 2009, 2010 Free Software Foundation, Inc.
+
+ This library is free software; you can redistribute it and/or modify
+ it under the terms of the GNU Lesser General Public License as published by
+ the Free Software Foundation; either version 3, or (at your option)
+ any later version.
+
+ This library 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 Lesser General Public License for more details.
+
+ You should have received a copy of the GNU Lesser General Public License
+ along with GNU Mailutils. If not, see <http://www.gnu.org/licenses/>. */
+
+#ifdef HAVE_CONFIG_H
+# include <config.h>
+#endif
+#include <stdlib.h>
+#include <string.h>
+#include <mailutils/types.h>
+#include <mailutils/alloc.h>
+#include <mailutils/error.h>
+#include <mailutils/errno.h>
+#include <mailutils/nls.h>
+#include <mailutils/stream.h>
+#include <mailutils/sys/filter.h>
+
+#define MFB_BASE(buf) ((buf).base)
+#define MFB_CURPTR(buf) ((buf).base + (buf).pos)
+#define MFB_ENDPTR(buf) ((buf).base + (buf).level)
+
+#define MFB_SIZE(buf) ((buf).size)
+#define MFB_LEVEL(buf) ((buf).level)
+#define MFB_POS(buf) ((buf).pos)
+#define MFB_RDBYTES(buf) \
+ (MFB_LEVEL (buf) - MFB_POS (buf))
+#define MFB_FREESIZE(buf) \
+ (MFB_SIZE (buf) - MFB_LEVEL (buf))
+
+#define MBF_CLEAR(buf) ((buf).pos = (buf).level = 0)
+#define MBF_FREE(buf) free ((buf).base)
+
+static void
+init_iobuf (struct mu_filter_io *io, struct _mu_filter_stream *fs)
+{
+ io->input = MFB_CURPTR (fs->inbuf);
+ io->isize = MFB_RDBYTES (fs->inbuf);
+ io->output = MFB_ENDPTR (fs->outbuf);
+ io->osize = MFB_FREESIZE (fs->outbuf);
+ io->errcode = 0;
+ io->eof = 0;
+}
+
+static int
+filter_stream_init (struct _mu_filter_stream *fs)
+{
+ if (fs->xdata)
+ {
+ struct mu_filter_io iobuf;
+ memset (&iobuf, 0, sizeof (iobuf));
+ if (fs->xcode (fs->xdata, mu_filter_init, &iobuf) == mu_filter_falure)
+ return iobuf.errcode;
+ }
+ return 0;
+}
+
+static int
+MFB_require (struct _mu_filter_buffer *buf, size_t size)
+{
+ if (size > MFB_FREESIZE (*buf))
+ {
+ /* Compact the buffer */
+ if (MFB_POS (*buf))
+ {
+ memmove (MFB_BASE (*buf), MFB_CURPTR (*buf), MFB_RDBYTES (*buf));
+ buf->level -= buf->pos;
+ buf->pos = 0;
+ }
+ if (size > MFB_FREESIZE (*buf))
+ {
+ char *p;
+
+ size += MFB_LEVEL (*buf);
+ p = realloc (buf->base, size);
+ if (!p)
+ return ENOMEM;
+ buf->size = size;
+ buf->base = p;
+ }
+ }
+ return 0;
+}
+
+static void
+MFB_advance_pos (struct _mu_filter_buffer *buf, size_t delta)
+{
+ buf->pos += delta;
+ if (buf->pos == buf->level)
+ buf->pos = buf->level = 0;
+}
+
+static void
+MFB_advance_level (struct _mu_filter_buffer *buf, size_t delta)
+{
+ buf->level += delta;
+}
+
+static int
+filter_read (mu_stream_t stream, char *buf, size_t size, size_t *pret)
+{
+ struct _mu_filter_stream *fs = (struct _mu_filter_stream *)stream;
+ struct mu_filter_io iobuf;
+ size_t min_input_level = MU_FILTER_BUF_SIZE;
+ size_t min_output_size = MU_FILTER_BUF_SIZE;
+ enum mu_filter_command cmd = mu_filter_xcode;
+ size_t total = 0;
+ int stop = 0;
+ int again = 0;
+
+ do
+ {
+ size_t rdsize;
+
+ if (MFB_RDBYTES (fs->outbuf) == 0)
+ {
+ enum mu_filter_result res;
+ int rc;
+
+ if (MFB_RDBYTES (fs->inbuf) < min_input_level && !again)
+ {
+ rc = MFB_require (&fs->inbuf, min_input_level);
+ if (rc)
+ return rc;
+ rc = mu_stream_read (fs->transport,
+ MFB_ENDPTR (fs->inbuf),
+ MFB_FREESIZE (fs->inbuf),
+ &rdsize);
+ if (rc)
+ return rc;
+ if (rdsize == 0 &&
+ MFB_RDBYTES (fs->outbuf) == 0 &&
+ MFB_RDBYTES (fs->inbuf) == 0)
+ cmd = mu_filter_lastbuf;
+
+ MFB_advance_level (&fs->inbuf, rdsize);
+ }
+
+ if (min_output_size < MFB_RDBYTES (fs->inbuf))
+ min_output_size = MFB_RDBYTES (fs->inbuf);
+ rc = MFB_require (&fs->outbuf, min_output_size);
+ if (rc)
+ return rc;
+
+ init_iobuf (&iobuf, fs);
+
+ if (cmd != mu_filter_lastbuf)
+ cmd = mu_stream_eof (fs->transport) ?
+ mu_filter_lastbuf : mu_filter_xcode;
+ res = fs->xcode (fs->xdata, cmd, &iobuf);
+ switch (res)
+ {
+ case mu_filter_again:
+ if (++again > MU_FILTER_MAX_AGAIN)
+ {
+ /* FIXME: What filter? Need some id. */
+ mu_error (_("filter returned `again' too many times"));
+ again = 0;
+ }
+ break;
+
+ case mu_filter_ok:
+ again = 0;
+ if (cmd == mu_filter_lastbuf || iobuf.eof)
+ {
+ _mu_stream_seteof (stream);
+ stop = 1;
+ }
+ break;
+
+ case mu_filter_falure:
+ return iobuf.errcode;
+
+ case mu_filter_moreinput:
+ min_input_level = iobuf.isize;
+ continue;
+
+ case mu_filter_moreoutput:
+ min_output_size = iobuf.osize;
+ continue;
+ }
+
+ if (iobuf.isize > MFB_RDBYTES (fs->inbuf)
+ || iobuf.osize > MFB_FREESIZE (fs->outbuf))
+ return MU_ERR_FAILURE; /* FIXME: special error code? */
+
+ /* iobuf.osize contains number of bytes written to output */
+ MFB_advance_level (&fs->outbuf, iobuf.osize);
+
+ /* iobuf.isize contains number of bytes read from input */
+ MFB_advance_pos (&fs->inbuf, iobuf.isize);
+ }
+
+ rdsize = size - total;
+ if (rdsize > MFB_RDBYTES (fs->outbuf))
+ rdsize = MFB_RDBYTES (fs->outbuf);
+ memcpy (buf + total, MFB_CURPTR (fs->outbuf), rdsize);
+ MFB_advance_pos (&fs->outbuf, rdsize);
+ total += rdsize;
+
+ }
+ while (!stop && (total < size || again));
+
+ *pret = total;
+ return 0;
+}
+
+static int
+filter_rd_flush (mu_stream_t stream)
+{
+ struct _mu_filter_stream *fs = (struct _mu_filter_stream *)stream;
+ return filter_stream_init (fs);
+}
+
+static int
+filter_write_internal (mu_stream_t stream, enum mu_filter_command cmd,
+ const char *buf, size_t size, size_t *pret)
+{
+ struct _mu_filter_stream *fs = (struct _mu_filter_stream *)stream;
+ struct mu_filter_io iobuf;
+ size_t min_input_level = cmd == mu_filter_xcode ? MU_FILTER_BUF_SIZE : 0;
+ size_t min_output_size = MU_FILTER_BUF_SIZE;
+ size_t total = 0;
+ int rc = 0;
+ int again;
+ int stop = 0;
+
+ do
+ {
+ size_t rdsize;
+ enum mu_filter_result res;
+
+ if (MFB_RDBYTES (fs->inbuf) < min_input_level)
+ {
+ rdsize = size - total;
+ if (rdsize == 0)
+ break;
+ rc = MFB_require (&fs->inbuf, min_input_level);
+ if (rc)
+ break;
+ if (rdsize > MFB_FREESIZE (fs->inbuf))
+ rdsize = MFB_FREESIZE (fs->inbuf);
+ memcpy (MFB_ENDPTR (fs->inbuf), buf + total, rdsize);
+ MFB_advance_level (&fs->inbuf, rdsize);
+ total += rdsize;
+ }
+
+ if (min_output_size < MFB_RDBYTES (fs->inbuf))
+ min_output_size = MFB_RDBYTES (fs->inbuf);
+ rc = MFB_require (&fs->outbuf, min_output_size);
+ if (rc)
+ return rc;
+
+ init_iobuf (&iobuf, fs);
+
+ res = fs->xcode (fs->xdata, cmd, &iobuf);
+ switch (res)
+ {
+ case mu_filter_again:
+ if (++again > MU_FILTER_MAX_AGAIN)
+ {
+ /* FIXME: What filter? Need some id. */
+ mu_error (_("filter returned `again' too many times"));
+ again = 0;
+ }
+ break;
+
+ case mu_filter_ok:
+ again = 0;
+ if (cmd == mu_filter_lastbuf || iobuf.eof)
+ {
+ _mu_stream_seteof (stream);
+ stop = 1;
+ }
+ break;
+
+ case mu_filter_falure:
+ return iobuf.errcode;
+
+ case mu_filter_moreinput:
+ min_input_level = iobuf.isize;
+ continue;
+
+ case mu_filter_moreoutput:
+ min_output_size = iobuf.osize;
+ continue;
+ }
+
+ if (iobuf.isize > MFB_RDBYTES (fs->inbuf)
+ || iobuf.osize > MFB_FREESIZE (fs->outbuf))
+ return MU_ERR_FAILURE; /* FIXME: special error code? */
+
+ /* iobuf.osize contains number of bytes written to output */
+ MFB_advance_level (&fs->outbuf, iobuf.osize);
+
+ /* iobuf.isize contains number of bytes read from input */
+ MFB_advance_pos (&fs->inbuf, iobuf.isize);
+
+ rc = mu_stream_write (fs->transport,
+ MFB_CURPTR (fs->outbuf),
+ MFB_RDBYTES (fs->outbuf),
+ &rdsize);
+ if (rc == 0)
+ MFB_advance_pos (&fs->outbuf, rdsize);
+ else
+ break;
+ }
+ while (!stop && (MFB_RDBYTES (fs->outbuf) || again));
+ if (pret)
+ *pret = total;
+ else if (total < size && rc == 0)
+ rc = MU_ERR_FAILURE;
+ return rc;
+}
+
+static int
+filter_write (mu_stream_t stream, const char *buf, size_t size, size_t *pret)
+{
+ return filter_write_internal (stream, mu_filter_xcode, buf, size, pret);
+}
+
+static int
+filter_wr_flush (mu_stream_t stream)
+{
+ struct _mu_filter_stream *fs = (struct _mu_filter_stream *)stream;
+ size_t dummy;
+ int rc = filter_write_internal (stream, mu_filter_flush, NULL, 0, &dummy);
+ if (rc == 0)
+ rc = mu_stream_flush (fs->transport);
+ return rc;
+}
+
+static int
+filter_seek (struct _mu_stream *stream, mu_off_t off, mu_off_t *ppos)
+{
+ struct _mu_filter_stream *fs = (struct _mu_filter_stream *)stream;
+ int status;
+
+ status = mu_stream_seek (fs->transport, 0, MU_SEEK_SET, NULL);
+ if (status)
+ return status;
+ stream->offset = 0;
+ return mu_stream_skip_input_bytes (stream, off, ppos);
+}
+
+static int
+filter_ctl (struct _mu_stream *stream, int op, void *ptr)
+{
+ struct _mu_filter_stream *fs = (struct _mu_filter_stream *)stream;
+ mu_transport_t *ptrans;
+
+ switch (op)
+ {
+ case MU_IOCTL_GET_TRANSPORT:
+ if (!ptr)
+ return EINVAL;
+ ptrans = ptr;
+ ptrans[0] = (mu_transport_t) fs->transport;
+ ptrans[1] = NULL;
+ break;
+
+ default:
+ return mu_stream_ioctl (fs->transport, op, ptr);
+ }
+ return 0;
+}
+
+static const char *
+filter_error_string (struct _mu_stream *stream, int rc)
+{
+ struct _mu_filter_stream *fs = (struct _mu_filter_stream *)stream;
+ const char *p = mu_stream_strerror (fs->transport, rc);
+ if (!p)
+ p = mu_strerror (rc);
+ return p;
+}
+
+static void
+filter_done (mu_stream_t stream)
+{
+ struct _mu_filter_stream *fs = (struct _mu_filter_stream *)stream;
+ MBF_FREE (fs->inbuf);
+ MBF_FREE (fs->outbuf);
+ if (fs->xdata)
+ {
+ fs->xcode (fs->xdata, mu_filter_done, NULL);
+ free (fs->xdata);
+ }
+ mu_stream_destroy (&fs->transport);
+}
+
+static int
+filter_wr_close (mu_stream_t stream)
+{
+ struct _mu_filter_stream *fs = (struct _mu_filter_stream *)stream;
+ if (!mu_stream_eof (stream))
+ {
+ size_t dummy;
+ int rc = filter_write_internal (stream, mu_filter_lastbuf, NULL, 0,
+ &dummy);
+ if (rc)
+ return rc;
+ }
+ MBF_CLEAR (fs->inbuf);
+ MBF_CLEAR (fs->outbuf);
+ return mu_stream_close (fs->transport);
+}
+
+static int
+filter_rd_close (mu_stream_t stream)
+{
+ struct _mu_filter_stream *fs = (struct _mu_filter_stream *)stream;
+ MBF_CLEAR (fs->inbuf);
+ MBF_CLEAR (fs->outbuf);
+ return mu_stream_close (fs->transport);
+}
+
+
+static int
+filter_read_through (struct _mu_stream *stream,
+ char *buf, size_t bufsize,
+ size_t *pnread)
+{
+ struct _mu_filter_stream *fs = (struct _mu_filter_stream *)stream;
+ return mu_stream_read (fs->transport, buf, bufsize, pnread);
+}
+
+static int
+filter_write_through (struct _mu_stream *stream,
+ const char *buf, size_t bufsize,
+ size_t *pnwrite)
+{
+ struct _mu_filter_stream *fs = (struct _mu_filter_stream *)stream;
+ return mu_stream_write (fs->transport, buf, bufsize, pnwrite);
+}
+
+static int
+filter_wait (struct _mu_stream *stream, int *pflags, struct timeval *tvp)
+{
+ struct _mu_filter_stream *fs = (struct _mu_filter_stream *)stream;
+ /* FIXME: Take into account internal buffer state. */
+ return mu_stream_wait (fs->transport, pflags, tvp);
+}
+
+
+int
+mu_filter_stream_create (mu_stream_t *pflt,
+ mu_stream_t str,
+ int mode,
+ mu_filter_xcode_t xcode,
+ void *xdata, int flags)
+{
+ int rc;
+ struct _mu_filter_stream *fs;
+
+ if ((flags & MU_STREAM_RDWR) == MU_STREAM_RDWR
+ || !(flags & MU_STREAM_RDWR)
+ || (flags & (MU_STREAM_WRITE|MU_STREAM_SEEK)) ==
+ (MU_STREAM_WRITE|MU_STREAM_SEEK)
+ || (flags & (MU_STREAM_RDTHRU|MU_STREAM_WRTHRU)) ==
+ (MU_STREAM_RDTHRU|MU_STREAM_WRTHRU)
+ || (flags & (MU_STREAM_READ|MU_STREAM_RDTHRU)) ==
+ (MU_STREAM_READ|MU_STREAM_RDTHRU)
+ || (flags & (MU_STREAM_WRITE|MU_STREAM_WRTHRU)) ==
+ (MU_STREAM_WRITE|MU_STREAM_WRTHRU))
+ return EINVAL;
+
+ fs = (struct _mu_filter_stream *) _mu_stream_create (sizeof (*fs), flags);
+ if (!fs)
+ return ENOMEM;
+
+ if (flags & MU_STREAM_READ)
+ {
+ fs->stream.read = filter_read;
+ fs->stream.flush = filter_rd_flush;
+ fs->stream.close = filter_rd_close;
+ if (flags & MU_STREAM_WRTHRU)
+ {
+ flags |= MU_STREAM_WRITE;
+ fs->stream.write = filter_write_through;
+ }
+ }
+ else
+ {
+ fs->stream.write = filter_write;
+ fs->stream.flush = filter_wr_flush;
+ fs->stream.close = filter_wr_close;
+ if (flags & MU_STREAM_RDTHRU)
+ {
+ flags |= MU_STREAM_READ;
+ fs->stream.read = filter_read_through;
+ }
+ }
+ fs->stream.done = filter_done;
+ if (flags & MU_STREAM_SEEK)
+ fs->stream.seek = filter_seek;
+ fs->stream.ctl = filter_ctl;
+ fs->stream.wait = filter_wait;
+ fs->stream.error_string = filter_error_string;
+ fs->stream.flags = flags;
+
+ if (!(flags & MU_STREAM_AUTOCLOSE))
+ mu_stream_ref (str);
+ fs->transport = str;
+ fs->xcode = xcode;
+ fs->xdata = xdata;
+ fs->mode = mode;
+
+ mu_stream_set_buffer ((mu_stream_t) fs, mu_buffer_full, MU_FILTER_BUF_SIZE);
+
+ rc = filter_stream_init (fs);
+ if (rc)
+ {
+ free (fs);
+ return rc;
+ }
+
+ *pflt = (mu_stream_t) fs;
+ return 0;
+}
+
+
diff --git a/libmailutils/folder.c b/libmailutils/folder.c
new file mode 100644
index 000000000..3ce430a15
--- /dev/null
+++ b/libmailutils/folder.c
@@ -0,0 +1,511 @@
+/* GNU Mailutils -- a suite of utilities for electronic mail
+ Copyright (C) 1999, 2000, 2001, 2004, 2005, 2006, 2007, 2008, 2009,
+ 2010 Free Software Foundation, Inc.
+
+ This library is free software; you can redistribute it and/or
+ modify it under the terms of the GNU Lesser General Public
+ License as published by the Free Software Foundation; either
+ version 3 of the License, or (at your option) any later version.
+
+ This library 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
+ Lesser General Public License for more details.
+
+ You should have received a copy of the GNU Lesser General
+ Public License along with this library; if not, write to the
+ Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor,
+ Boston, MA 02110-1301 USA */
+
+#ifdef HAVE_CONFIG_H
+# include <config.h>
+#endif
+
+#include <errno.h>
+#include <stdlib.h>
+#include <string.h>
+#include <fnmatch.h>
+
+#include <mailutils/auth.h>
+#include <mailutils/debug.h>
+#include <mailutils/iterator.h>
+#include <mailutils/list.h>
+#include <mailutils/monitor.h>
+#include <mailutils/observer.h>
+#include <mailutils/registrar.h>
+#include <mailutils/stream.h>
+#include <mailutils/url.h>
+#include <mailutils/errno.h>
+
+#include <mailutils/sys/folder.h>
+
+/* Internal folder list. */
+static mu_list_t known_folder_list;
+static int is_known_folder (mu_url_t, mu_folder_t *);
+
+/* Static folder lock. */
+static struct mu_monitor folder_lock = MU_MONITOR_INITIALIZER;
+
+int
+mu_folder_match (const char *name, void *pattern, int flags)
+{
+ return fnmatch (pattern, name[0] == '/' ? name + 1 : name, flags);
+}
+
+/* A folder could be remote (IMAP), or local(a spool directory) like $HOME/Mail
+ etc .. We maintain a list of known folders to avoid creating multiple
+ folders for the same URL. So, when mu_folder_create is called we check if
+ we already have a folder for that URL and return it, otherwise we create a
+ new one. Downsides: the scheme to detect the same URL is very weak, and
+ there could be cases where you'll want a different folder for the same URL,
+ there is not easy way to do this. */
+int
+mu_folder_create_from_record (mu_folder_t *pfolder, mu_url_t url,
+ mu_record_t record)
+{
+ if (!pfolder)
+ return MU_ERR_OUT_PTR_NULL;
+
+ if (record ||
+ /* Look in the registrar list(iterator), for a possible concrete mailbox
+ implementation that could match the URL. */
+ mu_registrar_lookup_url (url, MU_FOLDER_ATTRIBUTE_DIRECTORY, &record,
+ NULL) == 0)
+ {
+ int (*f_init) (mu_folder_t) = NULL;
+
+ mu_record_get_folder (record, &f_init);
+ if (f_init)
+ {
+ int status;
+ mu_folder_t folder;
+ int (*u_init) (mu_url_t) = NULL;
+
+ mu_record_get_url (record, &u_init);
+ if (u_init)
+ {
+ status = u_init (url);
+ if (status)
+ return status;
+ }
+
+ mu_monitor_wrlock (&folder_lock);
+
+ /* Check if we already have the same URL folder. */
+ if (is_known_folder (url, &folder))
+ {
+ folder->ref++;
+ *pfolder = folder;
+ mu_url_destroy (&url); /* FIXME: Hmm */
+ mu_monitor_unlock (&folder_lock);
+ return 0;
+ }
+ else
+ mu_monitor_unlock (&folder_lock);
+
+ /* Create a new folder. */
+
+ /* Allocate memory for the folder. */
+ folder = calloc (1, sizeof (*folder));
+ if (folder != NULL)
+ {
+ folder->url = url;
+ /* Initialize the internal foilder lock, now so the
+ concrete folder could use it. */
+ status = mu_monitor_create (&folder->monitor, 0, folder);
+ if (status == 0)
+ {
+ /* Create the concrete folder type. */
+ status = f_init (folder);
+ if (status == 0)
+ {
+ if (!folder->_match)
+ folder->_match = mu_folder_match;
+ *pfolder = folder;
+ folder->ref++;
+ /* Put on the internal list of known folders. */
+ if (known_folder_list == NULL)
+ mu_list_create (&known_folder_list);
+ mu_list_append (known_folder_list, folder);
+ }
+ }
+ /* Something went wrong, destroy the object. */
+ if (status)
+ {
+ if (folder->monitor)
+ mu_monitor_destroy (&folder->monitor, folder);
+ free (folder);
+ }
+ }
+ return status;
+ }
+ }
+
+ return MU_ERR_NOENT;
+}
+
+int
+mu_folder_create (mu_folder_t *pfolder, const char *name)
+{
+ int rc;
+ mu_url_t url;
+
+ rc = mu_url_create (&url, name);
+ if (rc)
+ return rc;
+ rc = mu_url_parse (url);
+ if (rc == 0)
+ rc = mu_folder_create_from_record (pfolder, url, NULL);
+ if (rc)
+ mu_url_destroy (&url);
+ return rc;
+}
+
+/* The folder is destroy if it is the last reference. */
+void
+mu_folder_destroy (mu_folder_t *pfolder)
+{
+ if (pfolder && *pfolder)
+ {
+ mu_folder_t folder = *pfolder;
+ int destroy_lock = 0;
+ mu_monitor_t monitor = folder->monitor;
+
+ mu_monitor_wrlock (monitor);
+
+ /* Check if this the last reference for this folder. If yes removed
+ it from the list. */
+ mu_monitor_wrlock (&folder_lock);
+ folder->ref--;
+ /* Remove the folder from the list of known folder. */
+ if (folder->ref <= 0)
+ mu_list_remove (known_folder_list, folder);
+ /* If the list is empty we can safely remove it. */
+ if (mu_list_is_empty (known_folder_list))
+ mu_list_destroy (&known_folder_list);
+
+ mu_monitor_unlock (&folder_lock);
+
+ if (folder->ref <= 0)
+ {
+ mu_monitor_unlock (monitor);
+ destroy_lock = 1;
+ /* Notify the observers. */
+ if (folder->observable)
+ {
+ mu_observable_notify (folder->observable, MU_EVT_FOLDER_DESTROY,
+ folder);
+ mu_observable_destroy (&folder->observable, folder);
+ }
+ if (folder->_destroy)
+ folder->_destroy (folder);
+ mu_monitor_wrlock (monitor);
+ if (folder->authority)
+ mu_authority_destroy (&folder->authority, folder);
+ if (folder->stream)
+ mu_stream_destroy (&folder->stream);
+ if (folder->url)
+ mu_url_destroy (&folder->url);
+ free (folder);
+ }
+ mu_monitor_unlock (monitor);
+ if (destroy_lock)
+ mu_monitor_destroy (&monitor, folder);
+ *pfolder = NULL;
+ }
+}
+
+/* Cover functions. */
+int
+mu_folder_open (mu_folder_t folder, int flags)
+{
+ if (folder == NULL || folder->_open == NULL)
+ return ENOSYS;
+ return folder->_open (folder, flags);
+}
+
+int
+mu_folder_close (mu_folder_t folder)
+{
+ if (folder == NULL || folder->_close == NULL)
+ return ENOSYS;
+ return folder->_close (folder);
+}
+
+int
+mu_folder_set_stream (mu_folder_t folder, mu_stream_t stream)
+{
+ if (folder == NULL)
+ return EINVAL;
+ if (folder->stream)
+ mu_stream_destroy (&folder->stream);
+ folder->stream = stream;
+ return 0;
+}
+
+int
+mu_folder_get_stream (mu_folder_t folder, mu_stream_t *pstream)
+{
+ /* FIXME: Deprecation warning */
+ if (folder == NULL)
+ return EINVAL;
+ if (pstream == NULL)
+ return MU_ERR_OUT_PTR_NULL;
+ *pstream = folder->stream;
+ return 0;
+}
+
+int
+mu_folder_get_streamref (mu_folder_t folder, mu_stream_t *pstream)
+{
+ if (folder == NULL)
+ return EINVAL;
+ if (pstream == NULL)
+ return MU_ERR_OUT_PTR_NULL;
+ return mu_streamref_create (pstream, folder->stream);
+}
+
+int
+mu_folder_set_authority (mu_folder_t folder, mu_authority_t authority)
+{
+ if (folder == NULL)
+ return EINVAL;
+ if (folder->authority)
+ mu_authority_destroy (&folder->authority, folder);
+ folder->authority = authority;
+ return 0;
+}
+
+int
+mu_folder_get_authority (mu_folder_t folder, mu_authority_t *pauthority)
+{
+ if (folder == NULL)
+ return EINVAL;
+ if (pauthority == NULL)
+ return MU_ERR_OUT_PTR_NULL;
+ *pauthority = folder->authority;
+ return 0;
+}
+
+int
+mu_folder_get_observable (mu_folder_t folder, mu_observable_t *pobservable)
+{
+ if (folder == NULL)
+ return EINVAL;
+ if (pobservable == NULL)
+ return MU_ERR_OUT_PTR_NULL;
+
+ if (folder->observable == NULL)
+ {
+ int status = mu_observable_create (&folder->observable, folder);
+ if (status != 0)
+ return status;
+ }
+ *pobservable = folder->observable;
+ return 0;
+}
+
+int
+mu_folder_set_match (mu_folder_t folder, mu_folder_match_fp pmatch)
+{
+ if (folder == NULL)
+ return EINVAL;
+ folder->_match = pmatch;
+ return 0;
+}
+
+int
+mu_folder_get_match (mu_folder_t folder, mu_folder_match_fp *pmatch)
+{
+ if (folder == NULL)
+ return EINVAL;
+ if (pmatch == NULL)
+ return MU_ERR_OUT_PTR_NULL;
+ *pmatch = folder->_match;
+ return 0;
+}
+
+int
+mu_folder_has_debug (mu_folder_t folder)
+{
+ if (folder == NULL)
+ return 0;
+
+ return folder->debug ? 1 : 0;
+}
+
+int
+mu_folder_set_debug (mu_folder_t folder, mu_debug_t debug)
+{
+ if (folder == NULL)
+ return EINVAL;
+ if (folder->debug)
+ mu_debug_destroy (&folder->debug, folder);
+ folder->debug = debug;
+ return 0;
+}
+
+int
+mu_folder_get_debug (mu_folder_t folder, mu_debug_t *pdebug)
+{
+ if (folder == NULL)
+ return EINVAL;
+ if (pdebug == NULL)
+ return MU_ERR_OUT_PTR_NULL;
+ if (folder->debug == NULL)
+ {
+ int status = mu_debug_create (&folder->debug, folder);
+ if (status != 0)
+ return status;
+ }
+ *pdebug = folder->debug;
+ return 0;
+}
+
+void
+mu_list_response_free (void *data)
+{
+ struct mu_list_response *f = data;
+ free (f->name);
+ free (f);
+}
+
+int
+mu_folder_list (mu_folder_t folder, const char *dirname, void *pattern,
+ size_t max_level,
+ mu_list_t *pflist)
+{
+ return mu_folder_enumerate (folder, dirname, pattern, 0, max_level,
+ pflist, NULL, NULL);
+}
+
+int
+mu_folder_enumerate (mu_folder_t folder, const char *name,
+ void *pattern, int flags,
+ size_t max_level,
+ mu_list_t *pflist,
+ mu_folder_enumerate_fp enumfun, void *enumdata)
+{
+ int status;
+ if (folder == NULL || folder->_list == NULL)
+ return EINVAL;
+ else
+ {
+ mu_list_t list = NULL;
+
+ if (pflist)
+ {
+ status = mu_list_create (&list);
+ if (status)
+ return status;
+ *pflist = list;
+ mu_list_set_destroy_item (list, mu_list_response_free);
+ }
+ else if (!enumfun)
+ return EINVAL;
+
+ status = folder->_list (folder, name, pattern, flags, max_level,
+ list, enumfun, enumdata);
+ if (status)
+ mu_list_destroy (pflist);
+ }
+ return status;
+}
+
+int
+mu_folder_lsub (mu_folder_t folder, const char *dirname, const char *basename,
+ mu_list_t *pflist)
+{
+ int status;
+
+ if (folder == NULL || folder->_lsub == NULL)
+ return ENOSYS;
+ else
+ {
+ status = mu_list_create (pflist);
+ if (status)
+ return status;
+ mu_list_set_destroy_item (*pflist, mu_list_response_free);
+ status = folder->_lsub (folder, dirname, basename, *pflist);
+ }
+ return status;
+}
+
+int
+mu_folder_subscribe (mu_folder_t folder, const char *name)
+{
+ if (folder == NULL || folder->_subscribe == NULL)
+ return EINVAL;
+ return folder->_subscribe (folder, name);
+}
+
+int
+mu_folder_unsubscribe (mu_folder_t folder, const char *name)
+{
+ if (folder == NULL || folder->_unsubscribe == NULL)
+ return EINVAL;
+ return folder->_unsubscribe (folder, name);
+}
+
+int
+mu_folder_delete (mu_folder_t folder, const char *name)
+{
+ if (folder == NULL || folder->_delete == NULL)
+ return ENOSYS;
+ return folder->_delete (folder, name);
+}
+
+int
+mu_folder_rename (mu_folder_t folder, const char *oldname, const char *newname)
+{
+ if (folder == NULL || folder->_rename == NULL)
+ return ENOSYS;
+ return folder->_rename (folder, oldname, newname);
+}
+
+int
+mu_folder_get_url (mu_folder_t folder, mu_url_t *purl)
+{
+ if (folder == NULL)
+ return EINVAL;
+ if (purl == NULL)
+ return MU_ERR_OUT_PTR_NULL;
+ *purl = folder->url;
+ return 0;
+}
+
+static int
+is_known_folder (mu_url_t url, mu_folder_t *pfolder)
+{
+ int ret = 0;
+ mu_folder_t folder = NULL;
+ mu_iterator_t iterator;
+
+ if (url == NULL || pfolder == NULL)
+ return ret;
+
+ if (mu_list_get_iterator (known_folder_list, &iterator) != 0)
+ return ret;
+
+ for (mu_iterator_first (iterator); !mu_iterator_is_done (iterator);
+ mu_iterator_next (iterator))
+ {
+ mu_iterator_current (iterator, (void **)&folder);
+ /* Check if the same URL type. */
+ if (folder && folder->url
+ && mu_url_is_same_scheme (url, folder->url)
+ && mu_url_is_same_user (url, folder->url)
+ && mu_url_is_same_host (url, folder->url)
+ && mu_url_is_same_path (url, folder->url)
+ && mu_url_is_same_port (url, folder->url))
+ {
+ ret = 1;
+ break;
+ }
+ }
+ if (ret)
+ *pfolder = folder;
+ mu_iterator_destroy (&iterator);
+ return ret;
+}
+
diff --git a/libmailutils/freeitem.c b/libmailutils/freeitem.c
new file mode 100644
index 000000000..c268ea455
--- /dev/null
+++ b/libmailutils/freeitem.c
@@ -0,0 +1,29 @@
+/* GNU Mailutils -- a suite of utilities for electronic mail
+ Copyright (C) 2010 Free Software Foundation, Inc.
+
+ This library is free software; you can redistribute it and/or
+ modify it under the terms of the GNU Lesser General Public
+ License as published by the Free Software Foundation; either
+ version 3 of the License, or (at your option) any later version.
+
+ This library 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
+ Lesser General Public License for more details.
+
+ You should have received a copy of the GNU Lesser General
+ Public License along with this library; if not, write to the
+ Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor,
+ Boston, MA 02110-1301 USA */
+
+#ifdef HAVE_CONFIG_H
+# include <config.h>
+#endif
+#include <stdlib.h>
+
+/* Default destroy_item function. */
+void
+mu_list_free_item (void *item)
+{
+ free (item);
+}
diff --git a/libmailutils/gdebug.c b/libmailutils/gdebug.c
new file mode 100644
index 000000000..3d1a54c38
--- /dev/null
+++ b/libmailutils/gdebug.c
@@ -0,0 +1,263 @@
+/* GNU Mailutils -- a suite of utilities for electronic mail
+ Copyright (C) 1999, 2000, 2001, 2004, 2005, 2007, 2008, 2009, 2010
+ Free Software Foundation, Inc.
+
+ This library is free software; you can redistribute it and/or
+ modify it under the terms of the GNU Lesser General Public
+ License as published by the Free Software Foundation; either
+ version 3 of the License, or (at your option) any later version.
+
+ This library 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
+ Lesser General Public License for more details.
+
+ You should have received a copy of the GNU Lesser General
+ Public License along with this library; if not, write to the
+ Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor,
+ Boston, MA 02110-1301 USA */
+
+#ifdef HAVE_CONFIG_H
+# include <config.h>
+#endif
+
+#include <errno.h>
+#include <stdlib.h>
+#include <stdio.h>
+#include <string.h>
+#include <mailutils/cctype.h>
+#include <mailutils/assoc.h>
+#include <mailutils/error.h>
+#include <mailutils/errno.h>
+#include <mailutils/argcv.h>
+#include <mailutils/debug.h>
+#include <mailutils/cfg.h>
+#include <mailutils/nls.h>
+
+int mu_debug_line_info = 0;
+
+struct debug_level
+{
+ unsigned level;
+};
+
+static mu_assoc_t debug_table;
+
+mu_log_level_t
+mu_global_debug_level (const char *object_name)
+{
+ struct debug_level *p = mu_assoc_ref (debug_table, object_name);
+ if (p)
+ return p->level;
+ return 0;
+}
+
+int
+mu_global_debug_set_level (const char *object_name, mu_log_level_t level)
+{
+ int rc;
+ struct debug_level *dbg;
+
+ if (!debug_table)
+ {
+ rc = mu_assoc_create (&debug_table, sizeof(struct debug_level), 0);
+ if (rc)
+ return rc;
+ }
+
+ rc = mu_assoc_ref_install (debug_table, object_name, (void**) &dbg);
+ if (rc == 0 || rc == MU_ERR_EXISTS)
+ dbg->level = level;
+ return rc;
+}
+
+int
+mu_global_debug_clear_level (const char *object_name)
+{
+ int rc = 0;
+
+ if (!object_name)
+ mu_assoc_clear (debug_table);
+ else
+ rc = mu_assoc_remove (debug_table, object_name);
+ return rc;
+}
+
+int
+decode_debug_level (const char *p, int *lev)
+{
+ if (strcmp (p, "error") == 0)
+ *lev = MU_DEBUG_ERROR;
+ else if (strncmp (p, "trace", 5) == 0 && mu_isdigit (p[5]) && p[6] == 0)
+ *lev = MU_DEBUG_TRACE0 + atoi (p + 5);
+ else if (strcmp (p, "proto") == 0)
+ *lev = MU_DEBUG_PROT;
+ else
+ return 1;
+ return 0;
+}
+
+int
+mu_debug_level_from_string (const char *string, mu_log_level_t *plev,
+ mu_debug_t debug)
+{
+ char *q;
+ unsigned level = MU_DEBUG_INHERIT;
+
+ if (mu_isdigit (*string))
+ {
+ level = strtoul (string, &q, 0);
+ if (*q)
+ {
+ mu_cfg_format_error (debug, MU_DEBUG_ERROR,
+ _("invalid debugging specification `%s': "
+ "expected levels or number after `=', "
+ "but found `%s'"),
+ string, string);
+ return MU_ERR_FAILURE;
+ }
+ }
+ else
+ {
+ char *p = strdup (string);
+ size_t len = strlen (p);
+ if (len > 0 && p[len-1] == '\n')
+ p[len-1] = 0;
+ for (q = strtok (p, ","); q; q = strtok (NULL, ","))
+ {
+ int flag;
+ int revert = 0;
+ int upto = 0;
+
+ if (*q == '!')
+ {
+ q++;
+ revert = 1;
+ }
+ if (*q == '<')
+ {
+ q++;
+ upto = 1;
+ }
+
+ if (decode_debug_level (q, &flag))
+ mu_cfg_format_error (debug, MU_DEBUG_ERROR,
+ _("invalid debugging level `%s'"),
+ q);
+ else if (revert)
+ {
+ if (upto)
+ level &= ~MU_DEBUG_LEVEL_UPTO (flag);
+ else
+ level &= ~MU_DEBUG_LEVEL_MASK (flag);
+ }
+ else
+ {
+ if (upto)
+ level |= MU_DEBUG_LEVEL_UPTO (flag);
+ else
+ level |= MU_DEBUG_LEVEL_MASK (flag);
+ }
+ }
+ free (p);
+ }
+ *plev = level;
+ return 0;
+}
+
+int
+mu_global_debug_from_string (const char *string, const char *errpfx)
+{
+ int rc;
+ int argc;
+ char **argv;
+ int i;
+
+ rc = mu_argcv_get (string, ";", NULL, &argc, &argv);
+ if (rc)
+ return rc;
+
+ for (i = 0; i < argc; i++)
+ {
+ char *p;
+ mu_log_level_t level = MU_DEBUG_INHERIT;
+ char *object_name = argv[i];
+
+ for (p = object_name; *p && *p != '='; p++)
+ ;
+
+ if (*p == '=')
+ {
+ /* FIXME: Use mu_debug_level_from_string */
+ char *q;
+
+ *p++ = 0;
+ if (mu_isdigit (*p))
+ {
+ level = strtoul (p, &q, 0);
+ if (*q)
+ {
+ mu_error ("%s: invalid debugging specification `%s': "
+ "expected levels or number after `=', "
+ "but found `%s'",
+ errpfx, argv[i], p);
+ break;
+ }
+ }
+ else
+ {
+ char *q;
+ for (q = strtok (p, ","); q; q = strtok (NULL, ","))
+ {
+ int flag;
+ int revert = 0;
+ int upto = 0;
+
+ if (*q == '!')
+ {
+ q++;
+ revert = 1;
+ }
+ if (*q == '<')
+ {
+ q++;
+ upto = 1;
+ }
+
+ if (decode_debug_level (q, &flag))
+ mu_error ("%s: invalid debugging level `%s'",
+ errpfx, q);
+ else if (revert)
+ {
+ if (upto)
+ level &= ~MU_DEBUG_LEVEL_UPTO (flag);
+ else
+ level &= ~MU_DEBUG_LEVEL_MASK (flag);
+ }
+ else
+ {
+ if (upto)
+ level |= MU_DEBUG_LEVEL_UPTO (flag);
+ else
+ level |= MU_DEBUG_LEVEL_MASK (flag);
+ }
+ }
+ }
+ }
+ else
+ level |= MU_DEBUG_LEVEL_UPTO (MU_DEBUG_PROT);
+
+ if (p[-1] == ':')
+ {
+ p[-1] = 0;
+ level &= ~MU_DEBUG_INHERIT;
+ }
+
+ mu_global_debug_set_level (object_name, level);
+ }
+
+ mu_argcv_free (argc, argv);
+ return 0;
+}
+
+
diff --git a/libmailutils/gocs.c b/libmailutils/gocs.c
new file mode 100644
index 000000000..40bde9d0d
--- /dev/null
+++ b/libmailutils/gocs.c
@@ -0,0 +1,397 @@
+/* GNU Mailutils -- a suite of utilities for electronic mail
+ Copyright (C) 2007, 2008, 2009, 2010 Free Software Foundation, Inc.
+
+ This library is free software; you can redistribute it and/or
+ modify it under the terms of the GNU Lesser General Public
+ License as published by the Free Software Foundation; either
+ version 3 of the License, or (at your option) any later version.
+
+ This library 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
+ Lesser General Public License for more details.
+
+ You should have received a copy of the GNU Lesser General
+ Public License along with this library; if not, write to the
+ Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor,
+ Boston, MA 02110-1301 USA */
+
+#ifdef HAVE_CONFIG_H
+# include <config.h>
+#endif
+#include <stdlib.h>
+#include <string.h>
+#include <mailutils/types.h>
+#include <mailutils/gocs.h>
+#include <mailutils/mailbox.h>
+#include <mailutils/locker.h>
+#include <mailutils/mutil.h>
+#include <mailutils/mailer.h>
+#include <mailutils/error.h>
+#include <mailutils/errno.h>
+#include <mailutils/nls.h>
+#include <mailutils/debug.h>
+#include <mailutils/syslog.h>
+#include <mailutils/registrar.h>
+#include <syslog.h>
+
+int mu_load_user_rcfile = 1;
+int mu_load_site_rcfile = 1;
+char *mu_load_rcfile = NULL;
+
+
+int
+mu_gocs_dummy (enum mu_gocs_op op, void *data)
+{
+ return 0;
+}
+
+int
+mu_gocs_mailbox_init (enum mu_gocs_op op, void *data)
+{
+ int rc;
+ struct mu_gocs_mailbox *p = data;
+
+ if (op == mu_gocs_op_set && p)
+ {
+ if (p->mail_spool)
+ {
+ rc = mu_set_mail_directory (p->mail_spool);
+ if (rc)
+ mu_error (_("cannot set mail directory name to `%s': %s"),
+ p->mail_spool, mu_strerror (rc));
+ free (p->mail_spool);
+ p->mail_spool = NULL;
+ }
+ if (p->mailbox_pattern)
+ {
+ rc = mu_set_mailbox_pattern (p->mailbox_pattern);
+ if (rc)
+ mu_error (_("cannot set mailbox pattern to `%s': %s"),
+ p->mailbox_pattern, mu_strerror (rc));
+ free (p->mailbox_pattern);
+ p->mailbox_pattern = NULL;
+ }
+ if (p->mailbox_type)
+ {
+ if (mu_registrar_set_default_scheme (p->mailbox_type))
+ mu_error (_("invalid mailbox type: %s"), p->mailbox_type);
+ free (p->mailbox_type);
+ p->mailbox_type = NULL;
+ }
+ }
+ return 0;
+}
+
+int
+mu_gocs_locking_init (enum mu_gocs_op op, void *data)
+{
+ struct mu_gocs_locking *p = data;
+
+ if (!(op == mu_gocs_op_set && p))
+ return 0;
+
+ if (p->lock_flags)
+ {
+ int flags = 0;
+ char *s;
+
+ for (s = p->lock_flags; *s; s++)
+ {
+ switch (*s)
+ {
+ case 'E':
+ flags |= MU_LOCKER_EXTERNAL;
+ break;
+
+ case 'R':
+ flags |= MU_LOCKER_RETRY;
+ break;
+
+ case 'T':
+ flags |= MU_LOCKER_TIME;
+ break;
+
+ case 'P':
+ flags |= MU_LOCKER_PID;
+ break;
+
+ default:
+ mu_error (_("invalid lock flag `%c'"), *s);
+ }
+ }
+ mu_locker_set_default_flags (flags, mu_locker_assign);
+ free (p->lock_flags);
+ p->lock_flags = NULL;
+ }
+
+ if (p->lock_retry_count)
+ {
+ mu_locker_set_default_retry_count (p->lock_retry_count);
+ mu_locker_set_default_flags (MU_LOCKER_RETRY, mu_locker_set_bit);
+ p->lock_retry_count = 0;
+ }
+
+ if (p->lock_retry_timeout)
+ {
+ mu_locker_set_default_retry_timeout (p->lock_retry_timeout);
+ mu_locker_set_default_flags (MU_LOCKER_RETRY, mu_locker_set_bit);
+ p->lock_retry_timeout = 0;
+ }
+
+ if (p->lock_expire_timeout)
+ {
+ mu_locker_set_default_expire_timeout (p->lock_expire_timeout);
+ mu_locker_set_default_flags (MU_LOCKER_EXTERNAL, mu_locker_set_bit);
+ p->lock_expire_timeout = 0;
+ }
+
+ if (p->external_locker)
+ {
+ mu_locker_set_default_external_program (p->external_locker);
+ mu_locker_set_default_flags (MU_LOCKER_TIME, mu_locker_set_bit);
+ free (p->external_locker);
+ p->external_locker = NULL;
+ }
+ return 0;
+}
+
+int
+mu_gocs_source_email_init (enum mu_gocs_op op, void *data)
+{
+ struct mu_gocs_source_email *p = data;
+ int rc;
+
+ if (!(op == mu_gocs_op_set && p))
+ return 0;
+
+ if (p->address)
+ {
+ if ((rc = mu_set_user_email (p->address)) != 0)
+ mu_error (_("invalid email address `%s': %s"),
+ p->address, mu_strerror (rc));
+ free (p->address);
+ p->address = NULL;
+ }
+
+ if (p->domain)
+ {
+ if ((rc = mu_set_user_email_domain (p->domain)) != 0)
+ mu_error (_("invalid email domain `%s': %s"),
+ p->domain, mu_strerror (rc));
+
+ free (p->domain);
+ p->domain = NULL;
+ }
+ return 0;
+}
+
+int
+mu_gocs_mailer_init (enum mu_gocs_op op, void *data)
+{
+ struct mu_gocs_mailer *p = data;
+ int rc;
+
+ if (!(op == mu_gocs_op_set && p))
+ return 0;
+
+ if (p->mailer)
+ {
+ if ((rc = mu_mailer_set_url_default (p->mailer)) != 0)
+ mu_error (_("invalid mailer URL `%s': %s"),
+ p->mailer, mu_strerror (rc));
+ free (p->mailer);
+ p->mailer = NULL;
+ }
+ return 0;
+}
+
+int
+mu_gocs_logging_init (enum mu_gocs_op op, void *data)
+{
+ struct mu_gocs_logging *p = data;
+
+ if (op == mu_gocs_op_set)
+ {
+ if (!p)
+ {
+ static struct mu_gocs_logging default_gocs_logging = { LOG_FACILITY };
+ p = &default_gocs_logging;
+ }
+
+ if (p->facility)
+ {
+ mu_log_facility = p->facility;
+ mu_debug_default_printer = mu_debug_syslog_printer;
+ }
+ else
+ mu_debug_default_printer = mu_debug_stderr_printer;
+
+ if (p->tag)
+ mu_log_tag = strdup (p->tag);
+ }
+ return 0;
+}
+
+int
+mu_gocs_debug_init (enum mu_gocs_op op, void *data)
+{
+ if (op == mu_gocs_op_set && data)
+ {
+ struct mu_gocs_debug *p = data;
+ if (p->string && p->errpfx)
+ {
+ mu_global_debug_from_string (p->string, p->errpfx);
+ free (p->errpfx);
+ }
+ if (p->line_info >= 0)
+ mu_debug_line_info = p->line_info;
+ }
+ return 0;
+}
+
+
+struct mu_gocs_entry
+{
+ const char *name;
+ mu_gocs_init_fp init;
+};
+
+#define MAX_GOCS 512
+
+static struct mu_gocs_entry _gocs_table[MAX_GOCS];
+
+void
+mu_gocs_register (const char *capa, mu_gocs_init_fp init)
+{
+ int i;
+ for (i = 0; _gocs_table[i].name; i++)
+ if (i == MAX_GOCS-1)
+ {
+ mu_error (_("gocs table overflow"));
+ abort ();
+ }
+ _gocs_table[i].name = capa;
+ _gocs_table[i].init = init;
+}
+
+int
+mu_gocs_enumerate (mu_list_action_t action, void *data)
+{
+ int i;
+
+ for (i = 0; _gocs_table[i].name; i++)
+ {
+ int rc = action ((void*) _gocs_table[i].name, data);
+ if (rc)
+ return rc;
+ }
+ return 0;
+}
+
+static mu_gocs_init_fp
+find_init_function (struct mu_gocs_entry *tab, const char *capa)
+{
+ for (; tab->name; tab++)
+ if (strcmp (tab->name, capa) == 0)
+ return tab->init;
+ return NULL;
+}
+
+static struct mu_gocs_entry std_gocs_table[] = {
+ { "common", mu_gocs_dummy },
+ { "license", mu_gocs_dummy },
+ { "mailbox", mu_gocs_mailbox_init },
+ { "locking", mu_gocs_locking_init },
+ { "address", mu_gocs_source_email_init },
+ { "mailer", mu_gocs_mailer_init },
+ { "logging", mu_gocs_logging_init },
+ { "debug", mu_gocs_debug_init },
+ { "auth", mu_gocs_dummy },
+ { NULL }
+};
+
+void
+mu_gocs_register_std (const char *name)
+{
+ mu_gocs_init_fp init = find_init_function (std_gocs_table, name);
+ if (!init)
+ {
+ mu_error (_("INTERNAL ERROR at %s:%d: unknown standard capability `%s'"),
+ __FILE__, __LINE__, name);
+ abort ();
+ }
+ mu_gocs_register (name, init);
+}
+
+
+struct mu_gocs_data
+{
+ char *capa;
+ void *data;
+};
+
+static mu_list_t /* of struct mu_gocs_data */ data_list;
+
+static int
+_gocs_comp (const void *a, const void *b)
+{
+ const struct mu_gocs_data *da = a, *db = b;
+ return !(strcmp (da->capa, db->capa) == 0 && da->data == db->data);
+}
+
+void
+mu_gocs_store (char *capa, void *data)
+{
+ struct mu_gocs_data *s;
+ if (!data_list)
+ {
+ mu_list_create (&data_list);
+ mu_list_set_destroy_item (data_list, mu_list_free_item);
+ mu_list_set_comparator (data_list, _gocs_comp);
+ }
+ s = malloc (sizeof *s);
+ if (!s)
+ {
+ mu_error ("%s", mu_strerror (ENOMEM));
+ exit (1);
+ }
+ s->capa = capa;
+ s->data = data;
+ if (mu_list_locate (data_list, s, NULL) == 0)
+ free (s);
+ else
+ mu_list_prepend (data_list, s);
+}
+
+int
+_gocs_flush (void *item, void *data)
+{
+ struct mu_gocs_data *s = item;
+ mu_gocs_init_fp initfun = find_init_function (_gocs_table, s->capa);
+
+ if (!initfun)
+ {
+ mu_error (_("INTERNAL ERROR at %s:%d: unknown capability `%s'"),
+ __FILE__, __LINE__, s->capa);
+ abort ();
+ }
+
+ if (initfun (mu_gocs_op_set, s->data))
+ {
+ mu_error (_("initialization of GOCS `%s' failed"), s->capa);
+ return 1;
+ }
+
+ return 0;
+}
+
+void
+mu_gocs_flush ()
+{
+ int i;
+ mu_list_do (data_list, _gocs_flush, NULL);
+
+ for (i = 0; _gocs_table[i].name; i++)
+ _gocs_table[i].init (mu_gocs_op_flush, NULL);
+}
diff --git a/libmailutils/hdritr.c b/libmailutils/hdritr.c
new file mode 100644
index 000000000..595735ffa
--- /dev/null
+++ b/libmailutils/hdritr.c
@@ -0,0 +1,162 @@
+/* GNU Mailutils -- a suite of utilities for electronic mail
+ Copyright (C) 2008, 2010 Free Software Foundation, Inc.
+
+ This library is free software; you can redistribute it and/or
+ modify it under the terms of the GNU Lesser General Public
+ License as published by the Free Software Foundation; either
+ version 3 of the License, or (at your option) any later version.
+
+ This library 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
+ Lesser General Public License for more details.
+
+ You should have received a copy of the GNU Lesser General
+ Public License along with this library; if not, write to the
+ Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor,
+ Boston, MA 02110-1301 USA */
+
+/* Mail header iterators. */
+
+#ifdef HAVE_CONFIG_H
+# include <config.h>
+#endif
+#include <stdlib.h>
+#include <string.h>
+
+#include <mailutils/sys/header.h>
+#include <mailutils/errno.h>
+
+struct header_iterator
+{
+ mu_header_t header;
+ size_t index;
+};
+
+static int
+hdr_first (void *owner)
+{
+ struct header_iterator *itr = owner;
+ itr->index = 1;
+ return 0;
+}
+
+static int
+hdr_next (void *owner)
+{
+ struct header_iterator *itr = owner;
+ itr->index++;
+ return 0;
+}
+
+static int
+hdr_getitem (void *owner, void **pret, const void **pkey)
+{
+ struct header_iterator *itr = owner;
+ int rc;
+ size_t count;
+
+ rc = mu_header_get_field_count (itr->header, &count);
+ if (rc)
+ return rc;
+ if (itr->index > count)
+ return MU_ERR_NOENT;
+
+ rc = mu_header_sget_field_name (itr->header, itr->index,
+ (const char**) pkey);
+ if (rc == 0)
+ {
+ if (pkey)
+ rc = mu_header_sget_field_value (itr->header, itr->index,
+ (const char**) pret);
+ }
+ return rc;
+}
+
+static int
+hdr_finished_p (void *owner)
+{
+ struct header_iterator *itr = owner;
+ size_t count;
+
+ if (mu_header_get_field_count (itr->header, &count))
+ return 1;
+ return itr->index > count;
+}
+
+static int
+hdr_destroy (mu_iterator_t iterator, void *data)
+{
+ struct header_iterator *itr = data;
+ mu_iterator_detach (&itr->header->itr, iterator);
+ free (data);
+ return 0;
+}
+
+static int
+hdr_curitem_p (void *owner, void *item)
+{
+ void *ptr;
+
+ if (hdr_getitem (owner, &ptr, NULL))
+ return 0;
+ return ptr == item;
+}
+
+static int
+hdr_data_dup (void **ptr, void *owner)
+{
+ struct header_iterator *itr = owner;
+
+ *ptr = malloc (sizeof (struct header_iterator));
+ if (*ptr == NULL)
+ return ENOMEM;
+ memcpy (*ptr, owner, sizeof (struct header_iterator));
+ mu_iterator_attach (&itr->header->itr, *ptr);
+ return 0;
+}
+
+int
+mu_header_get_iterator (mu_header_t hdr, mu_iterator_t *piterator)
+{
+ mu_iterator_t iterator;
+ int status;
+ struct header_iterator *itr;
+
+ if (!hdr)
+ return EINVAL;
+
+ itr = calloc (1, sizeof *itr);
+ if (!itr)
+ return ENOMEM;
+ itr->header = hdr;
+ itr->index = 1;
+
+ status = mu_iterator_create (&iterator, itr);
+ if (status)
+ {
+ free (itr);
+ return status;
+ }
+
+ mu_iterator_set_first (iterator, hdr_first);
+ mu_iterator_set_next (iterator, hdr_next);
+ mu_iterator_set_getitem (iterator, hdr_getitem);
+ mu_iterator_set_finished_p (iterator, hdr_finished_p);
+ mu_iterator_set_curitem_p (iterator, hdr_curitem_p);
+ mu_iterator_set_destroy (iterator, hdr_destroy);
+ mu_iterator_set_dup (iterator, hdr_data_dup);
+
+ mu_iterator_attach (&hdr->itr, iterator);
+
+ *piterator = iterator;
+ return 0;
+}
+
+
+
+
+
+
+
+
diff --git a/libmailutils/header.c b/libmailutils/header.c
new file mode 100644
index 000000000..2861c676b
--- /dev/null
+++ b/libmailutils/header.c
@@ -0,0 +1,1228 @@
+/* GNU Mailutils -- a suite of utilities for electronic mail
+ Copyright (C) 1999, 2000, 2001, 2004, 2005, 2007, 2008, 2009, 2010
+ Free Software Foundation, Inc.
+
+ This library is free software; you can redistribute it and/or
+ modify it under the terms of the GNU Lesser General Public
+ License as published by the Free Software Foundation; either
+ version 3 of the License, or (at your option) any later version.
+
+ This library 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
+ Lesser General Public License for more details.
+
+ You should have received a copy of the GNU Lesser General
+ Public License along with this library; if not, write to the
+ Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor,
+ Boston, MA 02110-1301 USA */
+
+/* This all header business needs a good rewrite.
+ * -- Alain Magloire, 2000-07-03 (rev. 1.21)
+ *
+ * It's the job that's never started as takes longest to finish.
+ * -- Hamfast Gamgee, some time in the Third Age
+ *
+ * It took almost 7 years to gather the courage to start the job,
+ * and only one day to finish it.
+ * -- Sergey Poznyakoff, 2007-06-24
+ */
+
+#ifdef HAVE_CONFIG_H
+# include <config.h>
+#endif
+
+#include <string.h>
+#include <stdlib.h>
+#include <stdio.h>
+#include <errno.h>
+
+#ifdef HAVE_STRINGS_H
+# include <strings.h>
+#endif
+
+#include <mailutils/stream.h>
+#include <mailutils/address.h>
+#include <mailutils/mutil.h>
+#include <mailutils/errno.h>
+#include <mailutils/cstr.h>
+#include <mailutils/sys/header_stream.h>
+#include <mailutils/sys/header.h>
+
+#define HEADER_MODIFIED 0x01
+#define HEADER_INVALIDATE 0x02
+
+#define HEADER_SET_MODIFIED(h) \
+ ((h)->flags |= (HEADER_MODIFIED|HEADER_INVALIDATE))
+
+
+/* mu_hdrent manipulation */
+
+#define MU_HDRENT_NAME(hp,ep) ((hp)->spool + (ep)->fn)
+#define MU_HDRENT_VALUE(hp,ep) ((hp)->spool + (ep)->fv)
+#define MU_STR_SIZE(nlen,vlen) ((nlen) + 2 + (vlen) + 1)
+
+static struct mu_hdrent *
+mu_hdrent_nth (struct _mu_header *hdr, int n)
+{
+ struct mu_hdrent *p;
+ for (p = hdr->head; p; p = p->next)
+ if (n-- == 1)
+ break;
+ return p;
+}
+
+static struct mu_hdrent *
+mu_hdrent_find (struct _mu_header *hdr, const char *name, int pos)
+{
+ struct mu_hdrent *p;
+
+ if (pos > 0)
+ {
+ for (p = hdr->head; p; p = p->next)
+ if (mu_c_strcasecmp (MU_HDRENT_NAME (hdr,p), name) == 0 && pos-- == 1)
+ break;
+ }
+ else if (pos < 0)
+ {
+ for (p = hdr->tail; p; p = p->prev)
+ if (mu_c_strcasecmp (MU_HDRENT_NAME (hdr,p), name) == 0 && ++pos == 0)
+ break;
+ }
+ else
+ p = NULL;
+ return p;
+}
+
+static int
+mu_hdrent_find_stream_pos (struct _mu_header *hdr, mu_off_t pos,
+ struct mu_hdrent **pent, size_t *poff)
+{
+ mu_off_t x;
+ struct mu_hdrent *p;
+
+ for (p = hdr->head, x = 0; p; p = p->next)
+ {
+ size_t strsize = MU_STR_SIZE (p->nlen, p->vlen);
+ if (x <= pos && pos < x + strsize)
+ {
+ *poff = pos - x;
+ *pent = p;
+ return 0;
+ }
+ x += strsize;
+ }
+ if (x == pos && hdr->tail)
+ {
+ /* To supply the trailing '\n' */
+ p = hdr->tail;
+ *pent = p;
+ *poff = MU_STR_SIZE (p->nlen, p->vlen) - 1;
+ return 0;
+ }
+ return 1;
+}
+
+static void
+mu_hdrent_count (struct _mu_header *hdr, size_t *pcount, size_t *psize,
+ size_t *plines)
+{
+ if (hdr->flags & HEADER_INVALIDATE)
+ {
+ size_t size = 0;
+ size_t count = 0;
+ size_t lines = 0;
+ struct mu_hdrent *p;
+ for (p = hdr->head; p; p = p->next)
+ {
+ count++;
+ size += MU_STR_SIZE (p->nlen, p->vlen);
+ lines += p->nlines;
+ }
+
+ hdr->numhdr = count;
+ hdr->numlines = lines;
+ hdr->size = size;
+ hdr->flags &= ~HEADER_INVALIDATE;
+ }
+
+ *pcount = hdr->numhdr;
+ *psize = hdr->size;
+ *plines = hdr->numlines;
+}
+
+static void
+mu_hdrent_remove (struct _mu_header *hdr, struct mu_hdrent *ent)
+{
+ struct mu_hdrent *p = ent->prev;
+ if (p)
+ p->next = ent->next;
+ else
+ hdr->head = ent->next;
+
+ p = ent->next;
+ if (p)
+ p->prev = ent->prev;
+ else
+ hdr->tail = ent->prev;
+}
+
+static void
+mu_hdrent_prepend (struct _mu_header *hdr, struct mu_hdrent *ent)
+{
+ struct mu_hdrent *p = hdr->head;
+ ent->prev = NULL;
+ ent->next = p;
+ if (p)
+ p->prev = ent;
+ else
+ hdr->tail = ent;
+ hdr->head = ent;
+}
+
+static void
+mu_hdrent_append (struct _mu_header *hdr, struct mu_hdrent *ent)
+{
+ struct mu_hdrent *p = hdr->tail;
+ ent->next = NULL;
+ ent->prev = p;
+ if (p)
+ p->next = ent;
+ else
+ hdr->head = ent;
+ hdr->tail = ent;
+}
+
+static int
+mu_hdrent_insert (struct _mu_header *hdr, struct mu_hdrent *ent,
+ const char *name, int pos, int before)
+{
+ struct mu_hdrent *p;
+ struct mu_hdrent *ref = mu_hdrent_find (hdr, name, pos);
+ if (!ref)
+ return MU_ERR_NOENT;
+
+ if (before)
+ {
+ ref = ref->prev;
+ if (!ref)
+ {
+ mu_hdrent_prepend (hdr, ent);
+ return 0;
+ }
+ }
+
+ p = ref->next;
+ if (!p)
+ {
+ mu_hdrent_append (hdr, ent);
+ return 0;
+ }
+
+ ent->next = p;
+ p->prev = ent;
+ ent->prev = ref;
+ ref->next = ent;
+
+ return 0;
+}
+
+#define SPOOLBLKSIZ 1024
+
+static struct mu_hdrent *
+mu_hdrent_create (struct _mu_header *ph,
+ struct mu_hdrent *ent,
+ const char *name, size_t nsize,
+ const char *value, size_t vsize)
+{
+ size_t strsize;
+ size_t sizeleft;
+ const char *p;
+
+ if (!ent)
+ {
+ ent = calloc (1, sizeof (*ent));
+ if (!ent)
+ return NULL;
+ }
+
+ strsize = MU_STR_SIZE (nsize, vsize);
+ sizeleft = ph->spool_size - ph->spool_used;
+
+ /* Ensure there is enough space in spool */
+ if (sizeleft < strsize)
+ {
+ char *newp;
+ size_t delta = (strsize - sizeleft + SPOOLBLKSIZ - 1) / SPOOLBLKSIZ;
+ delta *= SPOOLBLKSIZ;
+ newp = realloc (ph->spool, ph->spool_size + delta);
+ if (!newp)
+ return 0;
+ ph->spool = newp;
+ ph->spool_size += delta;
+ }
+
+ /* Copy header name */
+ ent->fn = ph->spool_used;
+ ent->nlen = nsize;
+ memcpy (ph->spool + ph->spool_used, name, nsize);
+ ph->spool_used += nsize;
+ ph->spool[ph->spool_used++] = 0;
+ ph->spool[ph->spool_used++] = ' ';
+
+ /* Copy header value */
+ ent->fv = ph->spool_used;
+ ent->vlen = vsize;
+ memcpy (ph->spool + ph->spool_used, value, vsize);
+ ph->spool_used += vsize;
+ ph->spool[ph->spool_used++] = 0;
+
+ ent->nlines = 1;
+ for (p = value; p < value + vsize; p++)
+ if (*p == '\n')
+ ent->nlines++;
+
+ return ent;
+}
+
+static void
+mu_hdrent_free_list (struct _mu_header *hdr)
+{
+ struct mu_hdrent *p;
+ for (p = hdr->head; p; )
+ {
+ struct mu_hdrent *next = p->next;
+ free (p);
+ p = next;
+ }
+ hdr->head = hdr->tail = NULL;
+ hdr->spool_used = 0;
+}
+
+
+
+#define ISLWSP(c) (((c) == ' ' || (c) == '\t'))
+
+/* Parsing is done in a rather simple fashion, meaning we just consider an
+ entry to be a field-name an a field-value. So they maybe duplicate of
+ field-name like "Received" they are just put in the array, see _get_value()
+ on how to handle the case. in the case of error .i.e a bad header construct
+ we do a full stop and return what we have so far. */
+
+static int
+header_parse (mu_header_t header, const char *blurb, int len)
+{
+ const char *header_end;
+ const char *header_start;
+ const char *header_start2;
+
+ /* Nothing to parse. */
+ if (blurb == NULL)
+ return 0;
+
+ header->flags |= HEADER_INVALIDATE;
+ mu_hdrent_free_list (header);
+
+ /* Get a header, a header is:
+ field-name LWSP ':'
+ LWSP field-value '\r' '\n'
+ *[ (' ' | '\t') field-value '\r' '\n' ]
+ */
+ /* First loop goes through the blurb */
+ for (header_start = blurb; len > 0; header_start = ++header_end)
+ {
+ const char *fn, *fn_end, *fv, *fv_end;
+ struct mu_hdrent *ent;
+
+ if (header_start[0] == ' '
+ || header_start[0] == '\t'
+ || header_start[0] == '\n')
+ break;
+
+ /* Second loop extract one header field. */
+ for (header_start2 = header_start; len; header_start2 = ++header_end)
+ {
+ header_end = memchr (header_start2, '\n', len);
+ if (header_end == NULL)
+ {
+ header_end = header_start2 + len;
+ len = 0;
+ break;
+ }
+ else
+ {
+ len -= (header_end - header_start2 + 1);
+ if (!len
+ || (header_end[1] != ' '
+ && header_end[1] != '\t'))
+ break; /* New header break the inner for. */
+ }
+ /* *header_end = ' '; smash LF ? NO */
+ }
+
+ /* Now save the header in the data structure. */
+
+ /* Treats unix "From " specially. FIXME: Should we? */
+ if ((header_end - header_start >= 5)
+ && strncmp (header_start, "From ", 5) == 0)
+ {
+ fn = header_start;
+ fn_end = header_start + 5;
+ fv = header_start + 5;
+ fv_end = header_end;
+ }
+ else /* Break the header in key: value */
+ {
+ char *colon = memchr (header_start, ':', header_end - header_start);
+
+ /* Houston we have a problem. */
+ if (colon == NULL)
+ break; /* FIXME: Disregard the rest and bailout. */
+
+ fn = header_start;
+ fn_end = colon;
+ /* Shrink any LWSP after the field name -- CRITICAL for
+ later name comparisons to work correctly! */
+ while (ISLWSP (fn_end[-1]))
+ fn_end--;
+
+ fv = colon + 1;
+ fv_end = header_end;
+
+ /* Skip any LWSP before the field value -- unnecessary, but
+ might make some field values look a little tidier. */
+ while (ISLWSP (fv[0]))
+ fv++;
+ }
+
+ /* Register this header */
+ ent = mu_hdrent_create (header, NULL, fn, fn_end - fn, fv, fv_end - fv);
+ if (!ent)
+ return ENOMEM;
+ mu_hdrent_append (header, ent);
+ } /* for (header_start ...) */
+
+ return 0;
+}
+
+
+static int
+mu_header_fill (mu_header_t header)
+{
+ int status;
+ size_t blurb_len = 0;
+ char *blurb = NULL;
+
+ if (header->spool_used)
+ return 0;
+
+ if (header->_fill == NULL)
+ return 0; /* FIXME: Really? */
+
+ /* Bring in the entire header. */
+ status = header->_fill (header->data, &blurb, &blurb_len);
+ if (status)
+ return status;
+ status = header_parse (header, blurb, blurb_len);
+ free (blurb);
+ return status;
+}
+
+
+
+int
+mu_header_create (mu_header_t *ph, const char *blurb, size_t len)
+{
+ mu_header_t header;
+ int status = 0;
+
+ header = calloc (1, sizeof (*header));
+ if (header == NULL)
+ return ENOMEM;
+
+ status = header_parse (header, blurb, len);
+
+ *ph = header;
+ return status;
+}
+
+void
+mu_header_destroy (mu_header_t *ph)
+{
+ if (ph && *ph)
+ {
+ mu_header_t header = *ph;
+
+ mu_stream_destroy (&header->stream);
+ mu_hdrent_free_list (header);
+ free (header->spool);
+ free (header);
+ *ph = NULL;
+ }
+}
+
+
+int
+mu_header_set_value (mu_header_t header, const char *fn, const char *fv,
+ int replace)
+{
+ int status;
+ struct mu_hdrent *ent = NULL;
+
+ if (header == NULL || fn == NULL)
+ return EINVAL;
+
+ status = mu_header_fill (header);
+ if (status)
+ return status;
+
+ /* An fv of NULL means delete the field, but only do it if replace
+ was also set to true! */
+ if (fv == NULL && !replace)
+ return EINVAL;
+
+ if (replace)
+ {
+ ent = mu_hdrent_find (header, fn, 1);
+ if (ent)
+ {
+ if (fv == NULL)
+ {
+ /* Delete the header */
+ mu_hdrent_remove (header, ent);
+ free (ent);
+ return 0;
+ }
+ mu_hdrent_create (header, ent, fn, strlen (fn), fv, strlen (fv));
+ HEADER_SET_MODIFIED (header);
+ return 0;
+ }
+ else if (fv == NULL)
+ return 0;
+ }
+
+ ent = mu_hdrent_create (header, NULL,
+ fn, strlen (fn), fv, strlen (fv));
+ if (!ent)
+ return ENOMEM;
+ mu_hdrent_prepend (header, ent);
+ HEADER_SET_MODIFIED (header);
+ return 0;
+}
+
+int
+mu_header_remove (mu_header_t header, const char *fn, int n)
+{
+ int status;
+ struct mu_hdrent *ent;
+
+ if (header == NULL || fn == NULL)
+ return EINVAL;
+
+ status = mu_header_fill (header);
+ if (status)
+ return status;
+
+ ent = mu_hdrent_find (header, fn, n);
+ if (!ent)
+ return MU_ERR_NOENT;
+
+ mu_hdrent_remove (header, ent);
+ HEADER_SET_MODIFIED (header);
+ free (ent);
+ return 0;
+}
+
+int
+mu_header_append (mu_header_t header, const char *fn, const char *fv)
+{
+ int status;
+ struct mu_hdrent *ent;
+
+ if (header == NULL || fn == NULL || fv == NULL)
+ return EINVAL;
+
+ status = mu_header_fill (header);
+ if (status)
+ return status;
+
+ ent = mu_hdrent_create (header, NULL, fn, strlen (fn), fv, strlen (fv));
+ if (!ent)
+ return ENOMEM;
+ mu_hdrent_append (header, ent);
+ HEADER_SET_MODIFIED (header);
+ return 0;
+}
+
+int
+mu_header_prepend (mu_header_t header, const char *fn, const char *fv)
+{
+ int status;
+ struct mu_hdrent *ent;
+
+ if (header == NULL || fn == NULL || fv == NULL)
+ return EINVAL;
+
+ status = mu_header_fill (header);
+ if (status)
+ return status;
+
+ ent = mu_hdrent_create (header, NULL, fn, strlen (fn), fv, strlen (fv));
+ if (!ent)
+ return ENOMEM;
+ mu_hdrent_prepend (header, ent);
+ HEADER_SET_MODIFIED (header);
+ return 0;
+}
+
+int
+mu_header_insert (mu_header_t header,
+ const char *fn, const char *fv,
+ const char *ref, int n, int flags)
+{
+ int status;
+ struct mu_hdrent *ent;
+
+ if (header == NULL || fn == NULL || fv == NULL)
+ return EINVAL;
+
+ status = mu_header_fill (header);
+ if (status)
+ return status;
+
+ if (flags & MU_HEADER_REPLACE)
+ {
+ if (!ref)
+ ref = fn;
+ ent = mu_hdrent_find (header, ref, n);
+ mu_hdrent_create (header, ent, fn, strlen (fn), fv, strlen (fv));
+ }
+ else
+ {
+ ent = mu_hdrent_create (header, NULL,
+ fn, strlen (fn), fv, strlen (fv));
+ if (!ent)
+ return ENOMEM;
+ if (ref)
+ return mu_hdrent_insert (header, ent, ref, n,
+ flags & MU_HEADER_BEFORE);
+ else
+ mu_hdrent_prepend (header, ent);
+ }
+ HEADER_SET_MODIFIED (header);
+ return 0;
+}
+
+
+int
+mu_header_sget_value_n (mu_header_t header,
+ const char *name, int n,
+ const char **pval)
+{
+ int status;
+ struct mu_hdrent *ent;
+
+ if (header == NULL || name == NULL)
+ return EINVAL;
+ status = mu_header_fill (header);
+ if (status)
+ return status;
+
+ ent = mu_hdrent_find (header, name, n);
+ if (!ent)
+ return MU_ERR_NOENT;
+
+ *pval = MU_HDRENT_VALUE (header, ent);
+ return 0;
+}
+
+int
+mu_header_aget_value_n (mu_header_t header,
+ const char *name, int n,
+ char **pval)
+{
+ const char *s;
+ int status = mu_header_sget_value_n (header, name, n, &s);
+ if (status == 0)
+ {
+ *pval = strdup (s);
+ if (!*pval)
+ status = ENOMEM;
+ }
+ return status;
+}
+
+int
+mu_header_get_value_n (mu_header_t header, const char *name, int n,
+ char *buffer, size_t buflen, size_t *pn)
+{
+ const char *s;
+ int status = mu_header_sget_value_n (header, name, n, &s);
+ if (status == 0)
+ {
+ size_t slen = strlen (s);
+
+ if (buffer)
+ {
+ if (slen > buflen)
+ slen = buflen;
+ memcpy (buffer, s, slen);
+ buffer[slen] = 0;
+ }
+ if (pn)
+ *pn = slen;
+ }
+ return status;
+}
+
+
+/* Unfolding functions */
+int
+mu_header_get_value_unfold_n (mu_header_t header,
+ const char *name, int n, char *buffer,
+ size_t buflen, size_t *pn)
+{
+ int rc = mu_header_get_value_n (header, name, n, buffer, buflen, pn);
+
+ if (rc == 0)
+ mu_string_unfold (buffer, pn);
+ return rc;
+}
+
+int
+mu_header_aget_value_unfold_n (mu_header_t header, const char *name, int n,
+ char **pvalue)
+{
+ int rc = mu_header_aget_value_n (header, name, n, pvalue);
+ if (rc == 0)
+ mu_string_unfold (*pvalue, NULL);
+ return rc;
+}
+
+
+int
+mu_header_get_address_n (mu_header_t header, const char *name, int n,
+ mu_address_t *addr)
+{
+ const char *value = NULL;
+ int status = mu_header_sget_value_n (header, name, n, &value);
+
+ if (status)
+ return status;
+
+ return mu_address_create (addr, value);
+}
+
+
+int
+mu_header_get_field_count (mu_header_t header, size_t *pcount)
+{
+ size_t count;
+ size_t size;
+ size_t lines;
+ int status;
+
+ if (header == NULL)
+ return EINVAL;
+
+ status = mu_header_fill (header);
+ if (status == 0)
+ {
+ mu_hdrent_count (header, &count, &size, &lines);
+
+ if (pcount)
+ *pcount = count;
+ }
+
+ return status;
+}
+
+int
+mu_header_sget_field_name (mu_header_t header, size_t num, const char **sptr)
+{
+ int status;
+
+ if (header == NULL)
+ return EINVAL;
+
+ status = mu_header_fill (header);
+ if (status == 0)
+ {
+ struct mu_hdrent *ent = mu_hdrent_nth (header, num);
+ if (ent)
+ *sptr = MU_HDRENT_NAME (header, ent);
+ else
+ status = MU_ERR_NOENT;
+ }
+ return status;
+}
+
+int
+mu_header_get_field_name (mu_header_t header, size_t num, char *buffer,
+ size_t buflen, size_t *pn)
+{
+ const char *s;
+ int status = mu_header_sget_field_name (header, num, &s);
+ if (status == 0)
+ {
+ size_t slen = strlen (s);
+
+ if (buffer)
+ {
+ if (slen > buflen)
+ slen = buflen;
+ memcpy (buffer, s, slen);
+ buffer[slen] = 0;
+ }
+ if (pn)
+ *pn = slen;
+ }
+ return status;
+}
+
+int
+mu_header_aget_field_name (mu_header_t header, size_t num, char **pvalue)
+{
+ const char *s;
+ int status = mu_header_sget_field_name (header, num, &s);
+ if (status == 0)
+ {
+ if ((*pvalue = strdup (s)) == NULL)
+ status = ENOMEM;
+ }
+ return status;
+}
+
+
+int
+mu_header_sget_field_value (mu_header_t header, size_t num, const char **sptr)
+{
+ int status;
+
+ if (header == NULL)
+ return EINVAL;
+
+ status = mu_header_fill (header);
+ if (status == 0)
+ {
+ struct mu_hdrent *ent = mu_hdrent_nth (header, num);
+ if (ent)
+ *sptr = MU_HDRENT_VALUE (header, ent);
+ else
+ status = MU_ERR_NOENT;
+ }
+ return status;
+}
+
+int
+mu_header_get_field_value (mu_header_t header, size_t num, char *buffer,
+ size_t buflen, size_t *pn)
+{
+ const char *s;
+ int status = mu_header_sget_field_value (header, num, &s);
+ if (status == 0)
+ {
+ size_t slen = strlen (s);
+
+ if (buffer)
+ {
+ if (slen > buflen)
+ slen = buflen;
+ memcpy (buffer, s, slen);
+ buffer[slen] = 0;
+ }
+ if (pn)
+ *pn = slen;
+ }
+ return status;
+}
+
+int
+mu_header_aget_field_value (mu_header_t header, size_t num, char **pvalue)
+{
+ const char *s;
+ int status = mu_header_sget_field_value (header, num, &s);
+ if (status == 0)
+ {
+ if ((*pvalue = strdup (s)) == NULL)
+ status = ENOMEM;
+ }
+ return status;
+}
+
+int
+mu_header_get_field_value_unfold (mu_header_t header, size_t num, char *buf,
+ size_t buflen, size_t *nwritten)
+{
+ int rc = mu_header_get_field_value (header, num, buf, buflen, nwritten);
+ if (rc == 0)
+ mu_string_unfold (buf, nwritten);
+ return rc;
+}
+
+int
+mu_header_aget_field_value_unfold (mu_header_t header, size_t num,
+ char **pvalue)
+{
+ int rc = mu_header_aget_field_value (header, num, pvalue);
+ if (rc == 0)
+ mu_string_unfold (*pvalue, NULL);
+ return rc;
+}
+
+
+int
+mu_header_lines (mu_header_t header, size_t *plines)
+{
+ int status;
+
+ if (header == NULL)
+ return EINVAL;
+ if (plines == NULL)
+ return MU_ERR_OUT_PTR_NULL;
+
+ status = mu_header_fill (header);
+ if (status == 0)
+ {
+ size_t count;
+ size_t size;
+ size_t lines;
+ mu_hdrent_count (header, &count, &size, &lines);
+ *plines = lines + 1;
+ }
+ return status;
+}
+
+int
+mu_header_size (mu_header_t header, size_t *psize)
+{
+ int status;
+
+ if (header == NULL)
+ return EINVAL;
+ if (psize == NULL)
+ return MU_ERR_OUT_PTR_NULL;
+
+ status = mu_header_fill (header);
+ if (status == 0)
+ {
+ size_t count;
+ size_t size;
+ size_t lines;
+ mu_hdrent_count (header, &count, &size, &lines);
+ *psize = size + 1;
+ }
+ return status;
+}
+
+int
+mu_header_invalidate (mu_header_t hdr)
+{
+ if (hdr == NULL)
+ return EINVAL;
+ mu_hdrent_free_list (hdr);
+ return 0;
+}
+
+
+static void
+mu_hdrent_fixup (mu_header_t hdr, struct mu_hdrent *ent)
+{
+ char *s = MU_HDRENT_NAME (hdr, ent);
+ s[ent->nlen] = ':';
+ s = MU_HDRENT_VALUE (hdr, ent);
+ s[ent->vlen] = '\n';
+}
+
+static void
+mu_hdrent_unroll_fixup (mu_header_t hdr, struct mu_hdrent *ent)
+{
+ char *s = MU_HDRENT_NAME (hdr, ent);
+ s[ent->nlen] = 0;
+ s = MU_HDRENT_VALUE (hdr, ent);
+ s[ent->vlen] = 0;
+}
+
+int
+header_seek (mu_stream_t str, mu_off_t off, mu_off_t *presult)
+{
+ struct _mu_header_stream *hstr = (struct _mu_header_stream *) str;
+ size_t size;
+ int status;
+
+ status = mu_header_size (hstr->hdr, &size);
+ if (status)
+ return status;
+
+ if (off < 0 || off > size)
+ return ESPIPE;
+ hstr->off = off;
+ *presult = off;
+ return 0;
+}
+
+static int
+header_read (mu_stream_t is, char *buffer, size_t buflen, size_t *pnread)
+{
+ struct _mu_header_stream *hstr = (struct _mu_header_stream *) is;
+ mu_header_t header;
+ struct mu_hdrent *ent;
+ size_t ent_off;
+ int status;
+ size_t nread;
+
+ if (is == NULL)
+ return EINVAL;
+
+ header = hstr->hdr;
+ status = mu_header_fill (header);
+ if (status)
+ return status;
+
+ if (mu_hdrent_find_stream_pos (header, hstr->off, &ent, &ent_off))
+ {
+ if (pnread)
+ *pnread = 0;
+ return 0;
+ }
+
+ for (nread = 0; nread < buflen && ent; ent = ent->next)
+ {
+ size_t rest = buflen - nread;
+ size_t strsize = MU_STR_SIZE (ent->nlen, ent->vlen) - ent_off;
+ if (rest > strsize)
+ rest = strsize;
+ mu_hdrent_fixup (header, ent);
+ memcpy (buffer + nread, MU_HDRENT_NAME (header, ent) + ent_off, rest);
+ mu_hdrent_unroll_fixup (header, ent);
+ nread += rest;
+ hstr->off += rest;
+ ent_off = 0;
+ }
+ if (pnread)
+ *pnread = nread;
+ return 0;
+}
+
+#if 0
+/* FIXME: Implement header_readdelim based on this: */
+static int
+_header_readline (mu_stream_t is, char *buffer, size_t buflen, size_t *pnread)
+{
+ struct _mu_header_stream *hstr = (struct _mu_header_stream *) is;
+ mu_header_t header = hstr->hdr;
+ struct mu_hdrent *ent;
+ size_t ent_off;
+ int status;
+ size_t strsize;
+ char *start, *end;
+
+ if (buflen == 0)
+ return EINVAL;
+
+ header = mu_stream_get_owner (is);
+ status = mu_header_fill (header);
+ if (status)
+ return status;
+ if (mu_hdrent_find_stream_pos (header, hstr->off, &ent, &ent_off))
+ {
+ if (pnread)
+ *pnread = 0;
+ return 0;
+ }
+
+ buflen--; /* Account for the terminating nul */
+
+ mu_hdrent_fixup (header, ent);
+ strsize = MU_STR_SIZE (ent->nlen, ent->vlen) - ent_off;
+ start = MU_HDRENT_NAME (header, ent) + ent_off;
+ end = strchr (start, '\n');
+ if (end)
+ {
+ size_t len = end - start + 1;
+ if (len < strsize)
+ strsize = len;
+ }
+
+ if (strsize < buflen)
+ buflen = strsize;
+
+ memcpy (buffer, start, buflen);
+ buffer[buflen] = 0;
+ hstr->off += buflen;
+ mu_hdrent_unroll_fixup (header, ent);
+ if (pnread)
+ *pnread = buflen;
+ return 0;
+}
+#endif
+
+static int
+header_write (mu_stream_t os, const char *buf, size_t buflen, size_t *pnwrite)
+{
+ struct _mu_header_stream *hstr;
+ mu_header_t header;
+ int status;
+ mu_off_t mstream_size;
+
+ if (!os || !buf)
+ return EINVAL;
+
+ hstr = (struct _mu_header_stream *) os;
+ header = hstr->hdr;
+ if (header == NULL)
+ return EINVAL;
+
+ /* Skip the obvious. */
+ if (*buf == '\0' || buflen == 0)
+ {
+ if (pnwrite)
+ *pnwrite = 0;
+ return 0;
+ }
+
+ if (!header->mstream)
+ {
+ status = mu_memory_stream_create (&header->mstream, MU_STREAM_RDWR);
+ if (status)
+ return status;
+ status = mu_stream_open (header->mstream);
+ if (status)
+ {
+ mu_stream_destroy (&header->mstream);
+ return status;
+ }
+ }
+
+ status = mu_stream_write (header->mstream, buf, buflen, NULL);
+ if (status)
+ {
+ mu_stream_destroy (&header->mstream);
+ return status;
+ }
+
+ status = mu_stream_size (header->mstream, &mstream_size);
+ if (status == 0 && mstream_size > 1)
+ {
+ char nlbuf[2];
+
+ status = mu_stream_seek (header->mstream, -2, MU_SEEK_END, NULL);
+ if (status == 0)
+ status = mu_stream_read (header->mstream, nlbuf, 2, NULL);
+ if (status == 0 && memcmp (nlbuf, "\n\n", 2) == 0)
+ {
+ char *blurb;
+
+ blurb = calloc (1, mstream_size + 1);
+ if (blurb)
+ {
+ mu_stream_read (header->mstream, blurb, mstream_size, NULL);
+ status = header_parse (header, blurb, mstream_size);
+ }
+ free (blurb);
+ mu_stream_destroy (&header->mstream);
+ }
+ }
+
+ if (pnwrite)
+ *pnwrite = buflen;
+
+ return status;
+}
+
+static int
+header_size (mu_stream_t str, mu_off_t *psize)
+{
+ mu_header_t header;
+ int status;
+ size_t size;
+
+ if (str == NULL)
+ return EINVAL;
+ if (psize == NULL)
+ return MU_ERR_OUT_PTR_NULL;
+
+ header = ((struct _mu_header_stream *) str)->hdr;
+ status = mu_header_fill (header);
+ if (status)
+ return status;
+ status = mu_header_size (header, &size);
+ if (status == 0)
+ *psize = size;
+ return status;
+}
+
+static int
+_header_get_stream (mu_header_t header, mu_stream_t *pstream, int ref)
+{
+ if (header == NULL)
+ return EINVAL;
+
+ if (pstream == NULL)
+ return MU_ERR_OUT_PTR_NULL;
+
+ if (header->stream == NULL)
+ {
+ struct _mu_header_stream *str =
+ (struct _mu_header_stream *) _mu_stream_create (sizeof (*str),
+ MU_STREAM_RDWR|MU_STREAM_SEEK);
+ if (!str)
+ return ENOMEM;
+ str->stream.read = header_read;
+ /*str->stream.rdelim? */
+ str->stream.write = header_write;
+ str->stream.seek = header_seek;
+ str->stream.size = header_size;
+ str->hdr = header;
+ header->stream = (mu_stream_t) str;
+ }
+ if (!ref)
+ {
+ *pstream = header->stream;
+ return 0;
+ }
+ return mu_streamref_create (pstream, header->stream);
+}
+
+int
+mu_header_get_stream (mu_header_t header, mu_stream_t *pstream)
+{
+ /* FIXME: Deprecation warning */
+ return _header_get_stream (header, pstream, 0);
+}
+
+int
+mu_header_get_streamref (mu_header_t header, mu_stream_t *pstream)
+{
+ return _header_get_stream (header, pstream, 1);
+}
+
+
+int
+mu_header_set_fill (mu_header_t header, int
+ (*_fill) (void *data, char **, size_t *),
+ void *data)
+{
+ if (header == NULL)
+ return EINVAL;
+ header->_fill = _fill;
+ header->data = data;
+ return 0;
+}
+
+
+int
+mu_header_is_modified (mu_header_t header)
+{
+ return header ? (header->flags & HEADER_MODIFIED) : 0;
+}
+
+int
+mu_header_clear_modified (mu_header_t header)
+{
+ if (header)
+ header->flags &= ~HEADER_MODIFIED;
+ return 0;
+}
+
+
diff --git a/libmailutils/iostream.c b/libmailutils/iostream.c
new file mode 100644
index 000000000..1a7da7e7b
--- /dev/null
+++ b/libmailutils/iostream.c
@@ -0,0 +1,261 @@
+/* GNU Mailutils -- a suite of utilities for electronic mail
+ Copyright (C) 2010 Free Software Foundation, Inc.
+
+ This library is free software; you can redistribute it and/or modify
+ it under the terms of the GNU Lesser General Public License as published by
+ the Free Software Foundation; either version 3, or (at your option)
+ any later version.
+
+ This library 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 Lesser General Public License for more details.
+
+ You should have received a copy of the GNU Lesser General Public License
+ along with GNU Mailutils. If not, see <http://www.gnu.org/licenses/>. */
+
+#ifdef HAVE_CONFIG_H
+# include <config.h>
+#endif
+#include <stdlib.h>
+#include <string.h>
+#include <errno.h>
+
+#include <mailutils/types.h>
+#include <mailutils/alloc.h>
+#include <mailutils/errno.h>
+
+#include <mailutils/nls.h>
+#include <mailutils/stream.h>
+#include <mailutils/sys/stream.h>
+#include <mailutils/sys/iostream.h>
+
+static int
+_iostream_read (struct _mu_stream *str, char *buf, size_t bufsize,
+ size_t *pnread)
+{
+ struct _mu_iostream *sp = (struct _mu_iostream *)str;
+ int rc = mu_stream_read (sp->transport[_MU_STREAM_INPUT], buf, bufsize,
+ pnread);
+ if (rc)
+ sp->last_err_str = _MU_STREAM_INPUT;
+ return rc;
+}
+
+static int
+_iostream_readdelim (struct _mu_stream *str, char *buf, size_t bufsize,
+ int delim, size_t *pnread)
+{
+ struct _mu_iostream *sp = (struct _mu_iostream *)str;
+ int rc = mu_stream_readdelim (sp->transport[_MU_STREAM_INPUT], buf,
+ bufsize, delim, pnread);
+ if (rc)
+ sp->last_err_str = _MU_STREAM_INPUT;
+ return rc;
+}
+
+static int
+_iostream_write (struct _mu_stream *str, const char *buf, size_t bufsize,
+ size_t *pnwrite)
+{
+ struct _mu_iostream *sp = (struct _mu_iostream *)str;
+ int rc = mu_stream_write (sp->transport[_MU_STREAM_OUTPUT], buf, bufsize,
+ pnwrite);
+ if (rc)
+ sp->last_err_str = _MU_STREAM_OUTPUT;
+ return rc;
+}
+
+static int
+_iostream_flush (struct _mu_stream *str)
+{
+ struct _mu_iostream *sp = (struct _mu_iostream *)str;
+ int rc = mu_stream_flush (sp->transport[_MU_STREAM_INPUT]);
+ if (rc)
+ {
+ sp->last_err_str = _MU_STREAM_INPUT;
+ return rc;
+ }
+ if (sp->transport[_MU_STREAM_INPUT] != sp->transport[_MU_STREAM_OUTPUT])
+ {
+ rc = mu_stream_flush (sp->transport[_MU_STREAM_OUTPUT]);
+ if (rc)
+ sp->last_err_str = _MU_STREAM_OUTPUT;
+ }
+ return rc;
+}
+
+static int
+_iostream_open (struct _mu_stream *str)
+{
+ struct _mu_iostream *sp = (struct _mu_iostream *)str;
+ int rc;
+ rc = mu_stream_open (sp->transport[_MU_STREAM_INPUT]);
+ if (rc)
+ {
+ sp->last_err_str = _MU_STREAM_INPUT;
+ return rc;
+ }
+ if (sp->transport[_MU_STREAM_INPUT] != sp->transport[_MU_STREAM_OUTPUT])
+ {
+ rc = mu_stream_open (sp->transport[_MU_STREAM_OUTPUT]);
+ if (rc)
+ {
+ sp->last_err_str = _MU_STREAM_OUTPUT;
+ mu_stream_close (sp->transport[_MU_STREAM_INPUT]);
+ }
+ }
+ return rc;
+}
+
+static int
+_iostream_close (struct _mu_stream *str)
+{
+ struct _mu_iostream *sp = (struct _mu_iostream *)str;
+ mu_stream_close (sp->transport[_MU_STREAM_INPUT]);
+ mu_stream_close (sp->transport[_MU_STREAM_OUTPUT]);
+ return 0;
+}
+
+static void
+_iostream_done (struct _mu_stream *str)
+{
+ struct _mu_iostream *sp = (struct _mu_iostream *)str;
+ mu_stream_unref (sp->transport[_MU_STREAM_INPUT]);
+ mu_stream_unref (sp->transport[_MU_STREAM_OUTPUT]);
+}
+
+static int
+_iostream_ctl (struct _mu_stream *str, int op, void *arg)
+{
+ struct _mu_iostream *sp = (struct _mu_iostream *)str;
+ mu_transport_t *ptrans;
+
+ switch (op)
+ {
+ case MU_IOCTL_GET_TRANSPORT:
+ if (!arg)
+ return EINVAL;
+ ptrans = arg;
+ ptrans[0] = (mu_transport_t) sp->transport[_MU_STREAM_INPUT];
+ ptrans[1] = (mu_transport_t) sp->transport[_MU_STREAM_OUTPUT];
+ break;
+
+ case MU_IOCTL_SET_TRANSPORT:
+ if (!arg)
+ return EINVAL;
+ ptrans = arg;
+ sp->transport[_MU_STREAM_INPUT] = (mu_stream_t) ptrans[0];
+ sp->transport[_MU_STREAM_OUTPUT] = (mu_stream_t) ptrans[1];
+ break;
+
+ case MU_IOCTL_SWAP_STREAM:
+ if (!arg)
+ return EINVAL;
+ return _mu_stream_swap_streams (str, sp->transport, arg, 0);
+
+ case MU_IOCTL_GET_TRANSPORT_BUFFER:
+ case MU_IOCTL_SET_TRANSPORT_BUFFER:
+ if (!arg)
+ return EINVAL;
+ else
+ {
+ struct mu_buffer_query *qp = arg;
+ if (!MU_TRANSPORT_VALID_TYPE (qp->type) || !sp->transport[qp->type])
+ return EINVAL;
+ return mu_stream_ioctl (sp->transport[qp->type], op, arg);
+ }
+
+ default:
+ return ENOSYS;
+ }
+ return 0;
+}
+
+static int
+_iostream_wait (struct _mu_stream *str, int *pflags, struct timeval *tvp)
+{
+ struct _mu_iostream *sp = (struct _mu_iostream *)str;
+ int rc = EINVAL;
+
+ if (*pflags == MU_STREAM_READY_RD)
+ {
+ rc = mu_stream_wait (sp->transport[_MU_STREAM_INPUT], pflags, tvp);
+ if (rc)
+ sp->last_err_str = _MU_STREAM_INPUT;
+ }
+ else if (*pflags == MU_STREAM_READY_WR)
+ {
+ rc = mu_stream_wait (sp->transport[_MU_STREAM_OUTPUT], pflags, tvp);
+ if (rc)
+ sp->last_err_str = _MU_STREAM_OUTPUT;
+ }
+ return rc;
+}
+
+static int
+_iostream_shutdown (struct _mu_stream *str, int how)
+{
+ struct _mu_iostream *sp = (struct _mu_iostream *)str;
+ int rc = EINVAL;
+ switch (how)
+ {
+ case MU_STREAM_READ:
+ rc = mu_stream_shutdown (sp->transport[_MU_STREAM_INPUT], how);
+ if (rc)
+ sp->last_err_str = _MU_STREAM_INPUT;
+ break;
+
+ case MU_STREAM_WRITE:
+ rc = mu_stream_shutdown (sp->transport[_MU_STREAM_OUTPUT], how);
+ if (rc)
+ sp->last_err_str = _MU_STREAM_OUTPUT;
+ }
+ return rc;
+}
+
+static const char *
+_iostream_error_string (struct _mu_stream *str, int rc)
+{
+ struct _mu_iostream *sp = (struct _mu_iostream *)str;
+ mu_stream_t transport = sp->transport[sp->last_err_str];
+ if (transport)
+ return mu_stream_strerror (transport, rc);
+ return mu_strerror (rc);
+}
+
+int
+mu_iostream_create (mu_stream_t *pref, mu_stream_t in, mu_stream_t out)
+{
+ struct _mu_iostream *sp;
+
+ sp = (struct _mu_iostream *)
+ _mu_stream_create (sizeof (*sp),
+ MU_STREAM_READ | MU_STREAM_WRITE);
+ if (!sp)
+ return ENOMEM;
+
+ sp->stream.read = _iostream_read;
+ if (in->readdelim)
+ sp->stream.readdelim = _iostream_readdelim;
+ sp->stream.write = _iostream_write;
+ sp->stream.flush = _iostream_flush;
+ sp->stream.open = _iostream_open;
+ sp->stream.close = _iostream_close;
+ sp->stream.done = _iostream_done;
+ sp->stream.ctl = _iostream_ctl;
+ sp->stream.wait = _iostream_wait;
+ sp->stream.shutdown = _iostream_shutdown;
+ sp->stream.error_string = _iostream_error_string;
+
+ mu_stream_ref (in);
+ sp->transport[_MU_STREAM_INPUT] = in;
+ mu_stream_ref (out);
+ sp->transport[_MU_STREAM_OUTPUT] = out;
+
+ mu_stream_set_buffer ((mu_stream_t) sp, mu_buffer_line, 1024);
+ *pref = (mu_stream_t) sp;
+ return 0;
+}
+
+
diff --git a/libmailutils/ipsrv.c b/libmailutils/ipsrv.c
new file mode 100644
index 000000000..e3efc62d1
--- /dev/null
+++ b/libmailutils/ipsrv.c
@@ -0,0 +1,580 @@
+/* GNU Mailutils -- a suite of utilities for electronic mail
+ Copyright (C) 2007, 2008, 2010 Free Software Foundation, Inc.
+
+ This library is free software; you can redistribute it and/or
+ modify it under the terms of the GNU Lesser General Public
+ License as published by the Free Software Foundation; either
+ version 3 of the License, or (at your option) any later version.
+
+ This library 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
+ Lesser General Public License for more details.
+
+ You should have received a copy of the GNU Lesser General
+ Public License along with this library; If not, see
+ <http://www.gnu.org/licenses/>. */
+
+#ifdef HAVE_CONFIG_H
+#include <config.h>
+#endif
+#include <sys/time.h>
+#include <sys/types.h>
+#include <sys/socket.h>
+#include <sys/stat.h>
+#include <netinet/in.h>
+#include <sys/un.h>
+#include <arpa/inet.h>
+#include <unistd.h>
+#include <stdlib.h>
+#include <string.h>
+#include <mailutils/acl.h>
+#include <mailutils/server.h>
+#include <mailutils/debug.h>
+#include <mailutils/diag.h>
+#include <mailutils/errno.h>
+#include <mailutils/nls.h>
+
+
+struct _mu_ip_server
+{
+ char *ident;
+ struct sockaddr *addr;
+ int addrlen;
+ int fd;
+ int type;
+ mu_debug_t debug;
+ mu_acl_t acl;
+ mu_ip_server_conn_fp f_conn;
+ mu_ip_server_intr_fp f_intr;
+ mu_ip_server_free_fp f_free;
+ void *data;
+ union
+ {
+ struct
+ {
+ int backlog;
+ } tcp_data;
+ struct
+ {
+ char *buf;
+ size_t bufsize;
+ ssize_t rdsize;
+ } udp_data;
+ } v;
+};
+
+#define IDENTSTR(s) ((s)->ident ? (s)->ident : "default")
+
+int
+mu_ip_server_create (mu_ip_server_t *psrv, struct sockaddr *addr,
+ int addrlen, int type)
+{
+ struct _mu_ip_server *srv;
+ mu_log_level_t level;
+
+ switch (type)
+ {
+ case MU_IP_UDP:
+ case MU_IP_TCP:
+ break;
+
+ default:
+ return EINVAL;
+ }
+
+ srv = calloc (1, sizeof *srv);
+ if (!srv)
+ return ENOMEM;
+ srv->addr = calloc (1, addrlen);
+ if (!srv->addr)
+ {
+ free (srv);
+ return ENOMEM;
+ }
+ memcpy (srv->addr, addr, addrlen);
+ srv->addrlen = addrlen;
+ srv->type = type;
+ level = mu_global_debug_level ("ip_server");
+ if (level)
+ {
+ mu_debug_create (&srv->debug, NULL);
+ mu_debug_set_level (srv->debug, level);
+ }
+ srv->fd = -1;
+ switch (type)
+ {
+ case MU_IP_UDP:
+ srv->v.udp_data.bufsize = 4096;
+ break;
+
+ case MU_IP_TCP:
+ srv->v.tcp_data.backlog = 4;
+ }
+
+ *psrv = srv;
+ return 0;
+}
+
+int
+mu_ip_server_destroy (mu_ip_server_t *psrv)
+{
+ mu_ip_server_t srv;
+ if (!psrv)
+ return EINVAL;
+ srv = *psrv;
+ if (!srv)
+ return 0;
+ if (srv->f_free)
+ srv->f_free (srv->data);
+ close (srv->fd);
+ free (srv->addr);
+ free (srv->ident);
+ if (srv->type == MU_IP_UDP && srv->v.udp_data.buf)
+ free (srv->v.udp_data.buf);
+ free (srv);
+ *psrv = NULL;
+ return 0;
+}
+
+int
+mu_ip_server_set_debug (mu_ip_server_t srv, mu_debug_t debug)
+{
+ if (!srv)
+ return EINVAL;
+ mu_debug_destroy (&srv->debug, NULL);
+ srv->debug = debug;
+ return 0;
+}
+
+int
+mu_ip_server_get_debug (mu_ip_server_t srv, mu_debug_t *pdebug)
+{
+ if (!srv)
+ return EINVAL;
+ *pdebug = srv->debug;
+ return 0;
+}
+
+int
+mu_ip_server_get_type (mu_ip_server_t srv, int *ptype)
+{
+ if (!srv)
+ return EINVAL;
+ *ptype = srv->type;
+ return 0;
+}
+
+int
+mu_tcp_server_set_backlog (mu_ip_server_t srv, int backlog)
+{
+ if (!srv || srv->type != MU_IP_TCP)
+ return EINVAL;
+ srv->v.tcp_data.backlog = backlog;
+ return 0;
+}
+
+int
+mu_udp_server_get_bufsize (mu_ip_server_t srv, size_t *psize)
+{
+ if (!srv || srv->type != MU_IP_UDP)
+ return EINVAL;
+ *psize = srv->v.udp_data.bufsize;
+ return 0;
+}
+
+int
+mu_udp_server_set_bufsize (mu_ip_server_t srv, size_t size)
+{
+ if (!srv || srv->type != MU_IP_UDP)
+ return EINVAL;
+ srv->v.udp_data.bufsize = size;
+ if (srv->v.udp_data.buf)
+ {
+ char *p = realloc (srv->v.udp_data.buf, size);
+ if (!p)
+ return ENOMEM;
+ srv->v.udp_data.buf = p;
+ }
+ return 0;
+}
+
+int
+mu_ip_server_set_ident (mu_ip_server_t srv, const char *ident)
+{
+ if (!srv)
+ return EINVAL;
+ if (srv->ident)
+ free (srv->ident);
+ srv->ident = strdup (ident);
+ if (!srv->ident)
+ return ENOMEM;
+ return 0;
+}
+
+int
+mu_ip_server_set_acl (mu_ip_server_t srv, mu_acl_t acl)
+{
+ if (!srv)
+ return EINVAL;
+ srv->acl = acl;
+ return 0;
+}
+
+int
+mu_ip_server_set_conn (mu_ip_server_t srv, mu_ip_server_conn_fp conn)
+{
+ if (!srv)
+ return EINVAL;
+ srv->f_conn = conn;
+ return 0;
+}
+
+int
+mu_ip_server_set_intr (mu_ip_server_t srv, mu_ip_server_intr_fp intr)
+{
+ if (!srv)
+ return EINVAL;
+ srv->f_intr = intr;
+ return 0;
+}
+
+int
+mu_ip_server_set_data (mu_ip_server_t srv,
+ void *data, mu_ip_server_free_fp free)
+{
+ if (!srv)
+ return EINVAL;
+ srv->data = data;
+ srv->f_free = free;
+ return 0;
+}
+
+int
+mu_address_family_to_domain (int family)
+{
+ switch (family)
+ {
+ case AF_UNIX:
+ return PF_UNIX;
+
+ case AF_INET:
+ return PF_INET;
+
+ default:
+ abort ();
+ }
+}
+
+int
+mu_ip_server_open (mu_ip_server_t srv)
+{
+ int fd;
+
+ if (!srv || srv->fd != -1)
+ return EINVAL;
+
+ if (mu_debug_check_level (srv->debug, MU_DEBUG_TRACE0))
+ {
+ char *p = mu_sockaddr_to_astr (srv->addr, srv->addrlen);
+ __MU_DEBUG2 (srv->debug, MU_DEBUG_TRACE0,
+ "opening server \"%s\" %s\n", IDENTSTR (srv),
+ p);
+ free (p);
+ }
+
+ fd = socket (mu_address_family_to_domain (srv->addr->sa_family),
+ ((srv->type == MU_IP_UDP) ? SOCK_DGRAM : SOCK_STREAM), 0);
+ if (fd == -1)
+ {
+ MU_DEBUG2 (srv->debug, MU_DEBUG_ERROR,
+ "%s: socket: %s\n", IDENTSTR (srv), mu_strerror (errno));
+ return errno;
+ }
+
+ switch (srv->addr->sa_family)
+ {
+ case AF_UNIX:
+ {
+ struct stat st;
+ struct sockaddr_un *s_un = (struct sockaddr_un *) srv->addr;
+
+ if (stat (s_un->sun_path, &st))
+ {
+ if (errno != ENOENT)
+ {
+ MU_DEBUG3 (srv->debug, MU_DEBUG_ERROR,
+ _("%s: file %s exists but cannot be stat'd: %s"),
+ IDENTSTR (srv),
+ s_un->sun_path,
+ mu_strerror (errno));
+ return EAGAIN;
+ }
+ }
+ else if (!S_ISSOCK (st.st_mode))
+ {
+ MU_DEBUG2 (srv->debug, MU_DEBUG_ERROR,
+ _("%s: file %s is not a socket"),
+ IDENTSTR (srv), s_un->sun_path);
+ return EAGAIN;
+ }
+ else if (unlink (s_un->sun_path))
+ {
+ MU_DEBUG3 (srv->debug, MU_DEBUG_ERROR,
+ _("%s: cannot unlink file %s: %s"),
+ IDENTSTR (srv), s_un->sun_path, mu_strerror (errno));
+ return EAGAIN;
+ }
+ }
+ break;
+
+ case AF_INET:
+ {
+ int t;
+
+ t = 1;
+ setsockopt (fd, SOL_SOCKET, SO_REUSEADDR, &t, sizeof (t));
+ }
+ }
+
+ if (bind (fd, srv->addr, srv->addrlen) == -1)
+ {
+ MU_DEBUG2 (srv->debug, MU_DEBUG_ERROR,
+ "%s: bind: %s\n", IDENTSTR (srv), mu_strerror (errno));
+ close (fd);
+ return errno;
+ }
+
+ if (srv->type == MU_IP_TCP)
+ {
+ if (listen (fd, srv->v.tcp_data.backlog) == -1)
+ {
+ MU_DEBUG2 (srv->debug, MU_DEBUG_ERROR,
+ "%s: listen: %s\n", IDENTSTR (srv), mu_strerror (errno));
+ close (fd);
+ return errno;
+ }
+ }
+
+ srv->fd = fd;
+ return 0;
+}
+
+int
+mu_ip_server_shutdown (mu_ip_server_t srv)
+{
+ if (!srv || srv->fd != -1)
+ return EINVAL;
+ if (mu_debug_check_level (srv->debug, MU_DEBUG_TRACE0))
+ {
+ char *p = mu_sockaddr_to_astr (srv->addr, srv->addrlen);
+ __MU_DEBUG2 (srv->debug, MU_DEBUG_TRACE0,
+ "closing server \"%s\" %s\n", IDENTSTR (srv),
+ p);
+ free (p);
+ }
+ close (srv->fd);
+ return 0;
+}
+
+int
+mu_ip_tcp_accept (mu_ip_server_t srv, void *call_data)
+{
+ int rc;
+ int connfd;
+ union
+ {
+ struct sockaddr sa;
+ struct sockaddr_in s_in;
+ struct sockaddr_un s_un;
+ } client;
+
+ socklen_t size = sizeof (client);
+
+ if (!srv || srv->fd == -1 || srv->type == MU_IP_UDP)
+ return EINVAL;
+
+ connfd = accept (srv->fd, &client.sa, &size);
+ if (connfd == -1)
+ {
+ int ec = errno;
+ if (ec == EINTR)
+ {
+ if (srv->f_intr && srv->f_intr (srv->data, call_data))
+ mu_ip_server_shutdown (srv);
+ }
+ return ec;
+ }
+
+ if (srv->acl)
+ {
+ mu_acl_result_t res;
+ int rc = mu_acl_check_sockaddr (srv->acl, &client.sa, size, &res);
+ if (rc)
+ MU_DEBUG2 (srv->debug, MU_DEBUG_ERROR,
+ "%s: mu_acl_check_sockaddr: %s\n",
+ IDENTSTR (srv), strerror (rc));
+ if (res == mu_acl_result_deny)
+ {
+ char *p = mu_sockaddr_to_astr (&client.sa, size);
+ mu_diag_output (MU_DIAG_INFO, "Denying connection from %s", p);
+ free (p);
+
+ close (connfd);
+ return 0;
+ }
+ }
+ rc = srv->f_conn (connfd, &client.sa, size, srv->data, call_data, srv);
+ close (connfd);
+ return rc;
+}
+
+int
+mu_ip_udp_accept (mu_ip_server_t srv, void *call_data)
+{
+ int rc;
+ union
+ {
+ struct sockaddr sa;
+ struct sockaddr_in s_in;
+ struct sockaddr_un s_un;
+ } client;
+ fd_set rdset;
+
+ socklen_t salen = sizeof (client);
+ ssize_t size;
+
+ if (!srv->v.udp_data.buf)
+ {
+ srv->v.udp_data.buf = malloc (srv->v.udp_data.bufsize);
+ if (!srv->v.udp_data.buf)
+ return ENOMEM;
+ }
+
+ FD_ZERO (&rdset);
+ FD_SET (srv->fd, &rdset);
+ for (;;)
+ {
+ rc = select (srv->fd + 1, &rdset, NULL, NULL, NULL);
+ if (rc == -1)
+ {
+ if (errno == EINTR)
+ {
+ if (srv->f_intr && srv->f_intr (srv->data, call_data))
+ break;
+ else
+ continue;
+ }
+ }
+ else
+ break;
+ }
+
+ if (rc == -1)
+ return errno;
+
+ size = recvfrom (srv->fd, srv->v.udp_data.buf, srv->v.udp_data.bufsize,
+ 0, &client.sa, &salen);
+ if (size < 0)
+ {
+ MU_DEBUG2 (srv->debug, MU_DEBUG_ERROR,
+ "%s: recvfrom: %s",
+ IDENTSTR (srv), strerror (errno));
+ return MU_ERR_FAILURE;
+ }
+ srv->v.udp_data.rdsize = size;
+
+ if (srv->acl)
+ {
+ mu_acl_result_t res;
+ int rc = mu_acl_check_sockaddr (srv->acl, &client.sa, size, &res);
+ if (rc)
+ MU_DEBUG2 (srv->debug, MU_DEBUG_ERROR,
+ "%s: mu_acl_check_sockaddr: %s\n",
+ IDENTSTR (srv), strerror (rc));
+ if (res == mu_acl_result_deny)
+ {
+ char *p = mu_sockaddr_to_astr (srv->addr, srv->addrlen);
+ mu_diag_output (MU_DIAG_INFO, "Denying connection from %s", p);
+ free (p);
+ return 0;
+ }
+ }
+ rc = srv->f_conn (-1, &client.sa, size, srv->data, call_data, srv);
+ return rc;
+}
+
+int
+mu_ip_server_accept (mu_ip_server_t srv, void *call_data)
+{
+ int rc;
+ if (!srv || srv->fd == -1)
+ return EINVAL;
+ switch (srv->type)
+ {
+ case MU_IP_UDP:
+ rc = mu_ip_udp_accept (srv, call_data);
+ break;
+
+ case MU_IP_TCP:
+ rc = mu_ip_tcp_accept (srv, call_data);
+ }
+
+ if (rc)
+ mu_ip_server_shutdown (srv);
+ return rc;
+}
+
+int
+mu_ip_server_loop (mu_ip_server_t srv, void *call_data)
+{
+ if (!srv)
+ return EINVAL;
+ while (srv->fd != -1)
+ {
+ int rc = mu_ip_server_accept (srv, call_data);
+ if (rc && rc != EINTR)
+ {
+ mu_ip_server_shutdown (srv);
+ return rc;
+ }
+ }
+ return 0;
+}
+
+int
+mu_ip_server_get_fd (mu_ip_server_t srv)
+{
+ return srv->fd;
+}
+
+int
+mu_udp_server_get_rdata (mu_ip_server_t srv, char **pbuf, size_t *pbufsize)
+{
+ if (!srv || srv->type != MU_IP_UDP)
+ return EINVAL;
+ *pbuf = srv->v.udp_data.buf;
+ *pbufsize = srv->v.udp_data.rdsize;
+ return 0;
+}
+
+int
+mu_ip_server_get_sockaddr (mu_ip_server_t srv, struct sockaddr *s, int *size)
+{
+ int len;
+
+ if (!srv || !s)
+ return EINVAL;
+ if (s == 0)
+ len = srv->addrlen;
+ else
+ {
+ len = srv->addrlen;
+ if (*size < len)
+ return MU_ERR_BUFSPACE;
+ memcpy (s, srv->addr, len);
+ }
+ *size = len;
+ return 0;
+}
+
diff --git a/libmailutils/iterator.c b/libmailutils/iterator.c
new file mode 100644
index 000000000..38979255a
--- /dev/null
+++ b/libmailutils/iterator.c
@@ -0,0 +1,284 @@
+/* GNU Mailutils -- a suite of utilities for electronic mail
+ Copyright (C) 1999, 2000, 2004, 2005, 2007, 2010 Free Software
+ Foundation, Inc.
+
+ This library is free software; you can redistribute it and/or
+ modify it under the terms of the GNU Lesser General Public
+ License as published by the Free Software Foundation; either
+ version 3 of the License, or (at your option) any later version.
+
+ This library 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
+ Lesser General Public License for more details.
+
+ You should have received a copy of the GNU Lesser General
+ Public License along with this library; if not, write to the
+ Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor,
+ Boston, MA 02110-1301 USA */
+
+#ifdef HAVE_CONFIG_H
+# include <config.h>
+#endif
+
+#include <errno.h>
+#include <stdlib.h>
+
+#include <mailutils/sys/list.h>
+#include <mailutils/sys/iterator.h>
+#include <mailutils/errno.h>
+
+int
+mu_iterator_create (mu_iterator_t *piterator, void *owner)
+{
+ mu_iterator_t iterator;
+ if (piterator == NULL)
+ return MU_ERR_OUT_PTR_NULL;
+ if (owner == NULL)
+ return EINVAL;
+ iterator = calloc (1, sizeof (*iterator));
+ if (iterator == NULL)
+ return ENOMEM;
+ iterator->owner = owner;
+ *piterator = iterator;
+ return 0;
+}
+
+int
+mu_iterator_set_first (mu_iterator_t itr, int (*first) (void *))
+{
+ if (!itr)
+ return EINVAL;
+ itr->first = first;
+ return 0;
+}
+
+int
+mu_iterator_set_next (mu_iterator_t itr, int (*next) (void *))
+{
+ if (!itr)
+ return EINVAL;
+ itr->next = next;
+ return 0;
+}
+
+int
+mu_iterator_set_getitem (mu_iterator_t itr,
+ int (*getitem) (void *, void **, const void **))
+{
+ if (!itr)
+ return EINVAL;
+ itr->getitem = getitem;
+ return 0;
+}
+
+int
+mu_iterator_set_finished_p (mu_iterator_t itr, int (*finished_p) (void *))
+{
+ if (!itr)
+ return EINVAL;
+ itr->finished_p = finished_p;
+ return 0;
+}
+
+int
+mu_iterator_set_curitem_p (mu_iterator_t itr,
+ int (*curitem_p) (void *, void *))
+{
+ if (!itr)
+ return EINVAL;
+ itr->curitem_p = curitem_p;
+ return 0;
+}
+
+int
+mu_iterator_set_itrctl (mu_iterator_t itr,
+ int (*itrctl) (void *,
+ enum mu_itrctl_req,
+ void *))
+{
+ if (!itr)
+ return EINVAL;
+ itr->itrctl = itrctl;
+ return 0;
+}
+
+int
+mu_iterator_set_destroy (mu_iterator_t itr, int (*destroy) (mu_iterator_t, void *))
+{
+ if (!itr)
+ return EINVAL;
+ itr->destroy = destroy;
+ return 0;
+}
+
+int
+mu_iterator_set_dup (mu_iterator_t itr, int (*dup) (void **ptr, void *data))
+{
+ if (!itr)
+ return EINVAL;
+ itr->dup = dup;
+ return 0;
+}
+
+
+
+int
+mu_iterator_dup (mu_iterator_t *piterator, mu_iterator_t orig)
+{
+ mu_iterator_t iterator;
+ int status;
+
+ if (piterator == NULL)
+ return MU_ERR_OUT_PTR_NULL;
+ if (orig == NULL)
+ return EINVAL;
+
+ status = mu_iterator_create (&iterator, orig->owner);
+ if (status)
+ return status;
+
+ status = orig->dup(&iterator->owner, orig->owner);
+ if (status)
+ {
+ free (iterator);
+ return status;
+ }
+ iterator->is_advanced = orig->is_advanced;
+ iterator->dup = orig->dup;
+ iterator->destroy = orig->destroy;
+ iterator->first = orig->first;
+ iterator->next = orig->next;
+ iterator->getitem = orig->getitem;
+ iterator->curitem_p = orig->curitem_p;
+ iterator->finished_p = orig->finished_p;
+ iterator->itrctl = orig->itrctl;
+
+ *piterator = iterator;
+ return 0;
+}
+
+void
+mu_iterator_destroy (mu_iterator_t *piterator)
+{
+ if (!piterator || !*piterator)
+ return;
+
+ if ((*piterator)->destroy)
+ (*piterator)->destroy (*piterator, (*piterator)->owner);
+
+ free (*piterator);
+ *piterator = NULL;
+}
+
+int
+mu_iterator_first (mu_iterator_t iterator)
+{
+ iterator->is_advanced = 0;
+ return iterator->first (iterator->owner);
+}
+
+int
+mu_iterator_next (mu_iterator_t iterator)
+{
+ int status = 0;
+ if (!iterator->is_advanced)
+ status = iterator->next (iterator->owner);
+ iterator->is_advanced = 0;
+ return status;
+}
+
+int
+mu_iterator_skip (mu_iterator_t iterator, ssize_t count)
+{
+ int status;
+ if (count < 0)
+ return ENOSYS; /* Need prev method */
+ while (count--)
+ if ((status = mu_iterator_next (iterator)))
+ break;
+ return status;
+}
+
+int
+mu_iterator_current (mu_iterator_t iterator, void **pitem)
+{
+ return iterator->getitem (iterator->owner, pitem, NULL);
+}
+
+int
+mu_iterator_current_kv (mu_iterator_t iterator,
+ const void **pkey, void **pitem)
+{
+ return iterator->getitem (iterator->owner, (void**)pitem, pkey);
+}
+
+int
+mu_iterator_is_done (mu_iterator_t iterator)
+{
+ if (iterator == NULL)
+ return 1;
+ return iterator->finished_p (iterator->owner);
+}
+
+int
+iterator_get_owner (mu_iterator_t iterator, void **powner)
+{
+ if (!iterator)
+ return EINVAL;
+ if (!powner)
+ return MU_ERR_OUT_PTR_NULL;
+ *powner = iterator->owner;
+ return 0;
+}
+
+void
+mu_iterator_advance (mu_iterator_t iterator, void *e)
+{
+ for (; iterator; iterator = iterator->next_itr)
+ {
+ if (iterator->curitem_p (iterator->owner, e))
+ {
+ iterator->next (iterator->owner);
+ iterator->is_advanced++;
+ }
+ }
+}
+
+int
+mu_iterator_attach (mu_iterator_t *root, mu_iterator_t iterator)
+{
+ iterator->next_itr = *root;
+ *root = iterator;
+ return 0;
+}
+
+int
+mu_iterator_detach (mu_iterator_t *root, mu_iterator_t iterator)
+{
+ mu_iterator_t itr, prev;
+
+ for (itr = *root, prev = NULL; itr; prev = itr, itr = itr->next_itr)
+ if (iterator == itr)
+ break;
+
+ if (itr)
+ {
+ if (prev)
+ prev->next_itr = itr->next_itr;
+ else
+ *root = itr->next_itr;
+ }
+
+ return 0;
+}
+
+int
+mu_iterator_ctl (mu_iterator_t iterator, enum mu_itrctl_req req, void *arg)
+{
+ if (!iterator)
+ return EINVAL;
+ if (!iterator->itrctl)
+ return ENOSYS;
+ return iterator->itrctl (iterator->owner, req, arg);
+}
diff --git a/libmailutils/kwd.c b/libmailutils/kwd.c
new file mode 100644
index 000000000..9135f9f96
--- /dev/null
+++ b/libmailutils/kwd.c
@@ -0,0 +1,94 @@
+/* GNU Mailutils -- a suite of utilities for electronic mail
+ Copyright (C) 2007, 2009, 2010 Free Software Foundation, Inc.
+
+ This library is free software; you can redistribute it and/or
+ modify it under the terms of the GNU Lesser General Public
+ License as published by the Free Software Foundation; either
+ version 3 of the License, or (at your option) any later version.
+
+ This library 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
+ Lesser General Public License for more details.
+
+ You should have received a copy of the GNU Lesser General
+ Public License along with this library; If not, see
+ <http://www.gnu.org/licenses/>. */
+
+#ifdef HAVE_CONFIG_H
+#include <config.h>
+#endif
+
+#include <string.h>
+#include <mailutils/kwd.h>
+#include <mailutils/errno.h>
+#include <mailutils/mutil.h>
+#include <mailutils/cstr.h>
+
+int
+mu_kwd_xlat_name_len (mu_kwd_t *kwtab, const char *str, size_t len, int *pres)
+{
+ for (; kwtab->name; kwtab++)
+ {
+ size_t kwlen = strlen (kwtab->name);
+ if (kwlen == len && memcmp (kwtab->name, str, len) == 0)
+ {
+ *pres = kwtab->tok;
+ return 0;
+ }
+ }
+ return MU_ERR_NOENT;
+}
+
+int
+mu_kwd_xlat_name_len_ci (mu_kwd_t *kwtab, const char *str, size_t len,
+ int *pres)
+{
+ for (; kwtab->name; kwtab++)
+ {
+ size_t kwlen = strlen (kwtab->name);
+ if (kwlen == len && mu_c_strncasecmp (kwtab->name, str, len) == 0)
+ {
+ *pres = kwtab->tok;
+ return 0;
+ }
+ }
+ return MU_ERR_NOENT;
+}
+
+int
+mu_kwd_xlat_name (mu_kwd_t *kwtab, const char *str, int *pres)
+{
+ for (; kwtab->name; kwtab++)
+ if (strcmp (kwtab->name, str) == 0)
+ {
+ *pres = kwtab->tok;
+ return 0;
+ }
+ return MU_ERR_NOENT;
+}
+
+int
+mu_kwd_xlat_name_ci (mu_kwd_t *kwtab, const char *str, int *pres)
+{
+ for (; kwtab->name; kwtab++)
+ if (mu_c_strcasecmp (kwtab->name, str) == 0)
+ {
+ *pres = kwtab->tok;
+ return 0;
+ }
+ return MU_ERR_NOENT;
+}
+
+
+int
+mu_kwd_xlat_tok (mu_kwd_t *kwtab, int tok, const char **pres)
+{
+ for (; kwtab->name; kwtab++)
+ if (kwtab->tok == tok)
+ {
+ *pres = kwtab->name;
+ return 0;
+ }
+ return MU_ERR_NOENT;
+}
diff --git a/libmailutils/linelenflt.c b/libmailutils/linelenflt.c
new file mode 100644
index 000000000..e3817de9f
--- /dev/null
+++ b/libmailutils/linelenflt.c
@@ -0,0 +1,110 @@
+/* GNU Mailutils -- a suite of utilities for electronic mail
+ Copyright (C) 2009, 2010 Free Software Foundation, Inc.
+
+ This library is free software; you can redistribute it and/or modify
+ it under the terms of the GNU Lesser General Public License as published by
+ the Free Software Foundation; either version 3, or (at your option)
+ any later version.
+
+ This library 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 Lesser General Public License for more details.
+
+ You should have received a copy of the GNU Lesser General Public License
+ along with GNU Mailutils. If not, see <http://www.gnu.org/licenses/>. */
+
+#ifdef HAVE_CONFIG_H
+# include <config.h>
+#endif
+#include <stdlib.h>
+#include <string.h>
+#include <mailutils/errno.h>
+#include <mailutils/filter.h>
+
+struct _mu_linelen_filter
+{
+ size_t max_len;
+ size_t cur_len;
+};
+
+static enum mu_filter_result
+_ll_encoder (void *xd,
+ enum mu_filter_command cmd,
+ struct mu_filter_io *iobuf)
+{
+ struct _mu_linelen_filter *flt = xd;
+ const char *iptr;
+ size_t isize;
+ char *optr;
+ size_t osize;
+ size_t consumed, written;
+
+ switch (cmd)
+ {
+ case mu_filter_init:
+ flt->cur_len = 0;
+ case mu_filter_done:
+ return mu_filter_ok;
+ default:
+ break;
+ }
+
+ iptr = iobuf->input;
+ isize = iobuf->isize;
+ optr = iobuf->output;
+ osize = iobuf->osize;
+
+ for (consumed = written = 0; consumed < isize && written < osize; )
+ {
+ char *p;
+ size_t rest = flt->max_len - flt->cur_len;
+ size_t len = isize - consumed;
+
+ if (len > rest)
+ len = rest;
+
+ p = memchr (iptr + consumed, '\n', len);
+ if (p)
+ len = p - iptr - consumed + 1;
+
+ rest = osize - written;
+ if (len > rest)
+ {
+ len = rest;
+ p = NULL;
+ }
+ memcpy (optr + written, iptr + consumed, len);
+ written += len;
+ consumed += len;
+ if (p)
+ flt->cur_len = 0;
+ else
+ {
+ flt->cur_len += len;
+ if (flt->cur_len == flt->max_len)
+ {
+ if (written < osize)
+ optr[written++] = '\n';
+ flt->cur_len = 0;
+ }
+ }
+ }
+ iobuf->isize = consumed;
+ iobuf->osize = written;
+ return mu_filter_ok;
+}
+
+int
+mu_linelen_filter_create (mu_stream_t *pstream, mu_stream_t stream,
+ size_t limit,
+ int flags)
+{
+ struct _mu_linelen_filter *flt = calloc (1, sizeof (*flt));
+ if (!flt)
+ return ENOMEM;
+ flt->max_len = limit;
+ return mu_filter_stream_create (pstream, stream,
+ MU_FILTER_ENCODE, _ll_encoder, flt, flags);
+}
+
diff --git a/libmailutils/list.c b/libmailutils/list.c
new file mode 100644
index 000000000..91f6a6ead
--- /dev/null
+++ b/libmailutils/list.c
@@ -0,0 +1,685 @@
+/* GNU Mailutils -- a suite of utilities for electronic mail
+ Copyright (C) 1999, 2000, 2001, 2004, 2005, 2007, 2008, 2010 Free
+ Software Foundation, Inc.
+
+ This library is free software; you can redistribute it and/or
+ modify it under the terms of the GNU Lesser General Public
+ License as published by the Free Software Foundation; either
+ version 3 of the License, or (at your option) any later version.
+
+ This library 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
+ Lesser General Public License for more details.
+
+ You should have received a copy of the GNU Lesser General
+ Public License along with this library; if not, write to the
+ Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor,
+ Boston, MA 02110-1301 USA */
+
+#ifdef HAVE_CONFIG_H
+# include <config.h>
+#endif
+
+#include <errno.h>
+#include <stdlib.h>
+#include <string.h>
+
+#include <mailutils/sys/list.h>
+#include <mailutils/sys/iterator.h>
+#include <mailutils/errno.h>
+
+#define DESTROY_ITEM(list, elt) \
+ do \
+ { \
+ if ((list)->destroy_item) \
+ (list)->destroy_item ((elt)->item); \
+ } \
+ while (0)
+
+int
+mu_list_create (mu_list_t *plist)
+{
+ mu_list_t list;
+ int status;
+
+ if (plist == NULL)
+ return MU_ERR_OUT_PTR_NULL;
+ list = calloc (sizeof (*list), 1);
+ if (list == NULL)
+ return ENOMEM;
+ status = mu_monitor_create (&list->monitor, 0, list);
+ if (status != 0)
+ {
+ free (list);
+ return status;
+ }
+ list->head.next = &list->head;
+ list->head.prev = &list->head;
+ *plist = list;
+ return 0;
+}
+
+void
+mu_list_destroy (mu_list_t *plist)
+{
+ if (plist && *plist)
+ {
+ mu_list_t list = *plist;
+ struct list_data *current;
+ struct list_data *previous;
+
+ mu_monitor_wrlock (list->monitor);
+ for (current = list->head.next; current != &list->head;)
+ {
+ previous = current;
+ current = current->next;
+ DESTROY_ITEM (list, previous);
+ free (previous);
+ }
+ mu_monitor_unlock (list->monitor);
+ mu_monitor_destroy (&list->monitor, list);
+ free (list);
+ *plist = NULL;
+ }
+}
+
+int
+mu_list_append (mu_list_t list, void *item)
+{
+ struct list_data *ldata;
+ struct list_data *last;
+
+ if (list == NULL)
+ return EINVAL;
+ last = list->head.prev;
+ ldata = calloc (sizeof (*ldata), 1);
+ if (ldata == NULL)
+ return ENOMEM;
+ ldata->item = item;
+ mu_monitor_wrlock (list->monitor);
+ ldata->next = &list->head;
+ ldata->prev = list->head.prev;
+ last->next = ldata;
+ list->head.prev = ldata;
+ list->count++;
+ mu_monitor_unlock (list->monitor);
+ return 0;
+}
+
+int
+mu_list_prepend (mu_list_t list, void *item)
+{
+ struct list_data *ldata;
+ struct list_data *first;
+
+ if (list == NULL)
+ return EINVAL;
+ first = list->head.next;
+ ldata = calloc (sizeof (*ldata), 1);
+ if (ldata == NULL)
+ return ENOMEM;
+ ldata->item = item;
+ mu_monitor_wrlock (list->monitor);
+ ldata->prev = &list->head;
+ ldata->next = list->head.next;
+ first->prev = ldata;
+ list->head.next = ldata;
+ list->count++;
+ mu_monitor_unlock (list->monitor);
+ return 0;
+}
+
+int
+mu_list_is_empty (mu_list_t list)
+{
+ size_t n = 0;
+
+ mu_list_count (list, &n);
+ return (n == 0);
+}
+
+int
+mu_list_count (mu_list_t list, size_t *pcount)
+{
+ if (list == NULL)
+ return EINVAL;
+ if (pcount == NULL)
+ return MU_ERR_OUT_PTR_NULL;
+ *pcount = list->count;
+ return 0;
+}
+
+mu_list_comparator_t
+mu_list_set_comparator (mu_list_t list, mu_list_comparator_t comp)
+{
+ mu_list_comparator_t old_comp;
+
+ if (list == NULL)
+ return NULL;
+ old_comp = list->comp;
+ list->comp = comp;
+ return old_comp;
+}
+
+int
+mu_list_get_comparator (mu_list_t list, mu_list_comparator_t *comp)
+{
+ if (!list)
+ return EINVAL;
+ *comp = list->comp;
+ return 0;
+}
+
+int
+_mu_list_ptr_comparator (const void *item, const void *value)
+{
+ return item != value;
+}
+
+int
+mu_list_locate (mu_list_t list, void *item, void **ret_item)
+{
+ struct list_data *current, *previous;
+ mu_list_comparator_t comp;
+ int status = MU_ERR_NOENT;
+
+ if (list == NULL)
+ return EINVAL;
+ comp = list->comp ? list->comp : _mu_list_ptr_comparator;
+ mu_monitor_wrlock (list->monitor);
+ for (previous = &list->head, current = list->head.next;
+ current != &list->head; previous = current, current = current->next)
+ {
+ if (comp (current->item, item) == 0)
+ {
+ if (ret_item)
+ *ret_item = current->item;
+ status = 0;
+ break;
+ }
+ }
+ mu_monitor_unlock (list->monitor);
+ return status;
+}
+
+static int
+_insert_item (mu_list_t list, struct list_data *current, void *new_item,
+ int insert_before)
+{
+ int status;
+ struct list_data *ldata = calloc (sizeof (*ldata), 1);
+ if (ldata == NULL)
+ status = ENOMEM;
+ else
+ {
+ ldata->item = new_item;
+ _mu_list_insert_sublist (list, current,
+ ldata, ldata,
+ 1,
+ insert_before);
+ status = 0;
+ }
+ return status;
+}
+
+int
+mu_list_insert (mu_list_t list, void *item, void *new_item, int insert_before)
+{
+ struct list_data *current;
+ mu_list_comparator_t comp;
+ int status = MU_ERR_NOENT;
+
+ if (list == NULL)
+ return EINVAL;
+ comp = list->comp ? list->comp : _mu_list_ptr_comparator;
+
+ mu_monitor_wrlock (list->monitor);
+ for (current = list->head.next;
+ current != &list->head;
+ current = current->next)
+ {
+ if (comp (current->item, item) == 0)
+ {
+ status = _insert_item (list, current, new_item, insert_before);
+ break;
+ }
+ }
+ mu_monitor_unlock (list->monitor);
+ return status;
+}
+
+int
+mu_list_remove (mu_list_t list, void *item)
+{
+ struct list_data *current;
+ mu_list_comparator_t comp;
+ int status = MU_ERR_NOENT;
+
+ if (list == NULL)
+ return EINVAL;
+ comp = list->comp ? list->comp : _mu_list_ptr_comparator;
+ mu_monitor_wrlock (list->monitor);
+ for (current = list->head.next;
+ current != &list->head; current = current->next)
+ {
+ if (comp (current->item, item) == 0)
+ {
+ struct list_data *previous = current->prev;
+
+ mu_iterator_advance (list->itr, current);
+ previous->next = current->next;
+ current->next->prev = previous;
+ DESTROY_ITEM (list, current);
+ free (current);
+ list->count--;
+ status = 0;
+ break;
+ }
+ }
+ mu_monitor_unlock (list->monitor);
+ return status;
+}
+
+int
+mu_list_remove_nd (mu_list_t list, void *item)
+{
+ mu_list_destroy_item_t dptr = mu_list_set_destroy_item (list, NULL);
+ int rc = mu_list_remove (list, item);
+ mu_list_set_destroy_item (list, dptr);
+ return rc;
+}
+
+int
+mu_list_replace (mu_list_t list, void *old_item, void *new_item)
+{
+ struct list_data *current, *previous;
+ mu_list_comparator_t comp;
+ int status = MU_ERR_NOENT;
+
+ if (list == NULL)
+ return EINVAL;
+ comp = list->comp ? list->comp : _mu_list_ptr_comparator;
+ mu_monitor_wrlock (list->monitor);
+ for (previous = &list->head, current = list->head.next;
+ current != &list->head; previous = current, current = current->next)
+ {
+ if (comp (current->item, old_item) == 0)
+ {
+ DESTROY_ITEM (list, current);
+ current->item = new_item;
+ status = 0;
+ break;
+ }
+ }
+ mu_monitor_unlock (list->monitor);
+ return status;
+}
+
+int
+mu_list_replace_nd (mu_list_t list, void *item, void *new_item)
+{
+ mu_list_destroy_item_t dptr = mu_list_set_destroy_item (list, NULL);
+ int rc = mu_list_replace (list, item, new_item);
+ mu_list_set_destroy_item (list, dptr);
+ return rc;
+}
+
+int
+mu_list_get (mu_list_t list, size_t indx, void **pitem)
+{
+ struct list_data *current;
+ size_t count;
+ int status = MU_ERR_NOENT;
+
+ if (list == NULL)
+ return EINVAL;
+ if (pitem == NULL)
+ return MU_ERR_OUT_PTR_NULL;
+ mu_monitor_rdlock (list->monitor);
+ for (current = list->head.next, count = 0; current != &list->head;
+ current = current->next, count++)
+ {
+ if (count == indx)
+ {
+ *pitem = current->item;
+ status = 0;
+ break;
+ }
+ }
+ mu_monitor_unlock (list->monitor);
+ return status;
+}
+
+int
+mu_list_do (mu_list_t list, mu_list_action_t *action, void *cbdata)
+{
+ mu_iterator_t itr;
+ int status = 0;
+
+ if (list == NULL || action == NULL)
+ return EINVAL;
+ status = mu_list_get_iterator (list, &itr);
+ if (status)
+ return status;
+ for (mu_iterator_first (itr); !mu_iterator_is_done (itr);
+ mu_iterator_next (itr))
+ {
+ void *item;
+ mu_iterator_current (itr, &item);
+ if ((status = action (item, cbdata)))
+ break;
+ }
+ mu_iterator_destroy (&itr);
+ return status;
+}
+
+mu_list_destroy_item_t
+mu_list_set_destroy_item (mu_list_t list, void (*destroy_item)(void *item))
+{
+ mu_list_destroy_item_t ret = list->destroy_item;
+ list->destroy_item = destroy_item;
+ return ret;
+}
+
+int
+mu_list_to_array (mu_list_t list, void **array, size_t count, size_t *pcount)
+{
+ size_t total = 0;
+
+ if (!list)
+ return EINVAL;
+
+ total = (count < list->count) ? count : list->count;
+
+ if (array)
+ {
+ size_t i;
+ struct list_data *current;
+
+ for (i = 0, current = list->head.next;
+ i < total && current != &list->head; current = current->next)
+ array[i++] = current->item;
+ }
+ if (pcount)
+ *pcount = total;
+ return 0;
+}
+
+/* Computes an intersection of two lists and returns it in PDEST.
+ The resulting list contains elements from A that are
+ also encountered in B (as per comparison function of
+ the latter).
+
+ If DUP_ITEM is not NULL, it is used to create copies of
+ items to be stored in PDEST. In this case, the destroy_item
+ function of B is also attached to PDEST. Otherwise, if
+ DUP_ITEM is NULL, pointers to elements are stored and
+ no destroy_item function is assigned. */
+int
+mu_list_intersect_dup (mu_list_t *pdest, mu_list_t a, mu_list_t b,
+ int (*dup_item) (void **, void *, void *),
+ void *dup_closure)
+{
+ mu_list_t dest;
+ int rc;
+ mu_iterator_t itr;
+
+ rc = mu_list_create (&dest);
+ if (rc)
+ return rc;
+
+ mu_list_set_comparator (dest, b->comp);
+ if (dup_item)
+ mu_list_set_destroy_item (dest, b->destroy_item);
+
+ rc = mu_list_get_iterator (a, &itr);
+ if (rc)
+ {
+ mu_list_destroy (&dest);
+ return rc;
+ }
+
+ rc = 0;
+ for (mu_iterator_first (itr); !mu_iterator_is_done (itr);
+ mu_iterator_next (itr))
+ {
+ void *data;
+ mu_iterator_current (itr, &data);
+ if (mu_list_locate (b, data, NULL) == 0)
+ {
+ void *new_data;
+ if (dup_item && data)
+ {
+ rc = dup_item (&new_data, data, dup_closure);
+ if (rc)
+ break;
+ }
+ else
+ new_data = data;
+
+ mu_list_append (dest, new_data); /* FIXME: Check return, and? */
+ }
+ }
+ mu_iterator_destroy (&itr);
+ *pdest = dest;
+ return rc;
+}
+
+int
+mu_list_intersect (mu_list_t *pdest, mu_list_t a, mu_list_t b)
+{
+ return mu_list_intersect_dup (pdest, a, b, NULL, NULL);
+}
+
+
+/* Iterator interface */
+
+struct list_iterator
+{
+ mu_list_t list;
+ struct list_data *cur;
+ int backwards; /* true if iterating backwards */
+};
+
+static int
+first (void *owner)
+{
+ struct list_iterator *itr = owner;
+ if (itr->backwards)
+ itr->cur = itr->list->head.prev;
+ else
+ itr->cur = itr->list->head.next;
+ return 0;
+}
+
+static int
+next (void *owner)
+{
+ struct list_iterator *itr = owner;
+ if (itr->backwards)
+ itr->cur = itr->cur->prev;
+ else
+ itr->cur = itr->cur->next;
+ return 0;
+}
+
+static int
+getitem (void *owner, void **pret, const void **pkey)
+{
+ struct list_iterator *itr = owner;
+ *pret = itr->cur->item;
+ if (pkey)
+ *pkey = NULL;
+ return 0;
+}
+
+static int
+finished_p (void *owner)
+{
+ struct list_iterator *itr = owner;
+ return itr->cur == &itr->list->head;
+}
+
+static int
+destroy (mu_iterator_t iterator, void *data)
+{
+ struct list_iterator *itr = data;
+ mu_iterator_detach (&itr->list->itr, iterator);
+ free (data);
+ return 0;
+}
+
+static int
+curitem_p (void *owner, void *item)
+{
+ struct list_iterator *itr = owner;
+ return itr->cur == item;
+}
+
+static int
+list_data_dup (void **ptr, void *owner)
+{
+ *ptr = malloc (sizeof (struct list_iterator));
+ if (*ptr == NULL)
+ return ENOMEM;
+ memcpy (*ptr, owner, sizeof (struct list_iterator));
+ return 0;
+}
+
+static int
+list_itrctl (void *owner, enum mu_itrctl_req req, void *arg)
+{
+ struct list_iterator *itr = owner;
+ mu_list_t list = itr->list;
+ struct list_data *ptr;
+
+ if (itr->cur == NULL)
+ return MU_ERR_NOENT;
+ switch (req)
+ {
+ case mu_itrctl_tell:
+ /* Return current position in the object */
+ {
+ size_t count;
+
+ for (count = 0, ptr = list->head.next; ptr != &list->head;
+ ptr = ptr->next, count++)
+ {
+ if (ptr == itr->cur)
+ {
+ *(size_t*)arg = count;
+ return 0;
+ }
+ }
+ return MU_ERR_NOENT;
+ }
+
+ case mu_itrctl_delete:
+ case mu_itrctl_delete_nd:
+ /* Delete current element */
+ {
+ struct list_data *prev;
+
+ ptr = itr->cur;
+ prev = ptr->prev;
+
+ mu_iterator_advance (list->itr, ptr);
+ prev->next = ptr->next;
+ ptr->next->prev = prev;
+ if (req == mu_itrctl_delete)
+ DESTROY_ITEM (list, ptr);
+ free (ptr);
+ list->count--;
+ }
+ break;
+
+ case mu_itrctl_replace:
+ case mu_itrctl_replace_nd:
+ /* Replace current element */
+ if (!arg)
+ return EINVAL;
+ if (req == mu_itrctl_replace)
+ DESTROY_ITEM (list, ptr);
+ ptr = itr->cur;
+ ptr->item = arg;
+ break;
+
+ case mu_itrctl_insert:
+ /* Insert new element in the current position */
+ if (!arg)
+ return EINVAL;
+ return _insert_item (list, itr->cur, arg, 0);
+
+ case mu_itrctl_insert_list:
+ /* Insert a list of elements */
+ if (!arg)
+ return EINVAL;
+ else
+ {
+ mu_list_t new_list = arg;
+ _mu_list_insert_sublist (list, itr->cur,
+ new_list->head.next, new_list->head.prev,
+ new_list->count,
+ 0);
+ _mu_list_clear (new_list);
+ }
+ break;
+
+ case mu_itrctl_qry_direction:
+ if (!arg)
+ return EINVAL;
+ else
+ *(int*)arg = itr->backwards;
+ break;
+
+ case mu_itrctl_set_direction:
+ if (!arg)
+ return EINVAL;
+ else
+ itr->backwards = !!*(int*)arg;
+ break;
+
+ default:
+ return ENOSYS;
+ }
+ return 0;
+}
+
+int
+mu_list_get_iterator (mu_list_t list, mu_iterator_t *piterator)
+{
+ mu_iterator_t iterator;
+ int status;
+ struct list_iterator *itr;
+
+ if (!list)
+ return EINVAL;
+
+ itr = calloc (1, sizeof *itr);
+ if (!itr)
+ return ENOMEM;
+ itr->list = list;
+ itr->cur = NULL;
+
+ status = mu_iterator_create (&iterator, itr);
+ if (status)
+ {
+ free (itr);
+ return status;
+ }
+
+ mu_iterator_set_first (iterator, first);
+ mu_iterator_set_next (iterator, next);
+ mu_iterator_set_getitem (iterator, getitem);
+ mu_iterator_set_finished_p (iterator, finished_p);
+ mu_iterator_set_curitem_p (iterator, curitem_p);
+ mu_iterator_set_destroy (iterator, destroy);
+ mu_iterator_set_dup (iterator, list_data_dup);
+ mu_iterator_set_itrctl (iterator, list_itrctl);
+
+ mu_iterator_attach (&list->itr, iterator);
+
+ *piterator = iterator;
+ return 0;
+}
diff --git a/libmailutils/listlist.c b/libmailutils/listlist.c
new file mode 100644
index 000000000..94172ac8f
--- /dev/null
+++ b/libmailutils/listlist.c
@@ -0,0 +1,139 @@
+/* GNU Mailutils -- a suite of utilities for electronic mail
+ Copyright (C) 1999, 2000, 2001, 2004, 2005, 2007, 2008, 2010 Free
+ Software Foundation, Inc.
+
+ This library is free software; you can redistribute it and/or
+ modify it under the terms of the GNU Lesser General Public
+ License as published by the Free Software Foundation; either
+ version 3 of the License, or (at your option) any later version.
+
+ This library 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
+ Lesser General Public License for more details.
+
+ You should have received a copy of the GNU Lesser General
+ Public License along with this library; if not, write to the
+ Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor,
+ Boston, MA 02110-1301 USA */
+
+#ifdef HAVE_CONFIG_H
+# include <config.h>
+#endif
+
+#include <errno.h>
+#include <stdlib.h>
+#include <string.h>
+
+#include <mailutils/sys/list.h>
+#include <mailutils/sys/iterator.h>
+#include <mailutils/errno.h>
+
+void
+_mu_list_insert_sublist (mu_list_t list,
+ struct list_data *current,
+ struct list_data *head,
+ struct list_data *tail,
+ size_t count,
+ int insert_before)
+{
+ if (insert_before)
+ {
+ head->prev = current->prev;
+ tail->next = current;
+ if (current->prev != &list->head)
+ current->prev->next = head;
+ else
+ list->head.next = head;
+
+ current->prev = tail;
+ }
+ else
+ {
+ tail->next = current->next;
+ head->prev = current;
+ if (current->next != &list->head)
+ current->next->prev = tail;
+ else
+ list->head.prev = tail;
+
+ current->next = head;
+ }
+ list->count += count;
+}
+
+void
+_mu_list_clear (mu_list_t list)
+{
+ list->head.next = list->head.prev = &list->head;
+ list->count = 0;
+}
+
+int
+mu_list_insert_list (mu_list_t list, void *item, mu_list_t new_list,
+ int insert_before)
+{
+ struct list_data *current;
+ mu_list_comparator_t comp;
+ int status = MU_ERR_NOENT;
+
+ if (list == NULL)
+ return EINVAL;
+ comp = list->comp ? list->comp : _mu_list_ptr_comparator;
+
+ mu_monitor_wrlock (list->monitor);
+ for (current = list->head.next;
+ current != &list->head;
+ current = current->next)
+ {
+ if (comp (current->item, item) == 0)
+ {
+ _mu_list_insert_sublist (list, current,
+ new_list->head.next, new_list->head.prev,
+ new_list->count,
+ insert_before);
+ _mu_list_clear (new_list);
+ status = 0;
+ break;
+ }
+ }
+ mu_monitor_unlock (list->monitor);
+ return status;
+}
+
+void
+mu_list_append_list (mu_list_t list, mu_list_t new_list)
+{
+ if (list->count == 0)
+ {
+ list->head = new_list->head;
+ list->head.next->prev = list->head.prev->next = &list->head;
+ list->count = new_list->count;
+ }
+ else
+ _mu_list_insert_sublist (list, list->head.prev,
+ new_list->head.next, new_list->head.prev,
+ new_list->count,
+ 0);
+ _mu_list_clear (new_list);
+}
+
+void
+mu_list_prepend_list (mu_list_t list, mu_list_t new_list)
+{
+ if (list->count == 0)
+ {
+ list->head = new_list->head;
+ list->head.next->prev = list->head.prev->next = &list->head;
+ list->count = new_list->count;
+ }
+ else
+ _mu_list_insert_sublist (list, list->head.next,
+ new_list->head.next, new_list->head.prev,
+ new_list->count,
+ 1);
+ _mu_list_clear (new_list);
+}
+
+
+
diff --git a/libmailutils/locale.c b/libmailutils/locale.c
new file mode 100644
index 000000000..b6bdc086a
--- /dev/null
+++ b/libmailutils/locale.c
@@ -0,0 +1,242 @@
+/* GNU Mailutils -- a suite of utilities for electronic mail
+ Copyright (C) 2003, 2009, 2010 Free Software Foundation, Inc.
+
+ This library is free software; you can redistribute it and/or
+ modify it under the terms of the GNU Lesser General Public
+ License as published by the Free Software Foundation; either
+ version 3 of the License, or (at your option) any later version.
+
+ This library 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
+ Lesser General Public License for more details.
+
+ You should have received a copy of the GNU Lesser General
+ Public License along with this library; if not, write to the
+ Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor,
+ Boston, MA 02110-1301 USA */
+
+#ifdef HAVE_CONFIG_H
+# include <config.h>
+#endif
+
+#include <stdlib.h>
+#include <string.h>
+#include <mailutils/mutil.h>
+#include <mailutils/cstr.h>
+
+struct langtab
+{
+ char *lang; /* Language code */
+ char *terr; /* Territory code */
+ char *charset; /* Corresponding charset */
+};
+
+/* The list of language codes defined in ISO 639 with the corresponding
+ default character sets.
+
+ NOTES:
+
+ 1) The list must be ordered by:
+ a) lang field in ascending order
+ b) terr field in descending order.
+ NULL fields are considered less than non-null ones.
+ 2) Many entries have NULL charset fields. Please help fill them!
+ 3) The "default" character set for a given language is a matter
+ of preference. Possibly the table should contain a *list* of
+ possible character sets.
+ 4) LC_ALL "modifier" field is not taken into account */
+
+static struct langtab langtab[] = {
+ { "C", NULL, "ASCII"},
+ { "POSIX", NULL, "ASCII" },
+ { "aa", NULL, NULL}, /* Afar */
+ { "ab", NULL, NULL}, /* Abkhazian */
+ { "ae", NULL, NULL}, /* Avestan */
+ { "af", NULL, "iso-8859-1"}, /* Afrikaans */
+ { "am", NULL, "UTF-8"}, /* Amharic */
+ { "ar", NULL, "iso-8859-6"}, /* Arabic */
+ { "as", NULL, NULL}, /* Assamese */
+ { "ay", NULL, "iso-8859-1"}, /* Aymara */
+ { "az", NULL, NULL}, /* Azerbaijani */
+ { "ba", NULL, NULL}, /* Bashkir */
+ { "be", NULL, "UTF-8"}, /* Byelorussian; Belarusian */
+ { "bg", NULL, "iso-8859-5"}, /* Bulgarian */
+ { "bh", NULL, NULL}, /* Bihari */
+ { "bi", NULL, NULL}, /* Bislama */
+ { "bn", NULL, NULL}, /* Bengali; Bangla */
+ { "bo", NULL, NULL}, /* Tibetan */
+ { "br", NULL, "iso-8859-1"}, /* Breton: 1,5,8,9 */
+ { "bs", NULL, NULL}, /* Bosnian */
+ { "ca", NULL, "iso-8859-1"}, /* Catalan: 1,5,8,9 */
+ { "ce", NULL, NULL}, /* Chechen */
+ { "ch", NULL, NULL}, /* Chamorro */
+ { "co", NULL, "iso-8859-1"}, /* Corsican */
+ { "cs", NULL, "iso-8859-2"}, /* Czech */
+ { "cu", NULL, NULL }, /* Church Slavic */
+ { "cv", NULL, NULL}, /* Chuvash */
+ { "cy", NULL, "iso-8859-1"}, /* Welsh */
+ { "da", NULL, "iso-8859-1"}, /* Danish: 4-9 */
+ { "de", NULL, "iso-8859-1"}, /* German */
+ { "dz", NULL, NULL }, /* Dzongkha; Bhutani */
+ { "el", NULL, "iso-8859-7"}, /* Greek */
+ { "en", NULL, "iso-8859-1"}, /* English */
+ { "eo", NULL, "iso-8859-3"}, /* Esperanto */
+ { "es", NULL, "iso-8859-1"}, /* Spanish */
+ { "et", NULL, "iso-8859-15"}, /* Estonian: 6,7,9 */
+ { "eu", NULL, "iso-8859-1"}, /* Basque: 5,8,9 */
+ { "fa", NULL, "UTF-8"}, /* Persian */
+ { "fi", NULL, "iso-8859-15"}, /* Finnish */
+ { "fj", NULL, NULL }, /* Fijian; Fiji */
+ { "fo", NULL, "iso-8859-1"}, /* Faroese: 6,9 */
+ { "fr", NULL, "iso-8859-1"}, /* French */
+ { "fy", NULL, "iso-8859-1"}, /* Frisian */
+ { "ga", NULL, "iso-8859-14"}, /* Irish */
+ { "gd", NULL, "iso-8859-14" }, /* Scots; Gaelic */
+ { "gl", NULL, NULL }, /* Gallegan; Galician */
+ { "gn", NULL, NULL}, /* Guarani */
+ { "gu", NULL, NULL}, /* Gujarati */
+ { "gv", NULL, "iso-8859-14"}, /* Manx */
+ { "ha", NULL, NULL }, /* Hausa (?) */
+ { "he", NULL, "iso-8859-8" }, /* Hebrew */
+ { "hi", NULL, NULL}, /* Hindi */
+ { "ho", NULL, NULL}, /* Hiri Motu */
+ { "hr", NULL, "iso-8859-2"}, /* Croatian: 10 */
+ { "hu", NULL, "iso-8859-2"}, /* Hungarian */
+ { "hy", NULL, NULL}, /* Armenian */
+ { "hz", NULL, NULL}, /* Herero */
+ { "id", NULL, "iso-8859-1"}, /* Indonesian (formerly in) */
+ { "ia", NULL, NULL}, /* Interlingua */
+ { "ie", NULL, NULL}, /* Interlingue */
+ { "ik", NULL, NULL}, /* Inupiak */
+ { "io", NULL, NULL}, /* Ido */
+ { "is", NULL, "iso-8859-1"}, /* Icelandic */
+ { "it", NULL, "iso-8859-1"}, /* Italian */
+ { "iu", NULL, NULL}, /* Inuktitut */
+ { "ja", NULL, "EUC-JP"}, /* Japanese */
+ { "jv", NULL, NULL}, /* Javanese */
+ { "ka", NULL, NULL}, /* Georgian */
+ { "ki", NULL, NULL}, /* Kikuyu */
+ { "kj", NULL, NULL}, /* Kuanyama */
+ { "kk", NULL, NULL}, /* Kazakh */
+ { "kl", NULL, "iso-8859-1"}, /* Kalaallisut; Greenlandic */
+ { "km", NULL, NULL}, /* Khmer; Cambodian */
+ { "kn", NULL, NULL}, /* Kannada */
+ { "ko", NULL, "EUC-KR"}, /* Korean */
+ { "ks", NULL, NULL}, /* Kashmiri */
+ { "ku", NULL, NULL}, /* Kurdish */
+ { "kv", NULL, NULL}, /* Komi */
+ { "kw", NULL, "iso-8859-14"}, /* Cornish: 1,5,8 */
+ { "ky", NULL, NULL}, /* Kirghiz */
+ { "la", NULL, "iso-8859-1"}, /* Latin */
+ { "lb", NULL, "iso-8859-1"}, /* Letzeburgesch */
+ { "ln", NULL, NULL}, /* Lingala */
+ { "lo", NULL, NULL}, /* Lao; Laotian */
+ { "lt", NULL, "iso-8859-4"}, /* Lithuanian */
+ { "lv", NULL, "iso-8859-4"}, /* Latvian; Lettish */
+ { "mg", NULL, NULL}, /* Malagasy */
+ { "mh", NULL, NULL}, /* Marshall */
+ { "mi", NULL, NULL}, /* Maori */
+ { "mk", NULL, NULL}, /* Macedonian */
+ { "ml", NULL, NULL}, /* Malayalam */
+ { "mn", NULL, NULL}, /* Mongolian */
+ { "mo", NULL, "iso-8859-2"}, /* Moldavian */
+ { "mr", NULL, NULL}, /* Marathi */
+ { "ms", NULL, NULL}, /* Malay */
+ { "mt", NULL, "iso-8859-3"}, /* Maltese */
+ { "my", NULL, NULL}, /* Burmese */
+ { "na", NULL, NULL}, /* Nauru */
+ { "nb", NULL, "iso-8859-1"}, /* Norwegian Bokmċl; Bokm@aa{}l */
+ { "nd", NULL, NULL}, /* Ndebele, North */
+ { "ne", NULL, NULL}, /* Nepali */
+ { "ng", NULL, NULL}, /* Ndonga */
+ { "nl", NULL, "iso-8859-1"}, /* Dutch: 5,9 */
+ { "nn", NULL, "iso-8859-1"}, /* Norwegian Nynorsk */
+ { "no", NULL, "iso-8859-1"}, /* Norwegian */
+ { "nr", NULL, NULL}, /* Ndebele, South */
+ { "nv", NULL, NULL}, /* Navajo */
+ { "ny", NULL, NULL}, /* Chichewa; Nyanja */
+ { "oc", NULL, NULL}, /* Occitan; Provençal; Proven@,{c}al */
+ { "om", NULL, NULL}, /* (Afan) Oromo */
+ { "or", NULL, NULL}, /* Oriya */
+ { "os", NULL, NULL}, /* Ossetian; Ossetic */
+ { "pa", NULL, NULL}, /* Panjabi; Punjabi */
+ { "pi", NULL, NULL}, /* Pali */
+ { "pl", NULL, "iso-8859-2"}, /* Polish */
+ { "ps", NULL, NULL}, /* Pashto, Pushto */
+ { "pt", NULL, "iso-8859-1"}, /* Portuguese */
+ { "qu", NULL, "iso-8859-1"}, /* Quechua */
+ { "rm", NULL, "iso-8859-1"}, /* Rhaeto-Romance */
+ { "rn", NULL, NULL }, /* Rundi; Kirundi */
+ { "ro", NULL, "iso-8859-2"}, /* Romanian */
+ { "ru", NULL, "koi8-r"}, /* Russian */
+ { "rw", NULL, NULL}, /* Kinyarwanda */
+ { "sa", NULL, NULL}, /* Sanskrit */
+ { "sc", NULL, "iso-8859-1"}, /* Sardinian */
+ { "sd", NULL, NULL}, /* Sindhi */
+ { "se", NULL, "iso-8859-10"}, /* Northern Sami */
+ { "sg", NULL, NULL}, /* Sango; Sangro */
+ { "si", NULL, NULL}, /* Sinhalese */
+ { "sk", NULL, "iso-8859-2"}, /* Slovak */
+ { "sl", NULL, "iso-8859-1"}, /* Slovenian */
+ { "sm", NULL, NULL}, /* Samoan */
+ { "sn", NULL, NULL}, /* Shona */
+ { "so", NULL, NULL}, /* Somali */
+ { "sq", NULL, "iso-8859-1"}, /* Albanian: 2,5,8,9,10 */
+ { "sr", NULL, "iso-8859-2"}, /* Serbian */
+ { "ss", NULL, NULL}, /* Swati; Siswati */
+ { "st", NULL, NULL}, /* Sesotho; Sotho, Southern */
+ { "su", NULL, NULL}, /* Sundanese */
+ { "sv", NULL, "iso-8859-1"}, /* Swedish */
+ { "sw", NULL, NULL}, /* Swahili */
+ { "ta", NULL, NULL}, /* Tamil */
+ { "te", NULL, NULL}, /* Telugu */
+ { "tg", NULL, NULL}, /* Tajik */
+ { "th", NULL, "iso-8859-11"}, /* Thai */
+ { "ti", NULL, NULL}, /* Tigrinya */
+ { "tk", NULL, NULL}, /* Turkmen */
+ { "tl", NULL, "iso-8859-1"}, /* Tagalog */
+ { "tn", NULL, NULL}, /* Tswana; Setswana */
+ { "to", NULL, NULL}, /* Tonga (?) */
+ { "tr", NULL, "iso-8859-9"}, /* Turkish */
+ { "ts", NULL, NULL}, /* Tsonga */
+ { "tt", NULL, NULL}, /* Tatar */
+ { "tw", NULL, NULL}, /* Twi */
+ { "ty", NULL, NULL}, /* Tahitian */
+ { "ug", NULL, NULL}, /* Uighur */
+ { "uk", NULL, "koi8-u"}, /* Ukrainian */
+ { "ur", NULL, NULL}, /* Urdu */
+ { "uz", NULL, NULL}, /* Uzbek */
+ { "vi", NULL, NULL}, /* Vietnamese */
+ { "vo", NULL, NULL}, /* Volapük; Volap@"{u}k; Volapuk */
+ { "wa", NULL, "iso-8859-1"}, /* Walloon */
+ { "wo", NULL, NULL}, /* Wolof */
+ { "xh", NULL, NULL}, /* Xhosa */
+ { "yi", NULL, "iso-8859-8"}, /* Yiddish (formerly ji) */
+ { "yo", NULL, NULL}, /* Yoruba */
+ { "za", NULL, NULL}, /* Zhuang */
+ { "zh", "TW", "big5"}, /* Chinese */
+ { "zh", NULL, "gb2312"}, /* Chinese */
+ { "zu", NULL, NULL}, /* Zulu */
+ { NULL }
+};
+
+/* Given the language and (optionally) territory code, return the
+ default character set for that language. See notes above. */
+
+const char *
+mu_charset_lookup (char *lang, char *terr)
+{
+ static struct langtab *p;
+
+ if (!lang)
+ return NULL;
+ for (p = langtab; p->lang; p++)
+ if (mu_c_strcasecmp (p->lang, lang) == 0
+ && (terr == NULL
+ || p->terr == NULL
+ || !mu_c_strcasecmp (p->terr, terr) == 0))
+ return p->charset;
+ return NULL;
+}
+
diff --git a/libmailutils/locker.c b/libmailutils/locker.c
new file mode 100644
index 000000000..f752c1661
--- /dev/null
+++ b/libmailutils/locker.c
@@ -0,0 +1,1025 @@
+/* GNU Mailutils -- a suite of utilities for electronic mail
+ Copyright (C) 1999, 2000, 2001, 2005, 2006, 2007, 2008, 2010 Free
+ Software Foundation, Inc.
+
+ This library is free software; you can redistribute it and/or
+ modify it under the terms of the GNU Lesser General Public
+ License as published by the Free Software Foundation; either
+ version 3 of the License, or (at your option) any later version.
+
+ This library 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
+ Lesser General Public License for more details.
+
+ You should have received a copy of the GNU Lesser General
+ Public License along with this library; if not, write to the
+ Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor,
+ Boston, MA 02110-1301 USA */
+
+#ifdef HAVE_CONFIG_H
+# include <config.h>
+#endif
+
+#include <assert.h>
+#include <errno.h>
+#include <fcntl.h>
+#include <limits.h>
+#include <signal.h>
+#include <stdio.h>
+#include <stdlib.h>
+#include <string.h>
+#include <time.h>
+#include <unistd.h>
+#include <fcntl.h>
+#include <utime.h>
+
+#include <sys/param.h>
+#include <sys/stat.h>
+#include <sys/types.h>
+#include <sys/wait.h>
+
+#include <mailutils/errno.h>
+#include <mailutils/locker.h>
+#include <mailutils/mutil.h>
+
+#define LOCKFILE_ATTR 0644
+
+/* First draft by Brian Edmond. */
+/* For subsequent modifications, see the GNU mailutils ChangeLog. */
+
+struct _mu_locker
+{
+ unsigned refcnt; /* Number of times mu_locker_lock was called */
+ enum mu_locker_mode mode; /* Current locking mode (if refcnt > 0) */
+
+ char *file;
+ int flags;
+ int expire_time;
+ int retries;
+ int retry_sleep;
+
+ union lock_data {
+ struct {
+ char *dotlock;
+ char *nfslock;
+ } dot;
+
+ struct {
+ char *name;
+ } external;
+
+ struct {
+ int fd;
+ } kernel;
+ } data;
+};
+
+#define MU_LOCKER_TYPE(l) MU_LOCKER_FLAG_TO_TYPE((l)->flags)
+
+struct locker_tab
+{
+ int (*init) (mu_locker_t);
+ void (*destroy) (mu_locker_t);
+ int (*prelock) (mu_locker_t);
+ int (*lock) (mu_locker_t, enum mu_locker_mode);
+ int (*unlock) (mu_locker_t);
+};
+
+static int init_dotlock (mu_locker_t);
+static void destroy_dotlock (mu_locker_t);
+static int lock_dotlock (mu_locker_t, enum mu_locker_mode);
+static int unlock_dotlock (mu_locker_t);
+
+static int init_external (mu_locker_t);
+static void destroy_external (mu_locker_t);
+static int lock_external (mu_locker_t, enum mu_locker_mode);
+static int unlock_external (mu_locker_t);
+
+static int init_kernel (mu_locker_t);
+static int lock_kernel (mu_locker_t, enum mu_locker_mode);
+static int unlock_kernel (mu_locker_t);
+
+static int prelock_common (mu_locker_t);
+
+static struct locker_tab locker_tab[] = {
+ /* MU_LOCKER_TYPE_DOTLOCK */
+ { init_dotlock, destroy_dotlock, prelock_common,
+ lock_dotlock, unlock_dotlock },
+ /* MU_LOCKER_TYPE_EXTERNAL */
+ { init_external, destroy_external, prelock_common,
+ lock_external, unlock_external },
+ /* MU_LOCKER_TYPE_KERNEL */
+ { init_kernel, NULL, NULL, lock_kernel, unlock_kernel },
+ /* MU_LOCKER_TYPE_NULL */
+ { NULL, NULL, NULL, NULL, NULL }
+};
+
+#define MU_LOCKER_NTYPES (sizeof (locker_tab) / sizeof (locker_tab[0]))
+
+
+static int
+stat_check (const char *file, int fd, int links)
+{
+ struct stat fn_stat;
+ struct stat fd_stat;
+ int err = 0;
+ int localfd = -1;
+
+ if (fd == -1)
+ {
+ localfd = open (file, O_RDONLY);
+
+ if (localfd == -1)
+ return errno;
+ fd = localfd;
+ }
+
+ /* We should always be able to stat a valid fd, so this
+ is an error condition. */
+ if (lstat (file, &fn_stat) || fstat (fd, &fd_stat))
+ err = errno;
+ else
+ {
+ /* If the link and stat don't report the same info, or the
+ file is a symlink, fail the locking. */
+#define CHK(X) if(X) err = EINVAL
+
+ CHK (!S_ISREG (fn_stat.st_mode));
+ CHK (!S_ISREG (fd_stat.st_mode));
+ CHK (fn_stat.st_nlink != links);
+ CHK (fn_stat.st_dev != fd_stat.st_dev);
+ CHK (fn_stat.st_ino != fd_stat.st_ino);
+ CHK (fn_stat.st_mode != fd_stat.st_mode);
+ CHK (fn_stat.st_nlink != fd_stat.st_nlink);
+ CHK (fn_stat.st_uid != fd_stat.st_uid);
+ CHK (fn_stat.st_gid != fd_stat.st_gid);
+ CHK (fn_stat.st_rdev != fd_stat.st_rdev);
+
+#undef CHK
+ }
+ if (localfd != -1)
+ close (localfd);
+
+ return err;
+}
+
+static int
+check_file_permissions (const char *file)
+{
+ int fd = -1;
+ int err = 0;
+
+ if ((fd = open (file, O_RDONLY)) == -1)
+ return errno == ENOENT ? 0 : errno;
+
+ err = stat_check (file, fd, 1);
+ close (fd);
+ fd = -1;
+ if (err)
+ {
+ if (err == EINVAL)
+ err = MU_ERR_LOCK_BAD_FILE;
+ return err;
+ }
+
+ return 0;
+}
+
+static int
+prelock_common (mu_locker_t locker)
+{
+ /* Check if we are trying to lock a regular file, with a link count
+ of 1, that we have permission to read, etc., or don't lock it. */
+ return check_file_permissions (locker->file);
+}
+
+
+static int mu_locker_default_flags = MU_LOCKER_DEFAULT;
+static time_t mu_locker_retry_timeout = MU_LOCKER_RETRY_SLEEP;
+static size_t mu_locker_retry_count = MU_LOCKER_RETRIES;
+static time_t mu_locker_expire_timeout = MU_LOCKER_EXPIRE_TIME;
+static char *mu_locker_external_program = NULL;
+
+int
+mu_locker_set_default_flags (int flags, enum mu_locker_set_mode mode)
+{
+ switch (mode)
+ {
+ case mu_locker_assign:
+ mu_locker_default_flags = flags;
+ break;
+
+ case mu_locker_set_bit:
+ mu_locker_default_flags |= flags;
+ break;
+
+ case mu_locker_clear_bit:
+ mu_locker_default_flags &= ~flags;
+ break;
+
+ default:
+ return EINVAL;
+ }
+ return 0;
+}
+
+void
+mu_locker_set_default_retry_timeout (time_t to)
+{
+ mu_locker_retry_timeout = to;
+}
+
+void
+mu_locker_set_default_retry_count (size_t n)
+{
+ mu_locker_retry_count = n;
+}
+
+void
+mu_locker_set_default_expire_timeout (time_t t)
+{
+ mu_locker_expire_timeout = t;
+}
+
+void
+mu_locker_set_default_external_program (char *path)
+{
+ free (mu_locker_external_program);
+ mu_locker_external_program = strdup (path);
+}
+
+int
+mu_locker_mod_flags (mu_locker_t locker, int flags,
+ enum mu_locker_set_mode mode)
+{
+ unsigned otype, ntype;
+ int new_flags;
+
+ if (!locker)
+ return MU_ERR_LOCKER_NULL;
+
+ switch (mode)
+ {
+ case mu_locker_assign:
+ new_flags = flags;
+ break;
+
+ case mu_locker_set_bit:
+ new_flags = locker->flags | flags;
+ break;
+
+ case mu_locker_clear_bit:
+ new_flags = locker->flags & ~flags;
+ break;
+
+ default:
+ return EINVAL;
+ }
+
+ otype = MU_LOCKER_TYPE (locker);
+ if (otype >= MU_LOCKER_NTYPES)
+ return EINVAL;
+ ntype = MU_LOCKER_FLAG_TO_TYPE (new_flags);
+ if (ntype >= MU_LOCKER_NTYPES)
+ return EINVAL;
+
+ if (ntype != otype)
+ {
+ int rc;
+
+ if (locker_tab[otype].destroy)
+ locker_tab[otype].destroy (locker);
+ locker->flags = new_flags;
+ if (locker_tab[ntype].init)
+ {
+ rc = locker_tab[ntype].init (locker);
+ if (rc)
+ locker->flags = MU_LOCKER_NULL;
+ return rc;
+ }
+ }
+ else
+ locker->flags = new_flags;
+
+ return 0;
+}
+
+int
+mu_locker_set_flags (mu_locker_t locker, int flags)
+{
+ return mu_locker_mod_flags (locker, flags, mu_locker_assign);
+}
+
+int
+mu_locker_set_expire_time (mu_locker_t locker, int etime)
+{
+ if (!locker)
+ return MU_ERR_LOCKER_NULL;
+
+ if (etime <= 0)
+ return EINVAL;
+
+ locker->expire_time = etime;
+
+ return 0;
+}
+
+int
+mu_locker_set_retries (mu_locker_t locker, int retries)
+{
+ if (!locker)
+ return MU_ERR_LOCKER_NULL;
+
+ if (retries <= 0)
+ return EINVAL;
+
+ locker->retries = retries;
+
+ return 0;
+}
+
+int
+mu_locker_set_retry_sleep (mu_locker_t locker, int retry_sleep)
+{
+ if (!locker)
+ return MU_ERR_LOCKER_NULL;
+
+ if (retry_sleep <= 0)
+ return EINVAL;
+
+ locker->retry_sleep = retry_sleep;
+
+ return 0;
+}
+
+int
+mu_locker_set_external (mu_locker_t locker, const char* program)
+{
+ char* p = NULL;
+
+ if (!locker)
+ return MU_ERR_LOCKER_NULL;
+ if (MU_LOCKER_TYPE (locker) != MU_LOCKER_TYPE_EXTERNAL)
+ return EINVAL;
+
+ /* program can be NULL */
+ if (program != 0)
+ {
+ p = strdup (program);
+ if (!p)
+ return ENOMEM;
+ }
+
+ free (locker->data.external.name);
+ locker->data.external.name = p;
+
+ return 0;
+}
+
+int
+mu_locker_get_flags (mu_locker_t locker, int *flags)
+{
+ if (!locker)
+ return MU_ERR_LOCKER_NULL;
+
+ if (!flags)
+ return EINVAL;
+
+ *flags = locker->flags;
+
+ return 0;
+}
+
+int
+mu_locker_get_expire_time (mu_locker_t locker, int *ptime)
+{
+ if (!locker)
+ return MU_ERR_LOCKER_NULL;
+
+ if (!ptime)
+ return EINVAL;
+
+ *ptime = locker->expire_time;
+
+ return 0;
+}
+
+int
+mu_locker_get_retries (mu_locker_t locker, int *retries)
+{
+ if (!locker)
+ return MU_ERR_LOCKER_NULL;
+
+ if (!retries)
+ return EINVAL;
+
+ *retries = locker->retries;
+
+ return 0;
+}
+
+int
+mu_locker_get_retry_sleep (mu_locker_t locker, int *retry_sleep)
+{
+ if (!locker)
+ return MU_ERR_LOCKER_NULL;
+
+ if (!retry_sleep)
+ return EINVAL;
+
+ *retry_sleep = locker->retry_sleep;
+
+ return 0;
+}
+
+int
+mu_locker_create (mu_locker_t *plocker, const char *fname, int flags)
+{
+ unsigned type;
+ mu_locker_t l;
+ char filename[_POSIX_PATH_MAX];
+ int err = 0;
+
+ if (plocker == NULL)
+ return MU_ERR_OUT_PTR_NULL;
+
+ if (fname == NULL)
+ return EINVAL;
+
+ if ((err = mu_unroll_symlink (filename, sizeof (filename), fname)))
+ return err;
+
+ l = calloc (1, sizeof (*l));
+
+ if (l == NULL)
+ return ENOMEM;
+
+ l->file = strdup (filename);
+
+ if (l->file == NULL)
+ {
+ free (l);
+ return ENOMEM;
+ }
+
+ if (strcmp (filename, "/dev/null") == 0)
+ l->flags = MU_LOCKER_NULL;
+ else if (flags)
+ l->flags = flags;
+ else
+ l->flags = mu_locker_default_flags;
+
+ l->expire_time = mu_locker_expire_timeout;
+ l->retries = mu_locker_retry_count;
+ l->retry_sleep = mu_locker_retry_timeout;
+
+ type = MU_LOCKER_TYPE (l);
+
+ if (type >= MU_LOCKER_NTYPES)
+ {
+ free (l->file);
+ return EINVAL;
+ }
+
+ /* Initialize locker-type-specific data */
+ err = locker_tab[type].init ? locker_tab[type].init (l) : 0;
+ if (err)
+ {
+ mu_locker_destroy (&l);
+ return err;
+ }
+
+ *plocker = l;
+
+ return 0;
+}
+
+void
+mu_locker_destroy (mu_locker_t *plocker)
+{
+ if (plocker && *plocker)
+ {
+ unsigned type = MU_LOCKER_TYPE (*plocker);
+ if (type < MU_LOCKER_NTYPES)
+ {
+ if (locker_tab[type].destroy)
+ locker_tab[type].destroy (*plocker);
+ free ((*plocker)->file);
+ free (*plocker);
+ *plocker = NULL;
+ }
+ }
+}
+
+int
+_mu_locker_lock (mu_locker_t lock, enum mu_locker_mode mode)
+{
+ int rc;
+ unsigned type;
+ unsigned retries = 1;
+
+ if (lock == NULL || (type = MU_LOCKER_TYPE (lock)) >= MU_LOCKER_NTYPES)
+ return EINVAL;
+
+ if (locker_tab[type].prelock && (rc = locker_tab[type].prelock (lock)))
+ return rc;
+
+ /* Is the lock already applied? */
+ if (lock->refcnt > 0)
+ {
+ lock->refcnt++;
+ if (mode == lock->mode)
+ return 0;
+ }
+
+ lock->mode = mode;
+
+ if (lock->flags & MU_LOCKER_RETRY)
+ retries = lock->retries;
+
+ if (locker_tab[type].lock)
+ {
+ while (retries--)
+ {
+ rc = locker_tab[type].lock (lock, mode);
+ if (rc == EAGAIN && retries)
+ {
+ sleep (lock->retry_sleep);
+ continue;
+ }
+
+ if (rc == 0)
+ lock->refcnt++;
+
+ break;
+ }
+ }
+ else
+ rc = 0;
+
+ return rc;
+}
+
+int
+mu_locker_lock (mu_locker_t lock)
+{
+ return _mu_locker_lock (lock, mu_lck_exc);
+}
+
+int
+mu_locker_unlock (mu_locker_t lock)
+{
+ int rc = 0;
+ unsigned type;
+
+ if (!lock)
+ return MU_ERR_LOCKER_NULL;
+
+ if (lock->refcnt == 0)
+ return MU_ERR_LOCK_NOT_HELD;
+
+ if ((rc = check_file_permissions (lock->file)))
+ return rc;
+
+ if (--lock->refcnt > 0)
+ return 0;
+
+ type = MU_LOCKER_TYPE (lock);
+ if (locker_tab[type].unlock)
+ rc = locker_tab[type].unlock (lock);
+ else
+ rc = 0;
+
+ return rc;
+}
+
+int
+mu_locker_remove_lock (mu_locker_t lock)
+{
+ if (!lock)
+ return MU_ERR_LOCKER_NULL;
+
+ /* Force the reference count to 1 to unlock the file. */
+ lock->refcnt = 1;
+ return mu_locker_unlock (lock);
+}
+
+
+#define DOTLOCK_SUFFIX ".lock"
+
+/* expire a stale lock (if MU_LOCKER_PID or MU_LOCKER_TIME) */
+static void
+expire_stale_lock (mu_locker_t lock)
+{
+ int stale = 0;
+ int fd = open (lock->data.dot.dotlock, O_RDONLY);
+ if (fd == -1)
+ return;
+
+ /* Check to see if this process is still running. */
+ if (lock->flags & MU_LOCKER_PID)
+ {
+ char buf[16];
+ pid_t pid;
+ int nread = read (fd, buf, sizeof (buf) - 1);
+ if (nread > 0)
+ {
+ buf[nread] = '\0';
+ pid = strtol (buf, NULL, 10);
+ if (pid > 0)
+ {
+ /* Process is gone so we try to remove the lock. */
+ if (kill (pid, 0) == -1)
+ stale = 1;
+ }
+ else
+ stale = 1; /* Corrupted file, remove the lock. */
+ }
+ }
+
+ /* Check to see if the lock expired. */
+ if (lock->flags & MU_LOCKER_TIME)
+ {
+ struct stat stbuf;
+
+ fstat (fd, &stbuf);
+ /* The lock has expired. */
+ if ((time (NULL) - stbuf.st_mtime) > lock->expire_time)
+ stale = 1;
+ }
+
+ close (fd);
+ if (stale)
+ unlink (lock->data.dot.dotlock);
+}
+
+static int
+init_dotlock (mu_locker_t locker)
+{
+ char *tmp, *p;
+
+ /* Make sure the spool directory is writable */
+ tmp = strdup (locker->file);
+ if (!tmp)
+ return ENOMEM;
+
+ strcpy (tmp, locker->file);
+ p = strrchr (tmp, '/');
+ if (!p)
+ {
+ free (tmp);
+ tmp = strdup (".");
+ if (!tmp)
+ return ENOMEM;
+ }
+ else
+ *p = 0;
+
+ if (access (tmp, W_OK))
+ {
+ /* Fallback to kernel locking */
+ free (tmp);
+ return mu_locker_set_flags (locker,
+ MU_LOCKER_KERNEL|MU_LOCKER_OPTIONS(locker->flags));
+ }
+
+ free (tmp);
+
+ locker->data.dot.dotlock = malloc (strlen (locker->file)
+ + sizeof (DOTLOCK_SUFFIX));
+
+ if (!locker->data.dot.dotlock)
+ return ENOMEM;
+ strcpy (locker->data.dot.dotlock, locker->file);
+ strcat (locker->data.dot.dotlock, DOTLOCK_SUFFIX);
+
+ return 0;
+}
+
+static void
+destroy_dotlock (mu_locker_t locker)
+{
+ free (locker->data.dot.dotlock);
+ free (locker->data.dot.nfslock);
+}
+
+#ifndef MAXHOSTNAMELEN
+# define MAXHOSTNAMELEN 256
+#endif
+
+static int
+lock_dotlock (mu_locker_t locker, enum mu_locker_mode mode)
+{
+ char host[MAXHOSTNAMELEN + 1] = "localhost";
+ char pid[11]; /* 10 is strlen(2^32 = 4294967296) */
+ char now[11];
+ size_t sz = 0;
+ int err = 0;
+ int fd;
+
+ if (locker->data.dot.nfslock)
+ {
+ unlink (locker->data.dot.nfslock);
+ free (locker->data.dot.nfslock);
+ locker->data.dot.nfslock = NULL;
+ }
+
+ expire_stale_lock (locker);
+
+ /* build the NFS hitching-post to the lock file */
+
+ gethostname (host, sizeof (host));
+ host[MAXHOSTNAMELEN] = 0;
+
+ snprintf (now, sizeof (now), "%lu", (unsigned long) time (0));
+ now[sizeof (now) - 1] = 0;
+
+ snprintf (pid, sizeof (pid), "%lu", (unsigned long) getpid ());
+ pid[sizeof (pid) - 1] = 0;
+
+ sz = strlen (locker->file) + 1 /* "." */
+ + strlen (pid) + 1 /* "." */
+ + strlen (now) + 1 /* "." */
+ + strlen (host) + 1;
+
+ locker->data.dot.nfslock = malloc (sz);
+
+ if (!locker->data.dot.nfslock)
+ return ENOMEM;
+
+ snprintf (locker->data.dot.nfslock, sz, "%s.%s.%s.%s",
+ locker->file, pid, now, host);
+
+ fd = open (locker->data.dot.nfslock,
+ O_WRONLY | O_CREAT | O_EXCL, LOCKFILE_ATTR);
+ if (fd == -1)
+ {
+ if (errno == EEXIST)
+ return EAGAIN;
+ else
+ return errno;
+ }
+ close (fd);
+
+ /* Try to link to the lockfile. */
+ if (link (locker->data.dot.nfslock, locker->data.dot.dotlock) == -1)
+ {
+ unlink (locker->data.dot.nfslock);
+ if (errno == EEXIST)
+ return MU_ERR_LOCK_CONFLICT;
+ return errno;
+ }
+
+ if ((fd = open (locker->data.dot.dotlock, O_RDWR)) == -1)
+ {
+ unlink (locker->data.dot.nfslock);
+ return errno;
+ }
+
+ err = stat_check (locker->data.dot.nfslock, fd, 2);
+ if (err)
+ {
+ unlink (locker->data.dot.nfslock);
+ if (err == EINVAL)
+ return MU_ERR_LOCK_BAD_LOCK;
+ return errno;
+ }
+
+ unlink (locker->data.dot.nfslock);
+
+ /* FIXME: If no errors, we have the lock. */
+ assert (locker->refcnt == 0);
+
+ if (locker->flags & MU_LOCKER_PID)
+ {
+ char buf[16];
+ sprintf (buf, "%ld", (long) getpid ());
+ write (fd, buf, strlen (buf));
+ }
+ close (fd);
+ return 0;
+}
+
+static int
+unlock_dotlock (mu_locker_t locker)
+{
+ if (unlink (locker->data.dot.dotlock) == -1)
+ {
+ int err = errno;
+ if (err == ENOENT)
+ {
+ locker->refcnt = 0; /*FIXME?*/
+ err = MU_ERR_LOCK_NOT_HELD;
+ return err;
+ }
+ return err;
+ }
+ return 0;
+}
+
+int
+mu_locker_touchlock (mu_locker_t lock)
+{
+ if (!lock)
+ return MU_ERR_LOCKER_NULL;
+
+ if (MU_LOCKER_TYPE (lock) != MU_LOCKER_TYPE_DOTLOCK)
+ return 0;
+
+ if (lock->refcnt > 0)
+ return utime (lock->data.dot.dotlock, NULL);
+
+ return MU_ERR_LOCK_NOT_HELD;
+}
+
+
+/* Kernel locking */
+static int
+init_kernel (mu_locker_t locker)
+{
+ return 0;
+}
+
+static int
+lock_kernel (mu_locker_t locker, enum mu_locker_mode mode)
+{
+ int fd;
+ struct flock fl;
+
+ switch (mode)
+ {
+ case mu_lck_shr:
+ case mu_lck_opt:
+ mode = O_RDONLY;
+ fl.l_type = F_RDLCK;
+ break;
+
+ case mu_lck_exc:
+ mode = O_RDWR;
+ fl.l_type = F_WRLCK;
+ break;
+
+ default:
+ return EINVAL;
+ }
+
+ fd = open (locker->file, O_RDWR);
+ if (fd == -1)
+ return errno;
+ locker->data.kernel.fd = fd;
+
+ fl.l_whence = SEEK_SET;
+ fl.l_start = 0;
+ fl.l_len = 0; /* Lock entire file */
+ if (fcntl (fd, F_SETLK, &fl))
+ {
+#ifdef EACCESS
+ if (errno == EACCESS)
+ return EAGAIN;
+#endif
+ if (errno == EAGAIN)
+ return EAGAIN;
+ return errno;
+ }
+ return 0;
+}
+
+static int
+unlock_kernel (mu_locker_t locker)
+{
+ struct flock fl;
+
+ fl.l_type = F_UNLCK;
+ fl.l_whence = SEEK_SET;
+ fl.l_start = 0;
+ fl.l_len = 0; /* Unlock entire file */
+ if (fcntl (locker->data.kernel.fd, F_SETLK, &fl))
+ {
+#ifdef EACCESS
+ if (errno == EACCESS)
+ return EAGAIN;
+#endif
+ if (errno == EAGAIN)
+ return EAGAIN;
+ return errno;
+ }
+ close (locker->data.kernel.fd);
+ return 0;
+}
+
+static int
+init_external (mu_locker_t locker)
+{
+ if (!(locker->data.external.name = strdup (mu_locker_external_program ?
+ mu_locker_external_program :
+ MU_LOCKER_EXTERNAL_PROGRAM)))
+ return ENOMEM;
+ return 0;
+}
+
+static void
+destroy_external (mu_locker_t locker)
+{
+ free (locker->data.external.name);
+}
+
+/*
+ Estimate 1 decimal digit per 3 bits, + 1 for round off.
+*/
+#define DEC_DIGS_PER_INT (sizeof(int) * 8 / 3 + 1)
+
+static int
+external_locker (mu_locker_t l, int lock)
+{
+ int err = 0;
+ char *av[6];
+ int ac = 0;
+ char aforce[3 + DEC_DIGS_PER_INT + 1];
+ char aretry[3 + DEC_DIGS_PER_INT + 1];
+ int status = 0;
+
+ assert (l);
+ assert (l->flags & MU_LOCKER_EXTERNAL);
+ /* FIXME */
+ assert (lock == !l->refcnt);
+ /* lock is true, refcnt is 0 or lock is false and refcnt is 1 */
+
+ av[ac++] = l->data.external.name ?
+ l->data.external.name : MU_LOCKER_EXTERNAL_PROGRAM;
+
+ if (l->flags & MU_LOCKER_TIME)
+ {
+ snprintf (aforce, sizeof (aforce), "-f%d", l->expire_time);
+ aforce[sizeof (aforce) - 1] = 0;
+ av[ac++] = aforce;
+ }
+
+ if (l->flags & MU_LOCKER_RETRY)
+ {
+ snprintf (aretry, sizeof (aretry), "-r%d", l->retries);
+ aretry[sizeof (aretry) - 1] = 0;
+ av[ac++] = aretry;
+ }
+
+ if (!lock)
+ av[ac++] = "-u";
+
+ av[ac++] = l->file;
+
+ av[ac++] = NULL;
+
+ if ((err = mu_spawnvp (av[0], av, &status)))
+ return err;
+
+ if (!WIFEXITED (status))
+ {
+ err = MU_ERR_LOCK_EXT_KILLED;
+ }
+ else
+ {
+ switch (WEXITSTATUS (status))
+ {
+ case 127:
+ err = MU_ERR_LOCK_EXT_FAIL;
+ break;
+
+ case MU_DL_EX_OK:
+ err = 0;
+ l->refcnt = lock;
+ break;
+
+ case MU_DL_EX_NEXIST:
+ err = MU_ERR_LOCK_NOT_HELD;
+ break;
+
+ case MU_DL_EX_EXIST:
+ err = MU_ERR_LOCK_CONFLICT;
+ break;
+
+ case MU_DL_EX_PERM:
+ err = EPERM;
+ break;
+
+ default:
+ case MU_DL_EX_ERROR:
+ err = MU_ERR_LOCK_EXT_ERR;
+ break;
+ }
+ }
+
+ return err;
+}
+
+static int
+lock_external (mu_locker_t locker, enum mu_locker_mode mode)
+{
+ return external_locker (locker, 1);
+}
+
+static int
+unlock_external (mu_locker_t locker)
+{
+ return external_locker (locker, 0);
+}
+
diff --git a/libmailutils/mailbox.c b/libmailutils/mailbox.c
new file mode 100644
index 000000000..e1c7b59f1
--- /dev/null
+++ b/libmailutils/mailbox.c
@@ -0,0 +1,786 @@
+/* GNU Mailutils -- a suite of utilities for electronic mail
+ Copyright (C) 1999, 2000, 2001, 2004, 2005, 2006, 2007, 2008, 2009,
+ 2010 Free Software Foundation, Inc.
+
+ This library is free software; you can redistribute it and/or
+ modify it under the terms of the GNU Lesser General Public
+ License as published by the Free Software Foundation; either
+ version 3 of the License, or (at your option) any later version.
+
+ This library 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
+ Lesser General Public License for more details.
+
+ You should have received a copy of the GNU Lesser General
+ Public License along with this library; if not, write to the
+ Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor,
+ Boston, MA 02110-1301 USA */
+
+#ifdef HAVE_CONFIG_H
+#include <config.h>
+#endif
+
+#include <stdlib.h>
+#include <errno.h>
+#include <string.h>
+
+#include <mailutils/debug.h>
+#include <mailutils/errno.h>
+#include <mailutils/error.h>
+#include <mailutils/folder.h>
+#include <mailutils/iterator.h>
+#include <mailutils/list.h>
+#include <mailutils/locker.h>
+#include <mailutils/observer.h>
+#include <mailutils/property.h>
+#include <mailutils/registrar.h>
+#include <mailutils/stream.h>
+#include <mailutils/url.h>
+#include <mailutils/attribute.h>
+#include <mailutils/message.h>
+#include <mailutils/mutil.h>
+
+#include <mailutils/sys/mailbox.h>
+#include <mailutils/sys/url.h>
+
+static int
+mailbox_folder_create (mu_mailbox_t mbox, const char *name,
+ mu_record_t record)
+{
+ int rc;
+ mu_url_t url;
+
+ if ((rc = mu_url_uplevel (mbox->url, &url)))
+ {
+ if (rc == MU_ERR_NOENT)
+ {
+ rc = mu_url_dup (mbox->url, &url);
+ if (rc)
+ return rc;
+ }
+ else
+ return rc;
+ }
+
+ rc = mu_folder_create_from_record (&mbox->folder, url, record);
+ if (rc)
+ mu_url_destroy (&url);
+ return rc;
+}
+
+int
+_mailbox_create_from_record (mu_mailbox_t *pmbox,
+ mu_record_t record,
+ mu_url_t url,
+ const char *name)
+{
+ mu_log_level_t level;
+ int (*m_init) (mu_mailbox_t) = NULL;
+
+ mu_record_get_mailbox (record, &m_init);
+ if (m_init)
+ {
+ int status;
+ int (*u_init) (mu_url_t) = NULL;
+ mu_mailbox_t mbox;
+
+ /* Allocate memory for mbox. */
+ mbox = calloc (1, sizeof (*mbox));
+ if (mbox == NULL)
+ return ENOMEM;
+
+ /* Initialize the internal lock now, so the concrete mailbox
+ could use it. */
+ status = mu_monitor_create (&mbox->monitor, 0, mbox);
+ if (status != 0)
+ {
+ mu_mailbox_destroy (&mbox);
+ return status;
+ }
+
+ /* Make sure scheme contains actual mailbox scheme */
+ /* FIXME: It is appropriate not for all record types. For now we
+ assume that if the record scheme ends with a plus sign, this
+ should not be done. Probably it requires some flag in struct
+ _mu_record? */
+ if (strcmp (url->scheme, record->scheme))
+ {
+ char *p = strdup (record->scheme);
+ if (!p)
+ {
+ mu_mailbox_destroy (&mbox);
+ return errno;
+ }
+ free (url->scheme);
+ url->scheme = p;
+ }
+
+ mu_record_get_url (record, &u_init);
+ if (u_init && (status = u_init (url)) != 0)
+ {
+ mu_mailbox_destroy (&mbox);
+ return status;
+ }
+
+ mbox->url = url;
+
+ /* Create the folder before initializing the concrete mailbox.
+ The mailbox needs it's back pointer. */
+ status = mailbox_folder_create (mbox, name, record);
+
+ if (status == 0)
+ status = m_init (mbox); /* Create the concrete mailbox type. */
+
+ if (status != 0)
+ {
+ /* Take care not to destroy url. Leave it to caller. */
+ mbox->url = NULL;
+ mu_mailbox_destroy (&mbox);
+ }
+ else
+ {
+ *pmbox = mbox;
+
+ level = mu_global_debug_level ("mailbox");
+ if (level)
+ {
+ int status = mu_debug_create (&mbox->debug, mbox);
+ if (status)
+ return 0; /* FIXME: don't want to bail out just because I
+ failed to create a *debug* object. But I may
+ be wrong... */
+ mu_debug_set_level (mbox->debug, level);
+ if (level & MU_DEBUG_INHERIT)
+ mu_folder_set_debug (mbox->folder, mbox->debug);
+ }
+ }
+
+ return status;
+ }
+ return MU_ERR_NO_HANDLER;
+}
+
+static int
+_create_mailbox0 (mu_mailbox_t *pmbox, mu_url_t url, const char *name)
+{
+ mu_record_t record = NULL;
+
+ if (mu_registrar_lookup_url (url, MU_FOLDER_ATTRIBUTE_FILE, &record, NULL)
+ == 0)
+ return _mailbox_create_from_record (pmbox, record, url, name);
+ return MU_ERR_NO_HANDLER;
+}
+
+static int
+_create_mailbox (mu_mailbox_t *pmbox, const char *name)
+{
+ int status;
+ mu_url_t url;
+
+ status = mu_url_create (&url, name);
+ if (status)
+ return status;
+ status = mu_url_parse (url);
+ if (status == 0)
+ status = _create_mailbox0 (pmbox, url, name);
+ if (status)
+ mu_url_destroy (&url);
+ return status;
+}
+
+/* The Mailbox Factory.
+ Create an iterator for registrar and see if any url scheme match,
+ Then we call the mailbox's mu_url_create() to parse the URL. Last
+ initialize the concrete mailbox and folder. */
+int
+mu_mailbox_create (mu_mailbox_t *pmbox, const char *name)
+{
+ if (pmbox == NULL)
+ return MU_ERR_OUT_PTR_NULL;
+
+ return _create_mailbox (pmbox, name);
+}
+
+int
+mu_mailbox_create_from_url (mu_mailbox_t *pmbox, mu_url_t url)
+{
+ if (pmbox == NULL)
+ return MU_ERR_OUT_PTR_NULL;
+ return _create_mailbox0 (pmbox, url, mu_url_to_string (url));
+}
+
+int
+mu_mailbox_create_from_record (mu_mailbox_t *pmbox, mu_record_t record,
+ const char *name)
+{
+ mu_url_t url;
+ int rc;
+
+ rc = mu_url_create (&url, name);
+ if (rc)
+ return rc;
+ rc = mu_url_parse (url);
+ if (rc == 0)
+ rc = _mailbox_create_from_record (pmbox, record, url, name);
+ if (rc)
+ mu_url_destroy (&url);
+ return rc;
+}
+
+void
+mu_mailbox_destroy (mu_mailbox_t *pmbox)
+{
+ if (pmbox && *pmbox)
+ {
+ mu_mailbox_t mbox = *pmbox;
+ mu_monitor_t monitor = mbox->monitor;
+
+ /* Notify the observers. */
+ if (mbox->observable)
+ {
+ mu_observable_notify (mbox->observable, MU_EVT_MAILBOX_DESTROY,
+ mbox);
+ mu_observable_destroy (&mbox->observable, mbox);
+ }
+
+ /* Call the concrete mailbox _destroy method. So it can clean itself. */
+ if (mbox->_destroy)
+ mbox->_destroy (mbox);
+
+ mu_monitor_wrlock (monitor);
+
+ /* Close the stream and nuke it */
+ if (mbox->stream)
+ {
+ /* FIXME: Is this right, should the client be responsible
+ for closing the stream? */
+ /* mu_stream_close (mbox->stream); */
+ mu_stream_destroy (&mbox->stream);
+ }
+
+ if (mbox->url)
+ mu_url_destroy (&mbox->url);
+
+ if (mbox->locker)
+ mu_locker_destroy (&mbox->locker);
+
+ if (mbox->debug)
+ mu_debug_destroy (&mbox->debug, mbox);
+
+ if (mbox->folder)
+ mu_folder_destroy (&mbox->folder);
+
+ if (mbox->property)
+ mu_property_destroy (&mbox->property, mbox);
+
+ free (mbox);
+ *pmbox = NULL;
+ mu_monitor_unlock (monitor);
+ mu_monitor_destroy (&monitor, mbox);
+ }
+}
+
+
+/* -------------- stub functions ------------------- */
+
+int
+mu_mailbox_open (mu_mailbox_t mbox, int flag)
+{
+ if (mbox == NULL || mbox->_open == NULL)
+ return MU_ERR_EMPTY_VFN;
+ if (flag & MU_STREAM_QACCESS)
+ {
+ /* Quick access mailboxes are read-only */
+ if (flag & (MU_STREAM_WRITE | MU_STREAM_RDWR
+ | MU_STREAM_APPEND | MU_STREAM_CREAT))
+ return EINVAL; /* FIXME: Better error code, please? */
+ }
+ return mbox->_open (mbox, flag);
+}
+
+int
+mu_mailbox_close (mu_mailbox_t mbox)
+{
+ if (mbox == NULL || mbox->_close == NULL)
+ return MU_ERR_EMPTY_VFN;
+
+ return mbox->_close (mbox);
+}
+
+int
+mu_mailbox_flush (mu_mailbox_t mbox, int expunge)
+{
+ size_t i, total = 0;
+ int status = 0;
+
+ if (!mbox)
+ return EINVAL;
+ if (!(mbox->flags & (MU_STREAM_RDWR|MU_STREAM_WRITE|MU_STREAM_APPEND)))
+ return 0;
+
+ mu_mailbox_messages_count (mbox, &total);
+ if (!(mbox->flags & MU_STREAM_APPEND))
+ for (i = 1; i <= total; i++)
+ {
+ mu_message_t msg = NULL;
+ mu_attribute_t attr = NULL;
+ mu_mailbox_get_message (mbox, i, &msg);
+ mu_message_get_attribute (msg, &attr);
+ mu_attribute_set_seen (attr);
+ }
+
+ if (expunge)
+ status = mu_mailbox_expunge (mbox);
+ else
+ status = mu_mailbox_sync (mbox);
+
+ return status;
+}
+
+/* messages */
+int
+mu_mailbox_append_message (mu_mailbox_t mbox, mu_message_t msg)
+{
+ if (mbox == NULL || mbox->_append_message == NULL)
+ return MU_ERR_EMPTY_VFN;
+ if (!(mbox->flags & (MU_STREAM_RDWR|MU_STREAM_WRITE|MU_STREAM_APPEND)))
+ return EACCES;
+ return mbox->_append_message (mbox, msg);
+}
+
+int
+mu_mailbox_get_message (mu_mailbox_t mbox, size_t msgno, mu_message_t *pmsg)
+{
+ if (mbox == NULL || mbox->_get_message == NULL)
+ return MU_ERR_EMPTY_VFN;
+ if (mbox->flags & MU_STREAM_QACCESS)
+ return MU_ERR_BADOP;
+ return mbox->_get_message (mbox, msgno, pmsg);
+}
+
+int
+mu_mailbox_quick_get_message (mu_mailbox_t mbox, mu_message_qid_t qid,
+ mu_message_t *pmsg)
+{
+ if (mbox == NULL || mbox->_quick_get_message == NULL)
+ return MU_ERR_EMPTY_VFN;
+ if (!(mbox->flags & MU_STREAM_QACCESS))
+ return MU_ERR_BADOP;
+ return mbox->_quick_get_message (mbox, qid, pmsg);
+}
+
+int
+mu_mailbox_messages_count (mu_mailbox_t mbox, size_t *num)
+{
+ if (mbox == NULL || mbox->_messages_count == NULL)
+ return MU_ERR_EMPTY_VFN;
+ if (mbox->flags & MU_STREAM_QACCESS)
+ return MU_ERR_BADOP;
+ return mbox->_messages_count (mbox, num);
+}
+
+int
+mu_mailbox_messages_recent (mu_mailbox_t mbox, size_t *num)
+{
+ if (mbox == NULL || mbox->_messages_recent == NULL)
+ return MU_ERR_EMPTY_VFN;
+ if (mbox->flags & MU_STREAM_QACCESS)
+ return MU_ERR_BADOP;
+ return mbox->_messages_recent (mbox, num);
+}
+
+int
+mu_mailbox_message_unseen (mu_mailbox_t mbox, size_t *num)
+{
+ if (mbox == NULL || mbox->_message_unseen == NULL)
+ return MU_ERR_EMPTY_VFN;
+ if (mbox->flags & MU_STREAM_QACCESS)
+ return MU_ERR_BADOP;
+ return mbox->_message_unseen (mbox, num);
+}
+
+int
+mu_mailbox_sync (mu_mailbox_t mbox)
+{
+ if (mbox == NULL || mbox->_sync == NULL)
+ return MU_ERR_EMPTY_VFN;
+ if (!(mbox->flags & (MU_STREAM_RDWR|MU_STREAM_WRITE|MU_STREAM_APPEND)))
+ return 0;
+ return mbox->_sync (mbox);
+}
+
+/* Historic alias: */
+int
+mu_mailbox_save_attributes (mu_mailbox_t mbox)
+{
+ if (mbox == NULL || mbox->_sync == NULL)
+ return MU_ERR_EMPTY_VFN;
+ if (!(mbox->flags & (MU_STREAM_RDWR|MU_STREAM_WRITE|MU_STREAM_APPEND)))
+ return EACCES;
+ return mbox->_sync (mbox);
+}
+
+int
+mu_mailbox_expunge (mu_mailbox_t mbox)
+{
+ if (mbox == NULL || mbox->_expunge == NULL)
+ return MU_ERR_EMPTY_VFN;
+ if (!(mbox->flags & (MU_STREAM_RDWR|MU_STREAM_WRITE|MU_STREAM_APPEND)))
+ return EACCES;
+ return mbox->_expunge (mbox);
+}
+
+int
+mu_mailbox_is_updated (mu_mailbox_t mbox)
+{
+ if (mbox == NULL || mbox->_is_updated == NULL)
+ return 1;
+ if (mbox->flags & MU_STREAM_QACCESS)
+ return 1;
+ return mbox->_is_updated (mbox);
+}
+
+int
+mu_mailbox_scan (mu_mailbox_t mbox, size_t msgno, size_t *pcount)
+{
+ if (mbox == NULL || mbox->_scan == NULL)
+ return MU_ERR_EMPTY_VFN;
+ if (mbox->flags & MU_STREAM_QACCESS)
+ return MU_ERR_BADOP;
+ return mbox->_scan (mbox, msgno, pcount);
+}
+
+int
+mu_mailbox_get_size (mu_mailbox_t mbox, mu_off_t *psize)
+{
+ int status;
+ if (mbox == NULL)
+ return MU_ERR_EMPTY_VFN;
+ if (mbox->flags & MU_STREAM_QACCESS)
+ return MU_ERR_BADOP;
+ if (mbox->_get_size == NULL
+ || (status = mbox->_get_size (mbox, psize)) == ENOSYS)
+ {
+ /* Fall back to brute-force method */
+ size_t i, total;
+ mu_off_t size = 0;
+
+ status = mu_mailbox_messages_count (mbox, &total);
+ if (status)
+ return status;
+ for (i = 1; i <= total; i++)
+ {
+ mu_message_t msg;
+ size_t msgsize;
+ status = mu_mailbox_get_message (mbox, i, &msg);
+ if (status)
+ return status;
+ status = mu_message_size (msg, &msgsize);
+ if (status)
+ return status;
+ size += msgsize;
+ }
+ *psize = size;
+ }
+ return status;
+}
+
+int
+mu_mailbox_uidvalidity (mu_mailbox_t mbox, unsigned long *pvalid)
+{
+ if (mbox == NULL || mbox->_uidvalidity == NULL)
+ return MU_ERR_EMPTY_VFN;
+ if (mbox->flags & MU_STREAM_QACCESS)
+ return MU_ERR_BADOP;
+ return mbox->_uidvalidity (mbox, pvalid);
+}
+
+int
+mu_mailbox_uidnext (mu_mailbox_t mbox, size_t *puidnext)
+{
+ if (mbox == NULL || mbox->_uidnext == NULL)
+ return MU_ERR_EMPTY_VFN;
+ if (mbox->flags & MU_STREAM_QACCESS)
+ return MU_ERR_BADOP;
+ return mbox->_uidnext (mbox, puidnext);
+}
+
+/* locking */
+int
+mu_mailbox_set_locker (mu_mailbox_t mbox, mu_locker_t locker)
+{
+ if (mbox == NULL)
+ return MU_ERR_MBX_NULL;
+ if (mbox->locker)
+ mu_locker_destroy (&mbox->locker);
+ mbox->locker = locker;
+ return 0;
+}
+
+int
+mu_mailbox_get_locker (mu_mailbox_t mbox, mu_locker_t *plocker)
+{
+ if (mbox == NULL)
+ return MU_ERR_MBX_NULL;
+ if (plocker == NULL)
+ return MU_ERR_OUT_PTR_NULL;
+ *plocker = mbox->locker;
+ return 0;
+}
+
+int
+mu_mailbox_get_flags (mu_mailbox_t mbox, int *flags)
+{
+ if (mbox == NULL)
+ return MU_ERR_MBX_NULL;
+ if (!*flags)
+ return MU_ERR_OUT_NULL;
+ *flags = mbox->flags;
+ return 0;
+}
+
+int
+mu_mailbox_set_stream (mu_mailbox_t mbox, mu_stream_t stream)
+{
+ if (mbox == NULL)
+ return MU_ERR_MBX_NULL;
+ if (mbox->flags & MU_STREAM_QACCESS)
+ return MU_ERR_BADOP;
+ if (mbox->stream)
+ mu_stream_destroy (&mbox->stream);
+ mbox->stream = stream;
+ return 0;
+}
+
+/* FIXME: This is a problem. We provide a mu_mailbox_get_stream ()
+ and this stream is special it should, in theory, represent
+ a "view" of a flow of messages. But providing this perspective
+ may make sense for local mailboxes but downright impossible
+ for a remote mailbox, short on downloading the entire mailbox
+ locally.
+ The question is : should this function be removed?
+ So far it as been used on local mailboxes to get offsets. */
+int
+mu_mailbox_get_stream (mu_mailbox_t mbox, mu_stream_t *pstream)
+{
+ /* FIXME: Deprecation warning */
+ if (mbox == NULL)
+ return MU_ERR_MBX_NULL;
+ if (pstream == NULL)
+ return MU_ERR_OUT_PTR_NULL;
+
+ /* If null two cases:
+ - it is no open yet.
+ - it a remote stream and the socket stream is on the folder. */
+ if (mbox->stream == NULL)
+ {
+ if (mbox->folder)
+ return mu_folder_get_stream (mbox->folder, pstream);
+ }
+ *pstream = mbox->stream;
+ return 0;
+}
+
+int
+mu_mailbox_get_streamref (mu_mailbox_t mbox, mu_stream_t *pstream)
+{
+ if (mbox == NULL)
+ return MU_ERR_MBX_NULL;
+ if (pstream == NULL)
+ return MU_ERR_OUT_PTR_NULL;
+
+ /* If null two cases:
+ - it is no open yet.
+ - it a remote stream and the socket stream is on the folder. */
+ if (mbox->stream == NULL)
+ {
+ if (mbox->folder)
+ return mu_folder_get_streamref (mbox->folder, pstream);
+ }
+ return mu_streamref_create (pstream, mbox->stream);
+}
+
+int
+mu_mailbox_get_observable (mu_mailbox_t mbox, mu_observable_t *pobservable)
+{
+ if (mbox == NULL)
+ return MU_ERR_MBX_NULL;
+ if (pobservable == NULL)
+ return MU_ERR_OUT_PTR_NULL;
+
+ if (mbox->observable == NULL)
+ {
+ int status = mu_observable_create (&mbox->observable, mbox);
+ if (status != 0)
+ return status;
+ }
+ *pobservable = mbox->observable;
+ return 0;
+}
+
+int
+mu_mailbox_get_property (mu_mailbox_t mbox, mu_property_t *pproperty)
+{
+ if (mbox == NULL)
+ return MU_ERR_MBX_NULL;
+ if (pproperty == NULL)
+ return MU_ERR_OUT_PTR_NULL;
+
+ if (mbox->property == NULL)
+ {
+ int status = mu_property_create (&mbox->property, mbox);
+ if (status != 0)
+ return status;
+ }
+ *pproperty = mbox->property;
+ return 0;
+}
+
+int
+mu_mailbox_has_debug (mu_mailbox_t mailbox)
+{
+ if (mailbox == NULL)
+ return 0;
+
+ return mailbox->debug ? 1 : 0;
+}
+
+int
+mu_mailbox_set_debug (mu_mailbox_t mbox, mu_debug_t debug)
+{
+ if (mbox == NULL)
+ return MU_ERR_MBX_NULL;
+ if (mbox->debug)
+ mu_debug_destroy (&mbox->debug, mbox);
+ mbox->debug = debug;
+ /* FIXME: Honor MU_DEBUG_INHERIT */
+ if (!mu_folder_has_debug (mbox->folder))
+ mu_folder_set_debug (mbox->folder, debug);
+ return 0;
+}
+
+int
+mu_mailbox_get_debug (mu_mailbox_t mbox, mu_debug_t *pdebug)
+{
+ if (mbox == NULL)
+ return MU_ERR_MBX_NULL;
+ if (pdebug == NULL)
+ return MU_ERR_OUT_PTR_NULL;
+ if (mbox->debug == NULL)
+ {
+ int status = mu_debug_create (&mbox->debug, mbox);
+ if (status != 0)
+ return status;
+ /* FIXME: MU_DEBUG_INHERIT?? */
+ if (!mu_folder_has_debug (mbox->folder))
+ mu_folder_set_debug (mbox->folder, mbox->debug);
+ }
+ *pdebug = mbox->debug;
+ return 0;
+}
+
+int
+mu_mailbox_get_url (mu_mailbox_t mbox, mu_url_t *purl)
+{
+ if (mbox == NULL)
+ return MU_ERR_MBX_NULL;
+ if (purl == NULL)
+ return MU_ERR_OUT_PTR_NULL;
+ *purl = mbox->url;
+ return 0;
+}
+
+int
+mu_mailbox_get_folder (mu_mailbox_t mbox, mu_folder_t *pfolder)
+{
+ if (mbox == NULL)
+ return MU_ERR_MBX_NULL;
+ if (pfolder == NULL)
+ return MU_ERR_OUT_PTR_NULL;
+ *pfolder = mbox->folder;
+ return 0;
+}
+
+int
+mu_mailbox_set_folder (mu_mailbox_t mbox, mu_folder_t folder)
+{
+ if (mbox == NULL)
+ return MU_ERR_MBX_NULL;
+ mbox->folder = folder;
+ return 0;
+}
+
+int
+mu_mailbox_lock (mu_mailbox_t mbox)
+{
+ mu_locker_t lock = NULL;
+ mu_mailbox_get_locker (mbox, &lock);
+ return mu_locker_lock (lock);
+}
+
+int
+mu_mailbox_unlock (mu_mailbox_t mbox)
+{
+ mu_locker_t lock = NULL;
+ mu_mailbox_get_locker (mbox, &lock);
+ return mu_locker_unlock (lock);
+}
+
+int
+mu_mailbox_get_uidls (mu_mailbox_t mbox, mu_list_t *plist)
+{
+ mu_list_t list;
+ int status;
+
+ if (mbox == NULL)
+ return MU_ERR_MBX_NULL;
+ if (plist == NULL)
+ return EINVAL;
+ status = mu_list_create (&list);
+ if (status)
+ return status;
+ mu_list_set_destroy_item (list, mu_list_free_item);
+ if (mbox->_get_uidls)
+ status = mbox->_get_uidls (mbox, list);
+ else
+ {