jaromail

a commandline tool to easily and privately handle your e-mail
git clone git://parazyd.org/jaromail.git
Log | Files | Refs | Submodules | README

commit b4e2769abbf5282a8672c86ba10c5edd87a4b7ee
parent 11889e16cc8279152d6623806b9b3a35cb137fbd
Author: Jaromil <jaromil@dyne.org>
Date:   Mon, 14 May 2012 22:08:30 +0200

fetchadds and dotlock files for lbdb

Diffstat:
Ashare/lbdb/dotlock.c | 538+++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++
Ashare/lbdb/fetchaddr.c | 226+++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++
Ashare/lbdb/helpers.c | 122+++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++
Ashare/lbdb/rfc2047.c | 268+++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++
Ashare/lbdb/rfc822.c | 768+++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++
5 files changed, 1922 insertions(+), 0 deletions(-)

diff --git a/share/lbdb/dotlock.c b/share/lbdb/dotlock.c @@ -0,0 +1,538 @@ +/* + * Copyright (C) 1996-2000 Michael R. Elkins <me@mutt.org> + * Copyright (C) 1998-2000 Thomas Roessler <roessler@does-not-exist.org> + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU 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 St, Fifth Floor, Boston, MA 02110-1301,, USA. + */ + +/* + * Completely stolen from the Mutt mailreader + */ + +#include <stdio.h> +#include <stdlib.h> +#include <string.h> + +#include <unistd.h> +#include <dirent.h> +#include <sys/file.h> +#include <sys/stat.h> +#include <sys/utsname.h> +#include <errno.h> +#include <time.h> +#include <fcntl.h> +#include <limits.h> + +#ifndef _POSIX_PATH_MAX +#include <posix1_lim.h> +#endif + +#include "dotlock.h" +#include "helpers.h" + +#ifdef HAVE_GETOPT_H +#include <getopt.h> +#endif + +#define MAXLINKS 1024 /* maximum link depth */ + +#define LONG_STRING 1024 +#define MAXLOCKATTEMPT 5 + +static int DotlockFlags; +static int Retry = MAXLOCKATTEMPT; + +static char *Hostname; + +static int dotlock_deference_symlink (char *, size_t, const char *); +static int dotlock_prepare (char *, size_t, const char *, int fd); +static int dotlock_check_stats (struct stat *, struct stat *); +static int dotlock_dispatch (const char *, int fd); + +static void usage (const char *); + +static void dotlock_expand_link (char *, const char *, const char *); + +/* These functions work on the current directory. + * Invoke dotlock_prepare () before and check their + * return value. + */ + +static int dotlock_try (void); +static int dotlock_unlock (const char *); +static int dotlock_unlink (const char *); +static int dotlock_lock (const char *); + + +#define check_flags(a) if (a & DL_FL_ACTIONS) usage (argv[0]) + +int main (int argc, char **argv) +{ + int i; + char *p; + struct utsname utsname; + + /* determine the system's host name */ + + uname (&utsname); + if (!(Hostname = strdup (utsname.nodename))) + return DL_EX_ERROR; + if ((p = strchr (Hostname, '.'))) + *p = '\0'; + + + /* parse the command line options. */ + DotlockFlags = 0; + + while ((i = getopt (argc, argv, "dtfur:")) != EOF) + { + switch (i) + { + /* actions, mutually exclusive */ + case 't': check_flags (DotlockFlags); DotlockFlags |= DL_FL_TRY; break; + case 'd': check_flags (DotlockFlags); DotlockFlags |= DL_FL_UNLINK; break; + case 'u': check_flags (DotlockFlags); DotlockFlags |= DL_FL_UNLOCK; break; + + /* other flags */ + case 'f': DotlockFlags |= DL_FL_FORCE; break; + case 'r': DotlockFlags |= DL_FL_RETRY; Retry = atoi (optarg); break; + + default: usage (argv[0]); + } + } + + if (optind == argc || Retry < 0) + usage (argv[0]); + + return dotlock_dispatch (argv[optind], -1); +} + + +static int dotlock_dispatch (const char *f, int fd) +{ + char realpath[_POSIX_PATH_MAX]; + + /* If dotlock_prepare () succeeds [return value == 0], + * realpath contains the basename of f, and we have + * successfully changed our working directory to + * `dirname $f`. Additionally, f has been opened for + * reading to verify that the user has at least read + * permissions on that file. + * + * For a more detailed explanation of all this, see the + * lengthy comment below. + */ + + if (dotlock_prepare (realpath, sizeof (realpath), f, fd) != 0) + return DL_EX_ERROR; + + /* Actually perform the locking operation. */ + + if (DotlockFlags & DL_FL_TRY) + return dotlock_try (); + else if (DotlockFlags & DL_FL_UNLOCK) + return dotlock_unlock (realpath); + else if (DotlockFlags & DL_FL_UNLINK) + return dotlock_unlink (realpath); + else /* lock */ + return dotlock_lock (realpath); +} + + +/* + * Usage information. + * + * This function doesn't return. + * + */ + +static void +usage (const char *av0) +{ + fprintf (stderr, "usage: %s [-t|-f|-u|-d] [-r <retries>] file\n", av0); + + fputs ("\noptions:" + "\n -t\t\ttry" + "\n -f\t\tforce" + "\n -u\t\tunlock" + "\n -d\t\tunlink" + "\n -r <retries>\tRetry locking" + "\n", stderr); + + exit (DL_EX_ERROR); +} + + +/* + * Access checking: Let's avoid to lock other users' mail + * spool files if we aren't permitted to read them. + * + * Some simple-minded access (2) checking isn't sufficient + * here: The problem is that the user may give us a + * deeply nested path to a file which has the same name + * as the file he wants to lock, but different + * permissions, say, e.g. + * /tmp/lots/of/subdirs/var/spool/mail/root. + * + * He may then try to replace /tmp/lots/of/subdirs by a + * symbolic link to / after we have invoked access () to + * check the file's permissions. The lockfile we'd + * create or remove would then actually be + * /var/spool/mail/root. + * + * To avoid this attack, we proceed as follows: + * + * - First, follow symbolic links a la + * dotlock_deference_symlink (). + * + * - get the result's dirname. + * + * - chdir to this directory. If you can't, bail out. + * + * - try to open the file in question, only using its + * basename. If you can't, bail out. + * + * - fstat that file and compare the result to a + * subsequent lstat (only using the basename). If + * the comparison fails, bail out. + * + * dotlock_prepare () is invoked from main () directly + * after the command line parsing has been done. + * + * Return values: + * + * 0 - Evereything's fine. The program's new current + * directory is the contains the file to be locked. + * The string pointed to by bn contains the name of + * the file to be locked. + * + * -1 - Something failed. Don't continue. + * + * tlr, Jul 15 1998 + */ + +static int +dotlock_check_stats (struct stat *fsb, struct stat *lsb) +{ + /* S_ISLNK (fsb->st_mode) should actually be impossible, + * but we may have mixed up the parameters somewhere. + * play safe. + */ + + if (S_ISLNK (lsb->st_mode) || S_ISLNK (fsb->st_mode)) + return -1; + + if ((lsb->st_dev != fsb->st_dev) || + (lsb->st_ino != fsb->st_ino) || + (lsb->st_mode != fsb->st_mode) || + (lsb->st_nlink != fsb->st_nlink) || + (lsb->st_uid != fsb->st_uid) || + (lsb->st_gid != fsb->st_gid) || + (lsb->st_rdev != fsb->st_rdev) || + (lsb->st_size != fsb->st_size)) + { + /* something's fishy */ + return -1; + } + + return 0; +} + +static int +dotlock_prepare (char *bn, size_t l, const char *f, int _fd) +{ + struct stat fsb, lsb; + char realpath[_POSIX_PATH_MAX]; + char *basename, *dirname; + char *p; + int fd; + int r; + + if (dotlock_deference_symlink (realpath, sizeof (realpath), f) == -1) + return -1; + + if ((p = strrchr (realpath, '/'))) + { + *p = '\0'; + basename = p + 1; + dirname = realpath; + } + else + { + basename = realpath; + dirname = "."; + } + + if (strlen (basename) + 1 > l) + return -1; + + strfcpy (bn, basename, l); + + if (chdir (dirname) == -1) + return -1; + + if (_fd != -1) + fd = _fd; + else if ((fd = open (basename, O_RDONLY)) == -1) + return -1; + + r = fstat (fd, &fsb); + + if (_fd == -1) + close (fd); + + if (r == -1) + return -1; + + if (lstat (basename, &lsb) == -1) + return -1; + + if (dotlock_check_stats (&fsb, &lsb) == -1) + return -1; + + return 0; +} + +/* + * Expand a symbolic link. + * + * This function expects newpath to have space for + * at least _POSIX_PATH_MAX characters. + * + */ + +static void +dotlock_expand_link (char *newpath, const char *path, const char *link) +{ + const char *lb = NULL; + size_t len; + + /* link is full path */ + if (*link == '/') + { + strfcpy (newpath, link, _POSIX_PATH_MAX); + return; + } + + if ((lb = strrchr (path, '/')) == NULL) + { + /* no path in link */ + strfcpy (newpath, link, _POSIX_PATH_MAX); + return; + } + + len = lb - path + 1; + memcpy (newpath, path, len); + strfcpy (newpath + len, link, _POSIX_PATH_MAX - len); +} + + +/* + * Deference a chain of symbolic links + * + * The final path is written to d. + * + */ + +static int +dotlock_deference_symlink (char *d, size_t l, const char *path) +{ + struct stat sb; + char realpath[_POSIX_PATH_MAX]; + const char *pathptr = path; + int count = 0; + + while (count++ < MAXLINKS) + { + if (lstat (pathptr, &sb) == -1) + { + /* perror (pathptr); */ + return -1; + } + + if (S_ISLNK (sb.st_mode)) + { + char linkfile[_POSIX_PATH_MAX]; + char linkpath[_POSIX_PATH_MAX]; + int len; + + if ((len = readlink (pathptr, linkfile, sizeof (linkfile))) == -1) + { + /* perror (pathptr); */ + return -1; + } + + linkfile[len] = '\0'; + dotlock_expand_link (linkpath, pathptr, linkfile); + strfcpy (realpath, linkpath, sizeof (realpath)); + pathptr = realpath; + } + else + break; + } + + strfcpy (d, pathptr, l); + return 0; +} + +/* + * Dotlock a file. + * + * realpath is assumed _not_ to be an absolute path to + * the file we are about to lock. Invoke + * dotlock_prepare () before using this function! + * + */ + +#define HARDMAXATTEMPTS 10 + +static int +dotlock_lock (const char *realpath) +{ + char lockfile[_POSIX_PATH_MAX + LONG_STRING]; + char nfslockfile[_POSIX_PATH_MAX + LONG_STRING]; + size_t prev_size = 0; + int fd; + int count = 0; + int hard_count = 0; + struct stat sb; + time_t t; + + sprintf (nfslockfile, "%s.%s.%d", realpath, Hostname, (int) getpid ()); + sprintf (lockfile, "%s.lock", realpath); + + + unlink (nfslockfile); + + while ((fd = open (nfslockfile, O_WRONLY | O_EXCL | O_CREAT, 0)) < 0) + { + if (errno != EAGAIN) + { + /* perror ("cannot open NFS lock file"); */ + return DL_EX_ERROR; + } + } + + + + close (fd); + + while (hard_count++ < HARDMAXATTEMPTS) + { + link (nfslockfile, lockfile); + + if (stat (nfslockfile, &sb) != 0) + { + /* perror ("stat"); */ + return DL_EX_ERROR; + } + + if (sb.st_nlink == 2) + break; + + if (count == 0) + prev_size = sb.st_size; + + if (prev_size == sb.st_size && ++count > Retry) + { + if (DotlockFlags & DL_FL_FORCE) + { + unlink (lockfile); + count = 0; + continue; + } + else + { + unlink (nfslockfile); + return DL_EX_EXIST; + } + } + + prev_size = sb.st_size; + + /* don't trust sleep (3) as it may be interrupted + * by users sending signals. + */ + + t = time (NULL); + do { + sleep (1); + } while (time (NULL) == t); + } + + unlink (nfslockfile); + + return DL_EX_OK; +} + + +/* + * Unlock a file. + * + * The same comment as for dotlock_lock () applies here. + * + */ + +static int +dotlock_unlock (const char *realpath) +{ + char lockfile[_POSIX_PATH_MAX + LONG_STRING]; + int i; + + sprintf (lockfile, "%s.lock", realpath); + + i = unlink (lockfile); + + if (i == -1) + return DL_EX_ERROR; + + return DL_EX_OK; +} + +/* remove an empty file */ + +static int +dotlock_unlink (const char *realpath) +{ + struct stat lsb; + int i = -1; + + if (dotlock_lock (realpath) != DL_EX_OK) + return DL_EX_ERROR; + + if ((i = lstat (realpath, &lsb)) == 0 && lsb.st_size == 0) + unlink (realpath); + + dotlock_unlock (realpath); + + return (i == 0) ? DL_EX_OK : DL_EX_ERROR; +} + + +/* + * Check if a file can be locked at all. + * + * The same comment as for dotlock_lock () applies here. + * + */ + +static int +dotlock_try (void) +{ + if (access (".", W_OK) == 0) + return DL_EX_OK; + + return DL_EX_IMPOSSIBLE; +} diff --git a/share/lbdb/fetchaddr.c b/share/lbdb/fetchaddr.c @@ -0,0 +1,226 @@ +/* + * Copyright (C) 1998-2000 Thomas Roessler <roessler@guug.de> + * Copyright (C) 2000 Roland Rosenfeld <roland@spinnaker.de> + * + * This program is free software; you can redistribute + * it and/or modify it under the terms of the GNU + * General Public License as published by the Free + * Software Foundation; either version 2 of the License, + * or (at your option) any later version. + * + * This program is distributed in the hope that it will + * be useful, but WITHOUT ANY WARRANTY; without even the + * implied warranty of MERCHANTABILITY or FITNESS FOR A + * PARTICULAR PURPOSE. See the GNU 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 St, Fifth Floor, Boston, MA 02110-1301,, USA. + * + */ + +/* $Id: fetchaddr.c,v 1.23 2007-10-28 16:33:35 roland Exp $ */ + +#include <stdio.h> +#include <stdlib.h> +#include <string.h> +#include <time.h> + +#include "helpers.h" +#include "rfc822.h" +#include "rfc2047.h" + +#define MAXHDRS 21 + +struct header +{ + char *tag; + char *value; + size_t len; + size_t taglen; +}; + +struct header hdr[MAXHDRS] = +{ + { "to:", NULL, 0, 3 }, + { "from:", NULL, 0, 5 }, + { "cc:", NULL, 0, 3 }, + { "resent-from:", NULL, 0, 12 }, + { "resent-to:", NULL, 0, 10 }, + { NULL, NULL, 0, 0 } +}; + +void chop(struct header *cur) +{ + if(cur->len && cur->value[cur->len - 1] == '\n') + cur->value[--cur->len] = '\0'; +} + +int writeout(struct header *h, const char *datefmt, + unsigned char create_real_name) +{ + int rv = 0; + ADDRESS *addr, *p; + time_t timep; + char timebuf[256]; + char *c; + + if(!h->value) + return 0; + + addr = rfc822_parse_adrlist(NULL, h->value); + time(&timep); + + rfc2047_decode_adrlist(addr); + for(p = addr; p; p = p->next) + { + if(create_real_name == 1 + && (!p->personal || !*p->personal) + && p->mailbox) + { + if(p->personal) + FREE(p->personal); + p->personal = safe_strdup(p->mailbox); + c=strchr(p->personal, '@'); + if (c) + *c='\0'; + } + if(!p->group && p->mailbox && *p->mailbox && p->personal) + { + if(p->personal && strlen(p->personal) > 30) + strcpy(p->personal + 27, "..."); + + if ((c=strchr(p->mailbox,'@'))) + for(c++; *c; c++) + *c=tolower(*c); + + strftime(timebuf, sizeof(timebuf), datefmt, localtime(&timep)); + printf("%s\t%s\t%s\n", p->mailbox, p->personal && *p->personal ? + p->personal : "no realname given", timebuf); + + rv = 1; + } + } + + rfc822_free_address(&addr); + + return rv; +} + +int main(int argc, char* argv[]) +{ + char buff[2048]; + char *t; + int i, rv; + int partial = 0; + struct header *cur_hdr = NULL; + char *datefmt = NULL; + char *headerlist = NULL; + char *fieldname, *next; + char create_real_name = 0; +#ifdef HAVE_ICONV + const char **charsetptr = &Charset; +#endif + + /* process command line arguments: */ + if (argc > 1) { + i = 1; + while (i < argc) { + if (!strcmp (argv[i], "-d") && i+1 < argc) { + datefmt = argv[++i]; + } else if (!strcmp (argv[i], "-x") && i+1 < argc) { + headerlist = argv[++i]; +#ifdef HAVE_ICONV + } else if (!strcmp (argv[i], "-c") && i+1 < argc) { + *charsetptr = argv[++i]; +#endif + } else if (!strcmp (argv[i], "-a")) { + create_real_name = 1; + } else { + fprintf (stderr, "%s: `%s' wrong parameter\n", argv[0], argv[i]); + } + i++; + } + } + + if (!datefmt) + datefmt = safe_strdup("%Y-%m-%d %H:%M"); + + if (headerlist && strlen (headerlist) > 0 ) { + fieldname = headerlist; + i = 0; + while ( i < MAXHDRS-1 && (next = strchr (fieldname, ':'))) { + hdr[i].tag = safe_malloc (next - fieldname + 2); + strncpy (hdr[i].tag, fieldname, next - fieldname); + hdr[i].tag[next - fieldname] = ':'; + hdr[i].tag[next - fieldname + 1] = '\0'; + hdr[i].taglen = next - fieldname + 1; + fieldname = next+1; + i++; + } + + if (i < MAXHDRS-1 && *fieldname != '\0') { + hdr[i].tag = safe_malloc (strlen (fieldname) + 2); + strncpy (hdr[i].tag, fieldname, strlen (fieldname)); + hdr[i].tag[strlen (fieldname)] = ':'; + hdr[i].tag[strlen (fieldname) + 1] = '\0'; + hdr[i].taglen = strlen (fieldname) + 1; + i++; + } + + hdr[i].tag = NULL; /* end of hdr list */ + } + + while(fgets(buff, sizeof(buff), stdin)) + { + + if(!partial && *buff == '\n') + break; + + if(cur_hdr && (partial || *buff == ' ' || *buff == '\t')) + { + size_t nl = cur_hdr->len + strlen(buff); + + safe_realloc((void **) &cur_hdr->value, nl + 1); + strcpy(cur_hdr->value + cur_hdr->len, buff); + cur_hdr->len = nl; + chop(cur_hdr); + } + else if(!partial && *buff != ' ' && *buff != '\t') + { + cur_hdr = NULL; + + for(i = 0; hdr[i].tag; i++) + { + if(!strncasecmp(buff, hdr[i].tag, hdr[i].taglen)) + { + cur_hdr = &hdr[i]; + break; + } + } + + if(cur_hdr) + { + safe_free(&cur_hdr->value); + cur_hdr->value = safe_strdup(buff + cur_hdr->taglen); + cur_hdr->len = strlen(cur_hdr->value); + chop(cur_hdr); + } + } + + if(!(t = strchr(buff, '\n'))) + partial = 1; + else + partial = 0; + } + + for(rv = 0, i = 0; hdr[i].tag; i++) + { + if(hdr[i].value) + rv = writeout(&hdr[i], datefmt, create_real_name) || rv; + } + + return (rv ? 0 : 1); + +} diff --git a/share/lbdb/helpers.c b/share/lbdb/helpers.c @@ -0,0 +1,122 @@ +/* + * The functions in this file have completely been stolen + * from the Mutt mail user agent, with some slight + * modifications by Thomas Roessler <roessler@guug.de> to + * use them in the "little brother database". + * + * The following copyright notice applies: + * + * Copyright (C) 1996-8 Michael R. Elkins <me@cs.hmc.edu> + * + * This program is free software; you can + * redistribute it and/or modify it under the terms + * of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of + * the License, or (at your option) any later + * version. + * + * This program is distributed in the hope that it + * will be useful, but WITHOUT ANY WARRANTY; without + * even the implied warranty of MERCHANTABILITY or + * FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * 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 St, Fifth Floor, Boston, MA 02110-1301,, USA. + */ + +/* $Id: helpers.c,v 1.5 2005-10-29 14:48:08 roland Exp $ */ + +#include <stdio.h> +#include <stdlib.h> +#include <string.h> +#include <unistd.h> + +#include "helpers.h" + + +void *safe_calloc (size_t nmemb, size_t size) +{ + void *p; + + if (!nmemb || !size) + return NULL; + if (!(p = calloc (nmemb, size))) + { + perror ("Out of memory"); + sleep (1); + exit (1); + } + return p; +} + +void *safe_malloc (unsigned int siz) +{ + void *p; + + if (siz == 0) + return 0; + if ((p = (void *) malloc (siz)) == 0) + { + perror ("Out of memory!"); + sleep (1); + exit (1); + } + return (p); +} + +void safe_realloc (void **p, size_t siz) +{ + void *r; + + if (siz == 0) + { + if (*p) + { + free (*p); + *p = NULL; + } + return; + } + + if (*p) + r = (void *) realloc (*p, siz); + else + { + /* realloc(NULL, nbytes) doesn't seem to work under SunOS 4.1.x */ + r = (void *) malloc (siz); + } + + if (!r) + { + perror ("Out of memory!"); + sleep (1); + exit (1); + } + + *p = r; +} + +void safe_free (void *ptr) +{ + void **p = (void **)ptr; + if (*p) + { + free (*p); + *p = 0; + } +} + +char *safe_strdup (const char *s) +{ + char *p; + size_t l; + + if (!s || !*s) return 0; + l = strlen (s) + 1; + p = (char *)safe_malloc (l); + memcpy (p, s, l); + return (p); +} + diff --git a/share/lbdb/rfc2047.c b/share/lbdb/rfc2047.c @@ -0,0 +1,268 @@ +/* + * Copyright (C) 1996-8 Michael R. Elkins <me@cs.hmc.edu> + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU 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 St, Fifth Floor, Boston, MA 02110-1301,, USA. + */ + +/* $Id: rfc2047.c,v 1.5 2007-10-28 16:33:36 roland Exp $ */ + +#include <ctype.h> +#include <string.h> +#ifdef HAVE_ICONV +#include <iconv.h> +#include <errno.h> +#include <limits.h> +#endif + +#include "rfc822.h" +#include "rfc2047.h" +#include "helpers.h" + +enum +{ + ENCOTHER, + ENC7BIT, + ENC8BIT, + ENCQUOTEDPRINTABLE, + ENCBASE64, + ENCBINARY +}; + +const char MimeSpecials[] = "@.,;<>[]\\\"()?/="; +const char *Charset = "iso-8859-15"; /* XXX - hack */ + + +int Index_hex[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,-1, -1,-1,-1,-1, + 0, 1, 2, 3, 4, 5, 6, 7, 8, 9,-1,-1, -1,-1,-1,-1, + -1,10,11,12, 13,14,15,-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,10,11,12, 13,14,15,-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 +}; + +int Index_64[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 +}; + + +#define IsPrint(c) (isprint((unsigned char)(c)) || \ + ((unsigned char)(c) >= 0xa0)) + +#define hexval(c) Index_hex[(unsigned int)(c)] +#define base64val(c) Index_64[(unsigned int)(c)] + +static int rfc2047_decode_word (char *d, const char *s, size_t dlen) +{ + char *p = safe_strdup (s); + char *pp = p; + char *pd = d; + size_t len = dlen; + int enc = 0, filter = 0, count = 0, c1, c2, c3, c4; +#ifdef HAVE_ICONV + char *fromcharset; + iconv_t cd; + size_t in; +#endif + + while ((pp = strtok (pp, "?")) != NULL) + { + count++; + switch (count) + { + case 2: + if (strcasecmp (pp, Charset) != 0) + { + filter = 1; +#ifdef HAVE_ICONV + fromcharset = safe_strdup (pp); +#endif + } + break; + case 3: + if (toupper (*pp) == 'Q') + enc = ENCQUOTEDPRINTABLE; + else if (toupper (*pp) == 'B') + enc = ENCBASE64; + else + return (-1); + break; + case 4: + if (enc == ENCQUOTEDPRINTABLE) + { + while (*pp && len > 0) + { + if (*pp == '_') + { + *pd++ = ' '; + len--; + } + else if (*pp == '=') + { + *pd++ = (hexval(pp[1]) << 4) | hexval(pp[2]); + len--; + pp += 2; + } + else + { + *pd++ = *pp; + len--; + } + pp++; + } + *pd = 0; + } + else if (enc == ENCBASE64) + { + while (*pp && len > 0) + { + c1 = base64val(pp[0]); + c2 = base64val(pp[1]); + *pd++ = (c1 << 2) | ((c2 >> 4) & 0x3); + if (--len == 0) break; + + if (pp[2] == '=') break; + + c3 = base64val(pp[2]); + *pd++ = ((c2 & 0xf) << 4) | ((c3 >> 2) & 0xf); + if (--len == 0) + break; + + if (pp[3] == '=') + break; + + c4 = base64val(pp[3]); + *pd++ = ((c3 & 0x3) << 6) | c4; + if (--len == 0) + break; + + pp += 4; + } + *pd = 0; + } + break; + } + pp = 0; + } + safe_free (&p); + if (filter) + { +#ifdef HAVE_ICONV + if ((cd = iconv_open (Charset, fromcharset)) == (iconv_t)(-1)) + { +#endif + pd = d; + while (*pd) + { + if (!IsPrint (*pd)) + *pd = '?'; + pd++; + } +#ifdef HAVE_ICONV + } else { + p = safe_strdup (d); + pp = p; + in = strlen (d) + 1; + pd = d; + /* maximum available buffer length for converted string */ + len = dlen; + while (*pd && iconv (cd, &pp, &in, &pd, &len) == (size_t)(-1)) + { + if (errno == E2BIG) + break; + + *pd = '?'; + pp++; + in--; + pd++; + len--; + } + iconv (cd, NULL, NULL, &pd, &len); + iconv_close (cd); + safe_free (&p); + } + safe_free (&fromcharset); +#endif + } + return (0); +} + +/* try to decode anything that looks like a valid RFC2047 encoded + * header field, ignoring RFC822 parsing rules + */ +void rfc2047_decode (char *d, const char *s, size_t dlen) +{ + const char *p, *q; + size_t n; + int found_encoded = 0; + + dlen--; /* save room for the terminal nul */ + + while (*s && dlen > 0) + { + if ((p = strstr (s, "=?")) == NULL || + (q = strchr (p + 2, '?')) == NULL || + (q = strchr (q + 1, '?')) == NULL || + (q = strstr (q + 1, "?=")) == NULL) + { + /* no encoded words */ + if (d != s) + strfcpy (d, s, dlen + 1); + return; + } + + if (p != s) + { + n = (size_t) (p - s); + /* ignore spaces between encoded words */ + if (!found_encoded || strspn (s, " \t\r\n") != n) + { + if (n > dlen) + n = dlen; + if (d != s) + memcpy (d, s, n); + d += n; + dlen -= n; + } + } + + rfc2047_decode_word (d, p, dlen); + found_encoded = 1; + s = q + 2; + n = strlen (d); + dlen -= n; + d += n; + } + *d = 0; +} + +void rfc2047_decode_adrlist (ADDRESS *a) +{ + while (a) + { + if (a->personal && strstr (a->personal, "=?") != NULL) + rfc2047_decode (a->personal, a->personal, strlen (a->personal) + 1); + a = a->next; + } +} diff --git a/share/lbdb/rfc822.c b/share/lbdb/rfc822.c @@ -0,0 +1,768 @@ +/* + * Copyright (C) 1996-8 Michael R. Elkins <me@cs.hmc.edu> + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU 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 St, Fifth Floor, Boston, MA 02110-1301,, USA. + */ + +/* $Id: rfc822.c,v 1.5 2005-10-29 14:48:11 roland Exp $ */ + +#include <string.h> +#include <ctype.h> +#include <stdlib.h> +#include <stdio.h> + +#include "helpers.h" +#include "rfc822.h" + +const char RFC822Specials[] = "@.,:;<>[]\\\"()"; +#define is_special(x) strchr(RFC822Specials,x) + +int RFC822Error = 0; + +/* these must defined in the same order as the numerated errors given in rfc822.h */ +const char *RFC822Errors[] = { + "out of memory", + "mismatched parenthesis", + "mismatched quotes", + "bad route in <>", + "bad address in <>", + "bad address spec" +}; + +void rfc822_dequote_comment (char *s) +{ + char *w = s; + + for (; *s; s++) + { + if (*s == '\\') + { + if (!*++s) + break; /* error? */ + *w++ = *s; + } + else if (*s != '\"') + { + if (w != s) + *w = *s; + w++; + } + } + *w = 0; +} + +void rfc822_free_address (ADDRESS **p) +{ + ADDRESS *t; + + while (*p) + { + t = *p; + *p = (*p)->next; +#ifdef EXACT_ADDRESS + FREE (&t->val); +#endif + FREE (&t->personal); + FREE (&t->mailbox); + FREE (&t); + } +} + +static const char * +parse_comment (const char *s, + char *comment, size_t *commentlen, size_t commentmax) +{ + int level = 1; + + while (*s && level) + { + if (*s == '(') + level++; + else if (*s == ')') + { + if (--level == 0) + { + s++; + break; + } + } + else if (*s == '\\') + { + if (!*++s) + break; + } + if (*commentlen < commentmax) + comment[(*commentlen)++] = *s; + s++; + } + if (level) + { + RFC822Error = ERR_MISMATCH_PAREN; + return NULL; + } + return s; +} + +static const char * +parse_quote (const char *s, char *token, size_t *tokenlen, size_t tokenmax) +{ + if (*tokenlen < tokenmax) + token[(*tokenlen)++] = '"'; + while (*s) + { + if (*tokenlen < tokenmax) + token[(*tokenlen)++] = *s; + if (*s == '"') + return (s + 1); + if (*s == '\\') + { + if (!*++s) + break; + } + s++; + } + RFC822Error = ERR_MISMATCH_QUOTE; + return NULL; +} + +static const char * +next_token (const char *s, char *token, size_t *tokenlen, size_t tokenmax) +{ + if (*s == '(') + return (parse_comment (s + 1, token, tokenlen, tokenmax)); + if (*s == '"') + return (parse_quote (s + 1, token, tokenlen, tokenmax)); + if (is_special (*s)) + { + if (*tokenlen < tokenmax) + token[(*tokenlen)++] = *s; + return (s + 1); + } + while (*s) + { + if (isspace (*s) || is_special (*s)) + break; + if (*tokenlen < tokenmax) + token[(*tokenlen)++] = *s; + s++; + } + return s; +} + +static const char * +parse_mailboxdomain (const char *s, const char *nonspecial, + char *mailbox, size_t *mailboxlen, size_t mailboxmax, + char *comment, size_t *commentlen, size_t commentmax) +{ + const char *ps; + + while (*s) + { + SKIPWS (s); + if (strchr (nonspecial, *s) == NULL && is_special (*s)) + return s; + + if (*s == '(') + { + if (*commentlen && *commentlen < commentmax) + comment[(*commentlen)++] = ' '; + ps = next_token (s, comment, commentlen, commentmax); + } + else + ps = next_token (s, mailbox, mailboxlen, mailboxmax); + if (!ps) + return NULL; + s = ps; + } + + return s; +} + +static const char * +parse_address (const char *s, + char *token, size_t *tokenlen, size_t tokenmax, + char *comment, size_t *commentlen, size_t commentmax, + ADDRESS *addr) +{ + s = parse_mailboxdomain (s, ".\"(\\", + token, tokenlen, tokenmax, + comment, commentlen, commentmax); + if (!s) + return NULL; + + if (*s == '@') + { + if (*tokenlen < tokenmax) + token[(*tokenlen)++] = '@'; + s = parse_mailboxdomain (s + 1, ".([]\\", + token, tokenlen, tokenmax, + comment, commentlen, commentmax); + if (!s) + return NULL; + } + + token[*tokenlen] = 0; + addr->mailbox = safe_strdup (token); + + if (*commentlen && !addr->personal) + { + comment[*commentlen] = 0; + addr->personal = safe_strdup (comment); + } + + return s; +} + +static const char * +parse_route_addr (const char *s, + char *comment, size_t *commentlen, size_t commentmax, + ADDRESS *addr) +{ + char token[STRING]; + size_t tokenlen = 0; + + SKIPWS (s); + + /* find the end of the route */ + if (*s == '@') + { + while (s && *s == '@') + { + if (tokenlen < sizeof (token) - 1) + token[tokenlen++] = '@'; + s = parse_mailboxdomain (s + 1, ".\\[](", token, + &tokenlen, sizeof (token) - 1, + comment, commentlen, commentmax); + } + if (!s || *s != ':') + { + RFC822Error = ERR_BAD_ROUTE; + return NULL; /* invalid route */ + } + + if (tokenlen < sizeof (token) - 1) + token[tokenlen++] = ':'; + s++; + } + + if ((s = parse_address (s, token, &tokenlen, sizeof (token) - 1, comment, commentlen, commentmax, addr)) == NULL) + return NULL; + + if (*s != '>' || !addr->mailbox) + { + RFC822Error = ERR_BAD_ROUTE_ADDR; + return NULL; + } + + s++; + return s; +} + +static const char * +parse_addr_spec (const char *s, + char *comment, size_t *commentlen, size_t commentmax, + ADDRESS *addr) +{ + char token[STRING]; + size_t tokenlen = 0; + + s = parse_address (s, token, &tokenlen, sizeof (token) - 1, comment, commentlen, commentmax, addr); + if (s && *s && *s != ',' && *s != ';') + { + RFC822Error = ERR_BAD_ADDR_SPEC; + return NULL; + } + return s; +} + +static void +add_addrspec (ADDRESS **top, ADDRESS **last, const char *phrase, + char *comment, size_t *commentlen, size_t commentmax) +{ + ADDRESS *cur = rfc822_new_address (); + + if (parse_addr_spec (phrase, comment, commentlen, commentmax, cur) == NULL) + return; + + if (*last) + (*last)->next = cur; + else + *top = cur; + *last = cur; +} + +ADDRESS *rfc822_parse_adrlist (ADDRESS *top, const char *s) +{ + const char *begin, *ps; + char comment[STRING], phrase[STRING]; + size_t phraselen = 0, commentlen = 0; + ADDRESS *cur, *last = NULL; + + RFC822Error = 0; + + last = top; + while (last && last->next) + last = last->next; + + SKIPWS (s); + begin = s; + while (*s) + { + if (*s == ',') + { + if (phraselen) + { + phrase[phraselen] = 0; + add_addrspec (&top, &last, phrase, comment, &commentlen, sizeof (comment) - 1); + } + else if (commentlen && last && !last->personal) + { + comment[commentlen] = 0; + last->personal = safe_strdup (comment); + } + +#ifdef EXACT_ADDRESS + if (last && !last->val) + last->val = mutt_substrdup (begin, s); +#endif + commentlen = 0; + phraselen = 0; + s++; + begin = s; + SKIPWS (begin); + } + else if (*s == '(') + { + if (commentlen && commentlen < sizeof (comment) - 1) + comment[commentlen++] = ' '; + if ((ps = next_token (s, comment, &commentlen, sizeof (comment) - 1)) == NULL) + { + rfc822_free_address (&top); + return NULL; + } + s = ps; + } + else if (*s == ':') + { + cur = rfc822_new_address (); + phrase[phraselen] = 0; + cur->mailbox = safe_strdup (phrase); + cur->group = 1; + + if (last) + last->next = cur; + else + top = cur; + last = cur; + +#ifdef EXACT_ADDRESS + last->val = mutt_substrdup (begin, s); +#endif + + phraselen = 0; + commentlen = 0; + s++; + begin = s; + SKIPWS (begin); + } + else if (*s == ';') + { + if (phraselen) + { + phrase[phraselen] = 0; + add_addrspec (&top, &last, phrase, comment, &commentlen, sizeof (comment) - 1); + } + else if (commentlen && !last->personal) + { + comment[commentlen] = 0; + last->personal = safe_strdup (comment); + } +#ifdef EXACT_ADDRESS + if (last && !last->val) + last->val = mutt_substrdup (begin, s); +#endif + + /* add group terminator */ + cur = rfc822_new_address (); + if (last) + { + last->next = cur; + last = cur; + } + + phraselen = 0; + commentlen = 0; + s++; + begin = s; + SKIPWS (begin); + } + else if (*s == '<') + { + phrase[phraselen] = 0; + cur = rfc822_new_address (); + if (phraselen) + { + if (cur->personal) + FREE (&cur->personal); + /* if we get something like "Michael R. Elkins" remove the quotes */ + rfc822_dequote_comment (phrase); + cur->personal = safe_strdup (phrase); + } + if ((ps = parse_route_addr (s + 1, comment, &commentlen, sizeof (comment) - 1, cur)) == NULL) + { + rfc822_free_address (&top); + rfc822_free_address (&cur); + return NULL; + } + + if (last) + last->next = cur; + else + top = cur; + last = cur; + + phraselen = 0; + commentlen = 0; + s = ps; + } + else + { + if (phraselen && phraselen < sizeof (phrase) - 1) + phrase[phraselen++] = ' '; + if ((ps = next_token (s, phrase, &phraselen, sizeof (phrase) - 1)) == NULL) + { + rfc822_free_address (&top); + return NULL; + } + s = ps; + } + SKIPWS (s); + } + + if (phraselen) + { + phrase[phraselen] = 0; + comment[commentlen] = 0; + add_addrspec (&top, &last, phrase, comment, &commentlen, sizeof (comment) - 1); + } + else if (commentlen && last && !last->personal) + { + comment[commentlen] = 0; + last->personal = safe_strdup (comment); + } +#ifdef EXACT_ADDRESS + if (last) + last->val = mutt_substrdup (begin, s); +#endif + + return top; +} + +void rfc822_qualify (ADDRESS *addr, const char *host) +{ + char *p; + + for (; addr; addr = addr->next) + if (!addr->group && addr->mailbox && strchr (addr->mailbox, '@') == NULL) + { + p = safe_malloc (strlen (addr->mailbox) + strlen (host) + 2); + sprintf (p, "%s@%s", addr->mailbox, host); + safe_free (&addr->mailbox); + addr->mailbox = p; + } +} + +void +rfc822_cat (char *buf, size_t buflen, const char *value, const char *specials) +{ + if (strpbrk (value, specials)) + { + char tmp[256], *pc = tmp; + size_t tmplen = sizeof (tmp) - 3; + + *pc++ = '"'; + for (; *value && tmplen > 1; value++) + { + if (*value == '\\' || *value == '"') + { + *pc++ = '\\'; + tmplen--; + } + *pc++ = *value; + tmplen--; + } + *pc++ = '"'; + *pc = 0; + strfcpy (buf, tmp, buflen); + } + else + strfcpy (buf, value, buflen); +} + +void rfc822_write_address_single (char *buf, size_t buflen, ADDRESS *addr) +{ + size_t len; + char *pbuf = buf; + char *pc; + + if (!addr) + return; + + buflen--; /* save room for the terminal nul */ + +#ifdef EXACT_ADDRESS + if (addr->val) + { + if (!buflen) + goto done; + strfcpy (pbuf, addr->val, buflen); + len = strlen (pbuf); + pbuf += len; + buflen -= len; + if (addr->group) + { + if (!buflen) + goto done; + *pbuf++ = ':'; + buflen--; + *pbuf = 0; + } + return; + } +#endif + + if (addr->personal) + { + if (strpbrk (addr->personal, RFC822Specials)) + { + if (!buflen) + goto done; + *pbuf++ = '"'; + buflen--; + for (pc = addr->personal; *pc && buflen > 0; pc++) + { + if (*pc == '"' || *pc == '\\') + { + if (!buflen) + goto done; + *pbuf++ = '\\'; + buflen--; + } + if (!buflen) + goto done; + *pbuf++ = *pc; + buflen--; + } + if (!buflen) + goto done; + *pbuf++ = '"'; + buflen--; + } + else + { + if (!buflen) + goto done; + strfcpy (pbuf, addr->personal, buflen); + len = strlen (pbuf); + pbuf += len; + buflen -= len; + } + + if (!buflen) + goto done; + *pbuf++ = ' '; + buflen--; + } + + if (addr->personal || (addr->mailbox && *addr->mailbox == '@')) + { + if (!buflen) + goto done; + *pbuf++ = '<'; + buflen--; + } + + if (addr->mailbox) + { + if (!buflen) + goto done; + strfcpy (pbuf, addr->mailbox, buflen); + len = strlen (pbuf); + pbuf += len; + buflen -= len; + + if (addr->personal || (addr->mailbox && *addr->mailbox == '@')) + { + if (!buflen) + goto done; + *pbuf++ = '>'; + buflen--; + } + + if (addr->group) + { + if (!buflen) + goto done; + *pbuf++ = ':'; + buflen--; + if (!buflen) + goto done; + *pbuf++ = ' '; + buflen--; + } + } + else + { + if (!buflen) + goto done; + *pbuf++ = ';'; + buflen--; + } +done: + /* no need to check for length here since we already save space at the + beginning of this routine */ + *pbuf = 0; +} + +/* note: it is assumed that `buf' is nul terminated! */ +void rfc822_write_address (char *buf, size_t buflen, ADDRESS *addr) +{ + char *pbuf = buf; + size_t len = strlen (buf); + + buflen--; /* save room for the terminal nul */ + + if (len > 0) + { + if (len > buflen) + return; /* safety check for bogus arguments */ + + pbuf += len; + buflen -= len; + if (!buflen) + goto done; + *pbuf++ = ','; + buflen--; + if (!buflen) + goto done; + *pbuf++ = ' '; + buflen--; + } + + for (; addr && buflen > 0; addr = addr->next) + { + /* use buflen+1 here because we already saved space for the trailing + nul char, and the subroutine can make use of it */ + rfc822_write_address_single (pbuf, buflen + 1, addr); + + /* this should be safe since we always have at least 1 char passed into + the above call, which means `pbuf' should always be nul terminated */ + len = strlen (pbuf); + pbuf += len; + buflen -= len; + + /* if there is another address, and its not a group mailbox name or + group terminator, add a comma to separate the addresses */ + if (addr->next && addr->next->mailbox && !addr->group) + { + if (!buflen) + goto done; + *pbuf++ = ','; + buflen--; + if (!buflen) + goto done; + *pbuf++ = ' '; + buflen--; + } + } +done: + *pbuf = 0; +} + +/* this should be rfc822_cpy_adr */ +ADDRESS *rfc822_cpy_adr_real (ADDRESS *addr) +{ + ADDRESS *p = rfc822_new_address (); + +#ifdef EXACT_ADDRESS + p->val = safe_strdup (addr->val); +#endif + p->personal = safe_strdup (addr->personal); + p->mailbox = safe_strdup (addr->mailbox); + p->group = addr->group; + return p; +} + +/* this should be rfc822_cpy_adrlist */ +ADDRESS *rfc822_cpy_adr (ADDRESS *addr) +{ + ADDRESS *top = NULL, *last = NULL; + + for (; addr; addr = addr->next) + { + if (last) + { + last->next = rfc822_cpy_adr_real (addr); + last = last->next; + } + else + top = last = rfc822_cpy_adr_real (addr); + } + return top; +} + +/* append list 'b' to list 'a' and return the last element in the new list */ +ADDRESS *rfc822_append (ADDRESS **a, ADDRESS *b) +{ + ADDRESS *tmp = *a; + + while (tmp && tmp->next) + tmp = tmp->next; + if (!b) + return tmp; + if (tmp) + tmp->next = rfc822_cpy_adr (b); + else + tmp = *a = rfc822_cpy_adr (b); + while (tmp && tmp->next) + tmp = tmp->next; + return tmp; +} + +#ifdef TESTING +void safe_free (void *ptr) +{ + void **p = (void **)ptr; + if (*p) + { + free (*p); + *p = 0; + } +} + +int main (int argc, char **argv) +{ + ADDRESS *list; + char buf[256]; + char *str = "michael, Michael Elkins <me@cs.hmc.edu>, testing a really complex address: this example <@contains.a.source.route@with.multiple.hosts:address@example.com>;, lothar@of.the.hillpeople (lothar)"; + + list = rfc822_parse_adrlist (NULL, str); + buf[0] = 0; + rfc822_write_address (buf, sizeof (buf), list); + rfc822_free_address (&list); + puts (buf); + exit (0); +} +#endif