/* $Id: example.c,v 1.4 2010/03/09 12:29:27 kristaps Exp $ */ /* * Copyright (c) 2009, 2010 Kristaps Dzonsons <kristaps@bsd.lv> * * Permission to use, copy, modify, and distribute this software for any * purpose with or without fee is hereby granted, provided that the above * copyright notice and this permission notice appear in all copies. * * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN * ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF * OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. */ #include <sys/types.h> #include <assert.h> #include <fcntl.h> #include <getopt.h> #include <stdarg.h> #include <stdlib.h> #include <stdio.h> #include <string.h> #include <unistd.h> #include "mmail.h" struct mmbuf { char *buf; size_t bufsz; }; static const char *to = "\"Charlie Rööt\" <root@localhost>"; static const char *bod = "bÄ“das kļuvuÅ¡as par ikdienišķu svÄrku"; static void usage(void); static void mmp_err(enum mmerr, const struct mmp *, void *); static int mmp_warn(enum mmerr, const struct mmp *, void *); static int mm_stdout(void *, const char *, ...); static int mm_filter(void *, const char *, size_t, const char **, size_t *); /* * Simple example: reads in an e-mail file, parses/validates it, adds * some arbitrary (encoded) headers and body, and does some output * filtering for the sake of example. */ int main(int argc, char *argv[]) { int fd, rc; struct mmail *mm; ssize_t ssz, i; char buf[BUFSIZ]; struct mmp *mp; enum mmerr merr; struct mmwriter w; struct mmbuf mb; extern int optind; extern char *__progname; if (-1 != getopt(argc, argv, "")) { usage(); return(EXIT_FAILURE); } rc = 1; argc -= optind; argv += optind; if (strcmp(*argv, "-")) fd = open(*argv, O_RDONLY, 0); else fd = STDIN_FILENO; if (-1 == fd) { perror(*argv); return(EXIT_FAILURE); } /* We use UTF-8 encoding! */ mp = mmp_alloc("UTF-8", mmp_err, mmp_warn, *argv); if (NULL == mp) { perror("malloc"); return(EXIT_FAILURE); } memset(&mb, 0, sizeof(struct mmbuf)); memset(&w, 0, sizeof(struct mmwriter)); w.print = mm_stdout; w.filter = mm_filter; w.arg = &mb; /* Read the file in blocks. */ for ( ;; ) { ssz = read(fd, buf, BUFSIZ); if (-1 == ssz) { perror(*argv); goto out; } else if (0 == ssz) break; for (i = 0; i < ssz; i++) { if (mmp_char(mp, buf[(int)i])) continue; goto out; } } if ( ! mmp_end(mp)) goto out; if (STDIN_FILENO != fd && -1 == close(fd)) { perror(*argv); fd = -1; goto out; } fd = -1; /* Add a binary "To" address header. */ mm = mmp_mmail(mp); assert(mm); merr = mmail_addhead(mm, MMHEAD_TO, to, strlen(to)); if (MMAIL_OK != merr) { fprintf(stderr, "%s: %s\n", __progname, mmail_errstring(merr)); goto out; } merr = mmail_addbody(mm, bod, strlen(bod)); if (MMAIL_OK != merr) { fprintf(stderr, "%s: %s\n", __progname, mmail_errstring(merr)); goto out; } /* Serialise to stdout! */ if ( ! mmail_write(mm, &w)) goto out; rc = 1; out: if (-1 != fd) close(fd); if (mp) mmp_free(mp); if (mb.buf) free(mb.buf); return(rc ? EXIT_SUCCESS : EXIT_FAILURE); } static void usage(void) { extern char *__progname; fprintf(stderr, "usage: %s file\n", __progname); } static int mmp_warn(enum mmerr t, const struct mmp *p, void *arg) { char *fn; assert(arg); fn = (char *)arg; fprintf(stderr, "%s:%zu:%zu: warning: %s\n", fn, mmp_line(p), mmp_col(p), mmail_errstring(t)); return(1); } static void mmp_err(enum mmerr t, const struct mmp *p, void *arg) { char *fn; assert(arg); fn = (char *)arg; fprintf(stderr, "%s:%zu:%zu: error: %s\n", fn, mmp_line(p), mmp_col(p), mmail_errstring(t)); } /* ARGSUSED */ static int mm_stdout(void *arg, const char *fmt, ...) { va_list ap; va_start(ap, fmt); vprintf(fmt, ap); va_end(ap); return(1); } /* * Example use of a filter. If an input line is blank (length zero), * it's passed over. If it's not, it's copied into a temporary buffer, * with space between consecutive '@' values omitted including the at * symbols. Thus, "How are you @DOING@" results in "How are you ". The * temporary buffer is expanded on-demand and freed in the main calling * body. */ static int mm_filter(void *arg, const char *line, size_t linesz, const char **cp, size_t *csz) { struct mmbuf *buf; size_t sz; const char *p, *pp; char *ppp; assert(arg); buf = (struct mmbuf *)arg; if (0 == linesz) return(1); if (linesz > buf->bufsz) { ppp = realloc(buf->buf, linesz); if (NULL == ppp) { perror("realloc"); return(0); } buf->buf = ppp; buf->bufsz = linesz; } sz = 0; p = line; while (linesz && NULL != (pp = memchr(p, '@', linesz))) { assert(pp >= p); linesz -= /* LINTED */ (pp - p); /* LINTED */ memcpy(buf->buf + sz, p, pp - p); /* LINTED */ sz += (pp - p); p = pp; if (0 == --linesz) break; p++; pp = memchr(p, '@', linesz); if (NULL == pp) { p--; linesz++; break; } /* Just drop the interior contents. */ linesz -= /* LINTED */ (pp - p); p = pp; linesz--; p++; } if (linesz) { /* LINTED */ memcpy(buf->buf + sz, p, linesz); sz += linesz; } *cp = buf->buf; *csz = sz; return(1); }