Back to index

courier  0.68.2
mailbot.c
Go to the documentation of this file.
00001 /*
00002 ** Copyright 2001-2010 Double Precision, Inc.
00003 ** See COPYING for distribution information.
00004 */
00005 
00006 #include "config.h"
00007 #include "dbobj.h"
00008 #include "liblock/config.h"
00009 #include "liblock/liblock.h"
00010 #include "unicode/unicode.h"
00011 #include "numlib/numlib.h"
00012 #include <string.h>
00013 #include <stdlib.h>
00014 #include <unistd.h>
00015 #include <fcntl.h>
00016 #include <stdio.h>
00017 #include <time.h>
00018 #if HAVE_LOCALE_H
00019 #include <locale.h>
00020 #endif
00021 #include <langinfo.h>
00022 #if HAVE_STRINGS_H
00023 #include <strings.h>
00024 #endif
00025 #include <ctype.h>
00026 #include "rfc822/rfc822.h"
00027 #include "rfc2045/rfc2045.h"
00028 #include "rfc2045/rfc2045charset.h"
00029 #include <sys/types.h>
00030 #include "mywait.h"
00031 #include <signal.h>
00032 #if HAVE_SYSEXITS_H
00033 #include <sysexits.h>
00034 #endif
00035 
00036 #ifndef EX_TEMPFAIL
00037 #define EX_TEMPFAIL  75
00038 #endif
00039 
00040 static const char *recips=0;
00041 static const char *dbfile=0;
00042 static const char *charset;
00043 static unsigned interval=1;
00044 static char *sender;
00045 
00046 struct header {
00047        struct header *next;
00048        char *buf;
00049 } ;
00050 
00051 static struct header *header_list;
00052 
00053 static struct header *extra_headers=0;
00054 
00055 
00056 void rfc2045_error(const char *str)
00057 {
00058        fprintf(stderr, "%s\n", str);
00059        exit(1);
00060 }
00061 
00062 static void usage()
00063 {
00064        fprintf(stderr,
00065               "Usage: mailbot [ options ] [ $MAILER arg arg... ]\n"
00066               "\n"
00067               "    -t filename        - text autoresponse\n"
00068               "    -c charset         - text MIME character set (default %s)\n"
00069               "    -m filename        - text autoresponse with a MIME header\n"
00070               "    -r addr1,addr2...  - any 'addr' required in a To/Cc header\n",
00071               charset);
00072 
00073        fprintf(stderr,
00074               "    -e                 - Prefer replies to Errors-To: or Return-Path: instead\n"
00075               "                         of From:\n"
00076               "    -T type            - \"type\": reply, replyall, replydsn, replyfeedback,\n"
00077               "                         forward, forwardatt\n"
00078               "    -N                 - Omit contents of the original message from replies\n"
00079               "    -F \"separator\"     - Set the forwarding separator\n"
00080               "    -S \"salutation\"    - Set salutation for replies\n"
00081               "    -d $pathname       - database to prevent duplicate autoresponses\n"
00082               "    -D x               - at least 'x' days before dupes (default: 1)\n");
00083 
00084        fprintf(stderr,
00085               "    -s subject         - Subject: on autoresponses\n"
00086               "    -A \"Header: stuff\" - Additional header on the autoresponse\n"
00087               "    -M recipient       - format \"replydsn\" as a DSN from 'recipient' (required)\n"
00088               "    -fuser@domain      - Set responding address for replydsn\n"
00089               "    -f                 - Set responding address from $SENDER\n"
00090               "    -R type            - Feedback type, for \"-T feedback\" or \"-T replyfeedback\":\n"
00091               "                         \"abuse\", \"fraud\", \"other\", or \"virus\"\n"
00092               "    -n                 - only show the resulting message, do not send it\n"
00093               "    -a                 - Attach entire message for replydsn, feedback, and\n"
00094               "                         replyfeedback, instead of only the headers.\n"
00095 );
00096 
00097        fprintf(stderr,
00098               "    --feedback-original-envelope-id {\"<envelopeid>\"}\n"
00099               "    --feedback-original-mail-from {\"<mailfrom>\"}\n"
00100               "    --feedback-reporting-mta {\"dns; hostname\"}\n"
00101               "    --feedback-source-ip {aaa.bbb.ccc.ddd}\n"
00102               "    --feedback-incidents {n}\n"
00103               "    --feedback-authentication-results {\"results\"}\n"
00104               "    --feedback-original-rcpt-to {\"<rcptto>\"]\n"
00105               "    --feedback-reported-domain {example.com}\n"
00106               "                       - optional parameters for -T \"feedback\" and \n"
00107               "                         -T \"replyfeedback\"\n"
00108               "    $MAILER arg arg... - run $MAILER (sendmail) to mail the autoresponse\n"
00109               );
00110 
00111        exit(EX_TEMPFAIL);
00112 }
00113 
00114 static void read_headers(FILE *tmpfp)
00115 {
00116        char buf[BUFSIZ];
00117        struct header **lasthdr= &header_list, *prevhdr=0;
00118 
00119        while (fgets(buf, sizeof(buf), tmpfp))
00120        {
00121               size_t l=strlen(buf);
00122 
00123               if (l > 0 && buf[l-1] == '\n')
00124                      --l;
00125               if (l > 0 && buf[l-1] == '\r')
00126                      --l;
00127               buf[l]=0;
00128 
00129               if (l == 0)
00130               {
00131                      /* Eat rest of message from stdin */
00132 
00133                      while (getc(stdin) != EOF)
00134                             ;
00135                      break;
00136               }
00137 
00138               if (isspace((int)(unsigned char)buf[0]) && prevhdr)
00139               {
00140                      if ( (prevhdr->buf=
00141                           realloc( prevhdr->buf,
00142                                   strlen (prevhdr->buf)+2+strlen(buf)))
00143                           == NULL)
00144                      {
00145                             perror("malloc");
00146                             exit(EX_TEMPFAIL);
00147                      }
00148                      strcat(strcat( prevhdr->buf, "\n"), buf);
00149               }
00150               else
00151               {
00152                      if ((*lasthdr=(struct header *)
00153                           malloc(sizeof(struct header))) == NULL ||
00154                          ((*lasthdr)->buf=strdup(buf)) == NULL)
00155                      {
00156                             perror("malloc");
00157                             exit(EX_TEMPFAIL);
00158                      }
00159 
00160                      prevhdr= *lasthdr;
00161                      lasthdr= &(*lasthdr)->next;
00162               }
00163        }
00164 
00165        *lasthdr=NULL;
00166 }
00167 
00168 const char *hdr(const char *hdrname)
00169 {
00170        struct header *h;
00171        size_t l=strlen(hdrname);
00172 
00173        for (h=header_list; h; h=h->next)
00174        {
00175               if (strncasecmp(h->buf, hdrname, l) == 0 &&
00176                   h->buf[l] == ':')
00177               {
00178                      const char *p=h->buf+l+1;
00179 
00180                      while (*p && isspace((int)(unsigned char)*p))
00181                             ++p;
00182                      return (p);
00183               }
00184        }
00185 
00186        return ("");
00187 }
00188 
00189 /*
00190 ** Get the sender's address
00191 */
00192 
00193 static void check_sender()
00194 {
00195        const char *h=hdr("reply-to");
00196        struct rfc822t *t;
00197        struct rfc822a *a;
00198 
00199        if (!h || !*h)
00200               h=hdr("from");
00201 
00202        if (!h || !*h)
00203               exit(0);
00204 
00205        t=rfc822t_alloc_new(h, NULL, NULL);
00206 
00207        if (!t || !(a=rfc822a_alloc(t)))
00208        {
00209               perror("malloc");
00210               exit(EX_TEMPFAIL);
00211        }
00212 
00213        if (a->naddrs <= 0)
00214               exit (0);
00215        sender=rfc822_getaddr(a, 0);
00216        rfc822a_free(a);
00217        rfc822t_free(t);
00218 
00219        if (!sender || !*sender)
00220               exit(0);
00221 }
00222 
00223 /*
00224 ** Do not autorespond to DSNs
00225 */
00226 
00227 static void check_dsn()
00228 {
00229        static const char ct[]="multipart/report;";
00230 
00231        const char *p=hdr("content-type");
00232 
00233        if (strncasecmp(p, ct, sizeof(ct)-1) == 0)
00234               exit(0);
00235 
00236        p=hdr("precedence");
00237 
00238        if (strncasecmp(p, "junk", 4) == 0 ||
00239            strncasecmp(p, "bulk", 4) == 0 ||
00240            strncasecmp(p, "list", 4) == 0)
00241               exit(0);      /* Just in case */
00242 
00243        p=hdr("auto-submitted");
00244 
00245        if (*p && strcmp(p, "no"))
00246               exit(0);
00247 
00248        p=hdr("list-id");
00249 
00250        if (*p)
00251               exit(0);
00252 }
00253 
00254 /*
00255 ** Check for a required recipient
00256 */
00257 
00258 static void check_recips()
00259 {
00260        char *buf;
00261        struct rfc822t *t;
00262        struct rfc822a *a;
00263        struct header *h;
00264 
00265        if (!recips || !*recips)
00266               return;
00267 
00268        buf=strdup(recips);
00269        if (!buf)
00270        {
00271               perror("strdup");
00272               exit(EX_TEMPFAIL);
00273        }
00274 
00275        for (h=header_list; h; h=h->next)
00276        {
00277               int i;
00278 
00279               if (strncasecmp(h->buf, "to:", 3) &&
00280                   strncasecmp(h->buf, "cc:", 3))
00281                      continue;
00282 
00283               t=rfc822t_alloc_new(h->buf+3, NULL, NULL);
00284               if (!t || !(a=rfc822a_alloc(t)))
00285               {
00286                      perror("malloc");
00287                      exit(EX_TEMPFAIL);
00288               }
00289 
00290               for (i=0; i<a->naddrs; i++)
00291               {
00292                      char *p=rfc822_getaddr(a, i);
00293                      char *q;
00294 
00295                      strcpy(buf, recips);
00296 
00297                      for (q=buf; (q=strtok(q, ", ")) != 0; q=0)
00298                      {
00299                             if (p && strcasecmp(p, q) == 0)
00300                             {
00301                                    free(p);
00302                                    free(buf);
00303                                    rfc822a_free(a);
00304                                    rfc822t_free(t);
00305                                    return;
00306                             }
00307                      }
00308 
00309                      free(p);
00310               }
00311               rfc822a_free(a);
00312               rfc822t_free(t);
00313        }
00314        free(buf);
00315        exit(0);
00316 }
00317 
00318 /*
00319 ** Check the dupe database.
00320 */
00321 
00322 #ifdef DbObj
00323 static void check_db()
00324 {
00325        char *dbname;
00326        char *lockname;
00327        int lockfd;
00328        struct dbobj db;
00329        time_t now;
00330        char *sender_key, *p;
00331 
00332        size_t val_len;
00333        char *val;
00334 
00335        if (!dbfile || !*dbfile)
00336               return;
00337 
00338        sender_key=strdup(sender);
00339        dbname=malloc(strlen(dbfile)+ sizeof( "." DBNAME));
00340        lockname=malloc(strlen(dbfile)+ sizeof(".lock"));
00341 
00342        for (p=sender_key; *p; p++)
00343               *p=tolower((int)(unsigned char)*p);
00344 
00345        if (!dbname || !lockname || !sender)
00346        {
00347               perror("malloc");
00348               exit(EX_TEMPFAIL);
00349        }
00350 
00351        strcat(strcpy(dbname, dbfile), "." DBNAME);
00352        strcat(strcpy(lockname, dbfile), ".lock");
00353 
00354        lockfd=open(lockname, O_RDWR|O_CREAT, 0666);
00355 
00356        if (lockfd < 0 || ll_lock_ex(lockfd))
00357        {
00358               perror(lockname);
00359               exit(EX_TEMPFAIL);
00360        }
00361 
00362        dbobj_init(&db);
00363 
00364        if (dbobj_open(&db, dbname, "C") < 0)
00365        {
00366               perror(dbname);
00367               exit(EX_TEMPFAIL);
00368        }
00369 
00370        time(&now);
00371 
00372        val=dbobj_fetch(&db, sender_key, strlen(sender_key), &val_len, "");
00373 
00374        if (val)
00375        {
00376               time_t t;
00377 
00378               if (val_len >= sizeof(t))
00379               {
00380                      memcpy(&t, val, sizeof(t));
00381 
00382                      if (t >= now - interval * 60 * 60 * 24)
00383                      {
00384                             free(val);
00385                             dbobj_close(&db);
00386                             close(lockfd);
00387                             exit(0);
00388                      }
00389               }
00390               free(val);
00391        }
00392 
00393        dbobj_store(&db, sender_key, strlen(sender_key),
00394                   (void *)&now, sizeof(now), "R");
00395        dbobj_close(&db);
00396        close(lockfd);
00397 }
00398 #endif
00399 
00400 static void opensendmail(int argn, int argc, char **argv)
00401 {
00402        char **newargv;
00403        int i;
00404 
00405        if (argn >= argc)
00406        {
00407               static char *sendmail_argv[]={"sendmail", "-f", ""};
00408 
00409               argn=0;
00410               argc=3;
00411               argv=sendmail_argv;
00412        }
00413 
00414        newargv=(char **)malloc( sizeof(char *)*(argc-argn+1));
00415        if (!newargv)
00416        {
00417               perror("malloc");
00418               exit(EX_TEMPFAIL);
00419        }
00420 
00421        for (i=0; argn+i < argc; i++)
00422               newargv[i]=argv[argn+i];
00423        newargv[i]=0;
00424        signal(SIGCHLD, SIG_DFL);
00425 
00426        execvp(newargv[0], newargv);
00427        perror(newargv[0]);
00428        exit(EX_TEMPFAIL);
00429 }
00430 
00431 static struct rfc2045 *savemessage(FILE *tmpfp)
00432 {
00433        struct rfc2045 *rfcp=rfc2045_alloc();
00434        char buf[BUFSIZ];
00435        int n;
00436 
00437        if (!rfcp)
00438        {
00439               perror("rfc2045_alloc");
00440               exit(1);
00441        }
00442 
00443        while ((n=fread(buf, 1, sizeof(buf), stdin)) > 0)
00444        {
00445               if (fwrite(buf, n, 1, tmpfp) != 1)
00446               {
00447                      perror("fwrite(tempfile)");
00448                      exit(1);
00449               }
00450 
00451               rfc2045_parse(rfcp, buf, n);
00452        }
00453 
00454        if (n < 0)
00455        {
00456               perror("tempfile");
00457               exit(1);
00458        }
00459        return rfcp;
00460 }
00461 
00462 
00463 struct mimeautoreply_s {
00464        struct rfc2045_mkreplyinfo info;
00465        FILE *outf;
00466 
00467        FILE *contentf;
00468 };
00469 
00470 static void mimeautoreply_write_func(const char *str, size_t cnt, void *ptr)
00471 {
00472        if (cnt &&
00473            fwrite(str, cnt, 1, ((struct mimeautoreply_s *)ptr)->outf) != 1)
00474        {
00475               perror("tmpfile");
00476               exit(1);
00477        }
00478 }
00479 
00480 static void mimeautoreply_writesig_func(void *ptr)
00481 {
00482 }
00483 
00484 static int mimeautoreply_myaddr_func(const char *addr, void *ptr)
00485 {
00486        return 0;
00487 }
00488 
00489 static void copy_headers(void *ptr)
00490 {
00491        struct mimeautoreply_s *p=(struct mimeautoreply_s *)ptr;
00492        char buf[BUFSIZ];
00493 
00494        static const char ct[]="Content-Transfer-Encoding:";
00495 
00496        while (fgets(buf, sizeof(buf), p->contentf) != NULL)
00497        {
00498               if (buf[0] == '\n')
00499                      break;
00500 
00501               if (strncasecmp(buf, ct, sizeof(ct)-1) == 0)
00502                      continue;
00503 
00504               mimeautoreply_write_func(buf, strlen(buf), ptr);
00505 
00506               while (strchr(buf, '\n') == NULL)
00507               {
00508                      if (fgets(buf, sizeof(buf), p->contentf) == NULL)
00509                             break;
00510 
00511                      mimeautoreply_write_func(buf, strlen(buf), ptr);
00512               }
00513        }
00514 }
00515 
00516 static void copy_body(void *ptr)
00517 {
00518        struct mimeautoreply_s *p=(struct mimeautoreply_s *)ptr;
00519        char buf[BUFSIZ];
00520 
00521        while (fgets(buf, sizeof(buf), p->contentf) != NULL)
00522        {
00523               mimeautoreply_write_func(buf, strlen(buf), ptr);
00524        }
00525 }
00526 
00527 struct fb {
00528        struct fb *next;
00529        const char *n;
00530        const char *v;
00531 };
00532 
00533 int main(int argc, char **argv)
00534 {
00535        int argn;
00536        FILE *tmpfp;
00537        struct rfc2045 *rfcp;
00538        struct mimeautoreply_s replyinfo;
00539        const char *subj=0;
00540        const char *txtfile=0, *mimefile=0;
00541        const char *mimedsn=0;
00542        int nosend=0;
00543        const char *replymode="reply";
00544        int replytoenvelope=0;
00545        int donotquote=0;
00546        int fullmsg=0;
00547        const char *forwardsep="--- Forwarded message ---";
00548        const char *replysalut="%F writes:";
00549        struct rfc2045src *src;
00550 
00551        const char *feedback_type=0;
00552        struct fb *fb_list=0, **fb_tail=&fb_list;
00553        size_t fb_cnt=0;
00554 
00555        setlocale(LC_ALL, "");
00556        charset=unicode_default_chset();
00557 
00558        sender=NULL;
00559        for (argn=1; argn < argc; argn++)
00560        {
00561               char optc;
00562               char *optarg;
00563 
00564               if (argv[argn][0] != '-')
00565                      break;
00566 
00567               if (strcmp(argv[argn], "--") == 0)
00568               {
00569                      ++argn;
00570                      break;
00571               }
00572 
00573               if (strncmp(argv[argn], "--feedback-", 11) == 0)
00574               {
00575                      struct fb *f;
00576 
00577                      if (++argn >= argc)
00578                             break;
00579 
00580                      if ((f=malloc(sizeof(struct fb))) == NULL)
00581                      {
00582                             perror("malloc");
00583                             exit(1);
00584                      }
00585                      
00586                      f->n=argv[argn-1]+11;
00587                      f->v=argv[argn];
00588 
00589                      f->next=NULL;
00590                      *fb_tail=f;
00591                      fb_tail=&f->next;
00592                      ++fb_cnt;
00593                      continue;
00594               }
00595 
00596               optc=argv[argn][1];
00597               optarg=argv[argn]+2;
00598 
00599               if (!*optarg)
00600                      optarg=NULL;
00601 
00602               switch (optc) {
00603               case 'c':
00604                      if (!optarg && argn+1 < argc)
00605                             optarg=argv[++argn];
00606 
00607                      if (optarg && *optarg)
00608                      {
00609                             char *p=libmail_u_convert_tobuf("",
00610                                                         optarg,
00611                                                         libmail_u_ucs4_native,
00612                                                         NULL);
00613 
00614                             if (!p)
00615                             {
00616                                    fprintf(stderr, "Unknown charset: %s\n",
00617                                           charset);
00618                                    exit(1);
00619                             }
00620                             free(p);
00621                             charset=optarg;
00622                      }
00623                      continue;
00624               case 't':
00625                      if (!optarg && argn+1 < argc)
00626                             optarg=argv[++argn];
00627 
00628                      txtfile=optarg;
00629                      continue;
00630               case 'm':
00631                      if (!optarg && argn+1 < argc)
00632                             optarg=argv[++argn];
00633 
00634                      mimefile=optarg;
00635                      continue;
00636               case 'r':
00637                      if (!optarg && argn+1 < argc)
00638                             optarg=argv[++argn];
00639 
00640                      recips=optarg;
00641                      continue;
00642               case 'M':
00643                      if (!optarg && argn+1 < argc)
00644                             optarg=argv[++argn];
00645 
00646                      mimedsn=optarg;
00647                      continue;
00648               case 'R':
00649                      if (!optarg && argn+1 < argc)
00650                             optarg=argv[++argn];
00651 
00652                      feedback_type=optarg;
00653                      continue;
00654               case 'd':
00655                      if (!optarg && argn+1 < argc)
00656                             optarg=argv[++argn];
00657 
00658                      dbfile=optarg;
00659                      continue;
00660               case 'e':
00661                      replytoenvelope=1;
00662                      continue;
00663               case 'T':
00664                      if (!optarg && argn+1 < argc)
00665                             optarg=argv[++argn];
00666 
00667                      if (optarg && *optarg)
00668                             replymode=optarg;
00669                      continue;
00670               case 'N':
00671                      donotquote=1;
00672                      continue;
00673               case 'a':
00674                      fullmsg=1;
00675                      continue;
00676               case 'F':
00677                      if (!optarg && argn+1 < argc)
00678                             optarg=argv[++argn];
00679 
00680                      if (optarg && *optarg)
00681                             forwardsep=optarg;
00682                      continue;
00683               case 'S':
00684                      if (!optarg && argn+1 < argc)
00685                             optarg=argv[++argn];
00686 
00687                      if (optarg && *optarg)
00688                             replysalut=optarg;
00689                      continue;
00690               case 'D':
00691                      if (!optarg && argn+1 < argc)
00692                             optarg=argv[++argn];
00693 
00694                      interval=optarg ? atoi(optarg):1;
00695                      continue;
00696               case 'A':
00697                      if (!optarg && argn+1 < argc)
00698                             optarg=argv[++argn];
00699 
00700                      if (optarg)
00701                      {
00702                             struct header **h;
00703 
00704                             for (h= &extra_headers; *h;
00705                                  h= &(*h)->next)
00706                                    ;
00707 
00708                             if ((*h=malloc(sizeof(struct header))) == 0 ||
00709                                 ((*h)->buf=strdup(optarg)) == 0)
00710                             {
00711                                    perror("malloc");
00712                                    exit(EX_TEMPFAIL);
00713                             }
00714                             (*h)->next=0;
00715                      }
00716                      continue;
00717               case 's':
00718                      if (!optarg && argn+1 < argc)
00719                             optarg=argv[++argn];
00720 
00721                      subj=optarg;
00722                      continue;
00723 
00724               case 'f':
00725                      if (optarg && *optarg)
00726                      {
00727                             sender=strdup(optarg);
00728                      }
00729                      else
00730                      {
00731                             sender=getenv("SENDER");
00732                             if (!sender)
00733                                    continue;
00734                             sender=strdup(sender);
00735                      }
00736                      if (sender == NULL)
00737                      {
00738                             perror("malloc");
00739                             exit(1);
00740                      }
00741                      continue;
00742               case 'n':
00743                      nosend=1;
00744                      continue;
00745               default:
00746                      usage();
00747               }
00748        }
00749 
00750        if (!txtfile && !mimefile)
00751               usage();
00752 
00753        if (txtfile && mimefile)
00754               usage();
00755 
00756        tmpfp=tmpfile();
00757 
00758        if (!tmpfp)
00759        {
00760               perror("tmpfile");
00761               exit(1);
00762        }
00763 
00764        rfcp=savemessage(tmpfp);
00765 
00766        if (fseek(tmpfp, 0L, SEEK_SET) < 0)
00767        {
00768               perror("fseek(tempfile)");
00769               exit(1);
00770        }
00771 
00772        read_headers(tmpfp);
00773 
00774        if (sender == NULL || *sender == 0)
00775               check_sender();
00776 
00777        check_dsn();
00778        check_recips();
00779 #ifdef DbObj
00780        check_db();
00781 #endif
00782 
00783        src=rfc2045src_init_fd(fileno(tmpfp));
00784 
00785        memset(&replyinfo, 0, sizeof(replyinfo));
00786 
00787        replyinfo.info.src=src;
00788        replyinfo.info.rfc2045partp=rfcp;
00789        replyinfo.info.voidarg=&replyinfo;
00790 
00791        replyinfo.info.write_func=mimeautoreply_write_func;
00792 
00793        replyinfo.info.writesig_func=mimeautoreply_writesig_func;
00794 
00795        replyinfo.info.myaddr_func=mimeautoreply_myaddr_func;
00796 
00797        replyinfo.info.replymode=replymode;
00798        replyinfo.info.replytoenvelope=replytoenvelope;
00799        replyinfo.info.donotquote=donotquote;
00800 
00801        replyinfo.info.replysalut=replysalut;
00802        replyinfo.info.forwarddescr="Forwarded message";
00803        replyinfo.info.mailinglists="";
00804        replyinfo.info.charset=charset;
00805        replyinfo.info.subject=subj;
00806        replyinfo.info.forwardsep=forwardsep;
00807        replyinfo.info.fullmsg=fullmsg;
00808 
00809        if (mimedsn && *mimedsn)
00810        {
00811               replyinfo.info.dsnfrom=mimedsn;
00812               replyinfo.info.replymode="replydsn";
00813        }
00814        else if (feedback_type && *feedback_type)
00815        {
00816               replyinfo.info.feedbacktype=feedback_type;
00817 
00818               if (strcmp(replyinfo.info.replymode, "feedback") &&
00819                   strcmp(replyinfo.info.replymode, "replyfeedback"))
00820               {
00821                      fprintf(stderr, "\"-T feedback\" or \"-T replyfeedback\" required\n");
00822                      exit(1);
00823               }
00824 
00825               if (fb_cnt > 0)
00826               {
00827                      size_t i;
00828                      struct fb *p;
00829                      const char **strp;
00830 
00831                      replyinfo.info.feedbackheaders=
00832                             strp=malloc(sizeof(char *) * 2 * fb_cnt+1);
00833 
00834                      for (i=0, p=fb_list; p; p=p->next)
00835                      {
00836                             strp[i++]=p->n;
00837                             strp[i++]=p->v;
00838                      }
00839                      strp[i]=NULL;
00840               }
00841        }
00842 
00843        if (mimefile)
00844        {
00845               if ((replyinfo.contentf=fopen(mimefile, "r")) == NULL)
00846               {
00847                      perror(mimefile);
00848                      exit(1);
00849               }
00850 
00851               {
00852                      struct rfc2045 *rfcp=rfc2045_alloc();
00853                      static const char mv[]="Mime-Version: 1.0\n";
00854                      char buf[BUFSIZ];
00855                      int l;
00856                      const char *content_type;
00857                      const char *content_transfer_encoding;
00858                      const char *charset;
00859 
00860                      rfc2045_parse(rfcp, mv, sizeof(mv)-1);
00861 
00862                      while ((l=fread(buf, 1, sizeof(buf), replyinfo.contentf)
00863                             ) > 0)
00864                      {
00865                             rfc2045_parse(rfcp, buf, l);
00866                      }
00867 
00868                      if (l < 0 ||
00869                          fseek(replyinfo.contentf, 0L, SEEK_SET) < 0)
00870                      {
00871                             perror(mimefile);
00872                             exit(1);
00873                      }
00874 
00875                      rfc2045_mimeinfo(rfcp, &content_type,
00876                                     &content_transfer_encoding,
00877                                     &charset);
00878 
00879                      if (strcasecmp(content_type, "text/plain"))
00880                      {
00881                             fprintf(stderr,
00882                                    "%s must specify text/plain MIME type\n",
00883                                    mimefile);
00884                             exit(1);
00885                      }
00886                      {
00887                             char *p=NULL;
00888 
00889                             if (charset)
00890                                    p=libmail_u_convert_tobuf("",
00891                                                           charset,
00892                                                           libmail_u_ucs4_native,
00893                                                           NULL);
00894 
00895                             if (!p)
00896                             {
00897                                    fprintf(stderr, "Unknown charset in %s\n",
00898                                           mimefile);
00899                                    exit(1);
00900                             }
00901                             free(p);
00902                             replyinfo.info.charset=strdup(charset);
00903                      }
00904                      rfc2045_free(rfcp);
00905               }
00906               replyinfo.info.content_set_charset=copy_headers;
00907               replyinfo.info.content_specify=copy_body;
00908        }
00909        else if (txtfile)
00910        {
00911               if ((replyinfo.contentf=fopen(txtfile, "r")) == NULL)
00912               {
00913                      perror(mimefile);
00914                      exit(1);
00915               }
00916               replyinfo.info.content_specify=copy_body;
00917        }
00918 
00919        if (replyinfo.contentf)
00920               fcntl(fileno(replyinfo.contentf), F_SETFD, FD_CLOEXEC);
00921 
00922        if (nosend)
00923               replyinfo.outf=stdout;
00924        else
00925        {
00926               replyinfo.outf=tmpfile();
00927 
00928               if (replyinfo.outf == NULL)
00929               {
00930                      perror("tmpfile");
00931                      exit(1);
00932               }
00933        }
00934 
00935        {
00936               struct header *h;
00937 
00938               for (h=extra_headers; h; h=h->next)
00939                      fprintf(replyinfo.outf, "%s\n", h->buf);
00940        }
00941        fprintf(replyinfo.outf,
00942               "Precedence: junk\n"
00943               "Auto-Submitted: auto-replied\n");
00944 
00945        if (rfc2045_makereply(&replyinfo.info) < 0 ||
00946            fflush(replyinfo.outf) < 0 || ferror(replyinfo.outf) ||
00947            (!nosend &&
00948             (
00949              fseek(replyinfo.outf, 0L, SEEK_SET) < 0 ||
00950              (close(0), dup(fileno(replyinfo.outf))) < 0)
00951             ))
00952        {
00953               perror("tempfile");
00954               exit(1);
00955        }
00956        fclose(replyinfo.outf);
00957        fcntl(0, F_SETFD, 0);
00958 
00959        rfc2045_free(rfcp);
00960        rfc2045src_deinit(src);
00961 
00962        if (!nosend)
00963               opensendmail(argn, argc, argv);
00964        return (0);
00965 }