author | Sergey Poznyakoff <gray@gnu.org.ua> | 2010-09-14 11:59:17 (GMT) |
---|---|---|
committer | Sergey Poznyakoff <gray@gnu.org.ua> | 2010-09-14 12:01:52 (GMT) |
commit | 7b79a6edcda94d4224bc4cbbe47cb6dcf2ea42db (patch) (side-by-side diff) | |
tree | dfbf43e0d6663b49c3ea3db93d15a2d2e793df10 /libmailutils | |
parent | d1d548e1473e5e87e42df174110d57a05b9402d1 (diff) | |
download | mailutils-7b79a6edcda94d4224bc4cbbe47cb6dcf2ea42db.tar.gz mailutils-7b79a6edcda94d4224bc4cbbe47cb6dcf2ea42db.tar.bz2 |
Rename mailbox/ to libmailutils/.
This change has been waiting since 2005-08-16 (aaab88142c8193f1),
when libmailbox had been renamed to libmailutils. Back then MU
was still under CVS, which didn't like renames.
141 files changed, 47188 insertions, 0 deletions
diff --git a/libmailutils/.gitignore b/libmailutils/.gitignore new file mode 100644 index 0000000..8ce64ea --- a/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 0000000..f28b1bd --- a/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 0000000..a3e3764 --- a/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 0000000..cd151c1 --- a/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 0000000..05801bb --- a/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 0000000..3cf3dde --- a/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 0000000..636b9af --- a/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], "e); + + 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 0000000..f8ef543 --- a/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 0000000..c563bcb --- a/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 0000000..a0c13a0 --- a/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 0000000..6bed79f --- a/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 0000000..3e76855 --- a/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 0000000..2d3e974 --- a/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 0000000..7a8c622 --- a/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 0000000..d3de518 --- a/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 0000000..513ad01 --- a/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 0000000..f4d56f7 --- a/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 (§ion_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 (§->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 0000000..be37bae --- a/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, "e); + 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 0000000..998d84d --- a/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 0000000..8b13ae1 --- a/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 0000000..eed4519 --- a/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 0000000..9dc147c --- a/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 0000000..4baeae2 --- a/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 0000000..98afb5f --- a/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 0000000..965977d --- a/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 0000000..b7fca61 --- a/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 0000000..d667e34 --- a/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 0000000..9a2e164 --- a/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 0000000..b03f299 --- a/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 0000000..f906cd3 --- a/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 0000000..7570f23 --- a/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 0000000..7497c82 --- a/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 0000000..a0d43fa --- a/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 0000000..1e5aa4a --- a/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 0000000..06eb98b --- a/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 0000000..99ec9da --- a/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 0000000..0df1254 --- a/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 0000000..8fdc25f --- a/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 0000000..88c397d --- a/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 0000000..4359e03 --- a/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 0000000..3ce430a --- a/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 0000000..c268ea4 --- a/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 0000000..3d1a54c --- a/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 0000000..40bde9d --- a/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 0000000..595735f --- a/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 0000000..2861c67 --- a/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 0000000..1a7da7e --- a/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 0000000..e3efc62 --- a/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 0000000..3897925 --- a/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 0000000..9135f9f --- a/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 0000000..e3817de --- a/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 0000000..91f6a6e --- a/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 0000000..94172ac --- a/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 0000000..b6bdc08 --- a/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 0000000..f752c16 --- a/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 0000000..e1c7b59 --- a/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; |