home *** CD-ROM | disk | FTP | other *** search
/ Big Green CD 8 / BGCD_8_Dev.iso / NEXTSTEP / UNIX / Mail / appnmail-1.8-Solaris / mailapp-utilities / appnmail.m < prev    next >
Encoding:
Text File  |  1997-05-04  |  13.3 KB  |  488 lines

  1. /* -*-C-*-
  2. *******************************************************************************
  3. *
  4. * File:         appnmail.m
  5. * RCS:          /usr/local/sources/CVS/mailapp-utilities/appnmail.m,v 1.4 1997/04/19 17:37:18 tom Exp
  6. * Description:  Append stdin to Mail.app mailbox
  7. * Author:       Carl Edman
  8. * Created:      Fri Mar 12 18:21:23 1993
  9. * Modified:     Sat Nov 30 23:28:19 1996 Tom Hageman <tom@basil.icce.rug.nl>
  10. * Language:     Objective C
  11. * Package:      mailapp-utilities
  12. * Status:       First release.
  13. *
  14. * (C) Copyright 1993, but otherwise this file is perfect freeware.
  15. *
  16. *******************************************************************************
  17. */
  18.  
  19. //#import <AppKit/AppKit.h>
  20. #include <sys/types.h>
  21. #include <sys/stat.h>
  22. #include <fcntl.h>
  23. #include <errno.h>
  24. #import <stdlib.h>
  25. #import <unistd.h>
  26. #import <stdio.h>
  27. #import <string.h>
  28. #import <strings.h>
  29. #import <sys/file.h>
  30. #import <sys/param.h>
  31. #import <sys/types.h>
  32. #import <sys/stat.h>
  33. #import <varargs.h>
  34. //#import <defaults/defaults.h>
  35. #import "mailutil.h"
  36. #import "mailtoc.h"
  37. #import "optutil.h"
  38. #import "regex.h"
  39.  
  40. #define USAGE "\
  41. Usage: %s [-nv][-r|-f|-d|][-p pri][-H|-V] mbox...\n"
  42.  
  43. #define HELP "\
  44.  -n           skip mailbox if locked\n\
  45.  -v           talkative mode\n\
  46.  -r           mark message as read\n\
  47.  -f           mark message as flagged\n\
  48.  -d           mark message as deleted\n\
  49.  -p pri       set message's priority level to `pri'\n\
  50.  -V           show version\n\
  51.  -H           this help\n\
  52. "
  53.  
  54. char *header=0,*content=0;
  55. int headerlen=0,contentlen=0;
  56. int headermaxlen=0,contentmaxlen=0;
  57. char line[LINELEN];
  58.  
  59. /* ISO header conversion cruft. */
  60. #import "iso2next.h"
  61. /* Tables in there are expected to be in the "recode --header" format. */
  62. /* XXX maybe we'd better use "recode --header --strict"? */
  63.  
  64. int iso_convert(char *line)
  65. {
  66.    static const struct
  67.    {
  68.       const char *name;
  69.       const unsigned char *contents;
  70.    }
  71.    table[] = {
  72.       /* The contents of these tables are defined in "iso2next.h".
  73.      If you add one there, don't forget to add it here too. */
  74.       { "iso-8859-1", latin1_to_next },
  75.       { "iso-8859-2", latin2_to_next },
  76.    };
  77.    static struct regex *isore = 0;
  78.    const unsigned char *tt = 0;
  79.    const char *name;
  80.    int i, namelen;
  81.  
  82.    if (!isore) isore = re_compile("=?\\([^?]*\\)?Q?",1);
  83.    if (re_match(line, isore) == 0) return 0;
  84.    name = isore->braslist[0];
  85.    namelen = (isore->braelist[0] - isore->braslist[0]);
  86.    for (i = 0;  i < sizeof(table)/sizeof(table[0]);  i++)
  87.    {
  88.       if (strncasecmp(table[i].name, name, namelen) == 0 &&
  89.       table[i].name[namelen] == 0)
  90.       {
  91.      tt = table[i].contents;
  92.      break;
  93.       }
  94.    }
  95.    if (tt != 0)
  96.    {
  97.       int errors = 0;
  98.       // hack to translate iso-stuff, in-place.
  99.       char *s = (char *)isore->end, *t;
  100.  
  101.       for (; (t = strchr(s, '=')) != 0;  s = t)
  102.       {
  103.      if (t > s && t[-1] == '?')
  104.      {
  105.         // ?= is end quote.
  106.         if (errors > 0)
  107.         {
  108.            fprintf(stderr, "%s: warning: MIME 8-bit header encoding `%.*s' incomplete conversion\n", progname(), namelen, name);
  109.            return -1;
  110.         }
  111.         strcpy(t-1, t+1);
  112.         strcpy((char *)isore->start, isore->end);
  113.         return 1;
  114.      }
  115.      else
  116.      {
  117.         //  parse "=XX" where XX is a 2-digit hex number
  118.         unsigned c = 0;
  119.         
  120.         sscanf(t+1, "%2x", &c);
  121.         if (c < 256 && tt[c] != 0)
  122.         {
  123.            *t = tt[c];
  124.            strcpy(t+1, t+3);
  125.         }
  126.         else
  127.            ++errors;
  128.         ++t;
  129.      }
  130.       }
  131.       fprintf(stderr, "%s: warning: Missing terminating `?=' in MIME 8-bit header `%s'\n", progname(), line);
  132.       return 0;
  133.    }
  134.    fprintf(stderr, "%s: warning: MIME 8-bit header encoding `%.*s' is unsupported\n", progname(), namelen, name);
  135.    return 0;
  136. }
  137.  
  138. struct message_index *readmail(void)
  139. {
  140.    struct message_index *mi;
  141.    int i,pri=0;
  142.    char from[LINELEN]="",subject[LINELEN]="",reference[LINELEN]="";
  143.    struct regex *fre=re_compile("^From:  *\\(.*\\)\n",0);
  144.    struct regex *sre=re_compile("^Subject:  *\\(.*\\)\n",0);
  145.    struct regex *dre=re_compile("^Date: .*\\([ 0123][0-9]\\) \\([A-Z][a-z][a-z][a-z]*\\) 1*9*\\([789][0-9]\\)",0);
  146.    struct regex *rre=re_compile("^Next-Attachment: \\.tar\\.\\([0-9]*\\)\\.\\(.*\\)\\.attach, \\(E*,* *\\)\\([0-9]*\\), \\([0-9]*/[0-9]*\\), \\([0-9]*\\), \\([0-9]*\\)",0);
  147.    struct regex *mre=re_compile("^Content-Type: \\([a-zA-Z]*/[a-zA-Z]*\\)",0);
  148.    struct regex *pre=0;
  149.    const char *pridef=0;
  150. /*
  151.    if (pridef=NXGetDefaultValue("Mail","PriorityHeader"))
  152.    {
  153.       static char buf[MAXPATHLEN];
  154.       sprintf(buf,"^%s: [ \t]*\\([^ \t]*\\)[ \t]*\n",pridef);
  155.       pre=re_compile(buf,0);
  156.       pridef=NXGetDefaultValue("Mail","PriorityValues");
  157.    }
  158. */
  159.    mi=malloc(sizeof(*mi));
  160.    
  161.    mi->record_length=sizeof(*mi);
  162.    mi->mes_offset=-1;
  163.    mi->status='*';
  164.    mi->msgtype=' ';
  165.    mi->encrypted=' ';
  166.    mi->sync=' ';
  167.    mi->mes_date=message_current_date();
  168.  
  169.    headerlen=0;
  170.    
  171.    while(fgets(line,LINELEN,stdin))
  172.    {
  173.       if (*line=='\n') break;
  174.       if (re_match(line,fre)==1)
  175.       {
  176.      strpcpy(from,fre->braslist[0],fre->braelist[0]);
  177.      iso_convert(from);
  178.       }
  179.       if (re_match(line,sre)==1)
  180.       {
  181.      strpcpy(subject,sre->braslist[0],sre->braelist[0]);
  182.      iso_convert(subject);
  183.       }
  184.       if (re_match(line,dre)==1)
  185.      mi->mes_date=message_date(atoi(dre->braslist[2])+1900,
  186.                    dre->braslist[1],atoi(dre->braslist[0]));
  187.       if (pre && pridef && (re_match(line,pre)==1))
  188.       {
  189.      const char *beg,*end;
  190.  
  191.      for(beg=pridef,pri=1;*beg;(beg=*end ? end+1 : end),(pri++))
  192.      {
  193.         if (!(end=index(beg,' '))) end=beg+strlen(beg);
  194.         if (((pre->braelist[0]-pre->braslist[0])==(end-beg))
  195.         &&(strncmp(pre->braslist[0],beg,end-beg)==0))
  196.            break;
  197.      }
  198.      if (!*beg) pri=0;
  199.       }
  200.       if ((mi->msgtype==' ') && (re_match(line,mre)==1))
  201.       {
  202.      if (!strpcaseequ("text/plain",mre->braslist[0],mre->braelist[0]))
  203.         mi->msgtype='m';
  204.       }
  205.       if (re_match(line,rre)==1)
  206.       {
  207.      char *c;
  208.      mi->msgtype='r';
  209.      strpcpy(reference,rre->braslist[1],rre->braelist[1]);
  210.      c=reference+strlen(reference);
  211.      *c++='_';
  212.      while(c-reference<22) *c++='_';
  213.      *c='\0';
  214.      sprintf(c,"%ld",time(0));
  215.      strcat(reference,".attach");
  216.      c=reference+strlen(reference);
  217.      strcat(reference,", ");
  218.      if (*(rre->braslist[2])=='E')
  219.      {
  220.         strcat(reference,"E, ");
  221.         mi->encrypted='e';
  222.      }
  223.      strpcat(reference,rre->braslist[4],rre->braelist[4]);
  224.      strcat(reference,"\n");
  225.      appstring(&header,&headerlen,&headermaxlen,"Next-Reference: ",
  226.            strlen("Next-Reference: "));
  227.      appstring(&header,&headerlen,&headermaxlen,reference,strlen(reference));
  228.      *c='\0';
  229.       }
  230.       else
  231.       {
  232.      appstring(&header,&headerlen,&headermaxlen,line,strlen(line));
  233.       }
  234.    }
  235.    appstring(&header,&headerlen,&headermaxlen,"\n",1);
  236.  
  237.    contentlen=0;
  238.    
  239.    while((i=fread(growstring(&content,&contentlen,&contentmaxlen,LINELEN*16),
  240.           1,16*LINELEN,stdin))>0)
  241.       contentlen+=i;
  242.    if (content[contentlen-1]!='\n')
  243.       appstring(&content,&contentlen,&contentmaxlen,"\n",1);
  244.  
  245.    mi->mes_length=headerlen;
  246.    if (mi->msgtype!='r') mi->mes_length+=contentlen;
  247.    mi->record_length+=strlen(from)+1+strlen(subject)+1+strlen(reference)+1;
  248.    mi->record_length+=sizeof(int)+sizeof(time_t)+sizeof(int);
  249.    mi=realloc(mi,mi->record_length);
  250.    strcpy(mi->data,from);
  251.    strcpy(mi->data+strlen(from)+1,subject); 
  252.    strcpy(mi->data+strlen(from)+1+strlen(subject)+1,reference);
  253.  
  254.    message_set_attachsize(mi,0);
  255.    message_set_attachtime(mi,0);
  256.    message_set_priority(mi,pri);
  257.    
  258.    free(fre); free(sre); free(rre); free(dre);
  259.    if (pre) free(pre);
  260.    return mi;
  261. }
  262.  
  263. const char *get_command(const char *defaultKey, ...)
  264. {
  265.    /* Find command named `defaultKey' in Mail's defaults database, and check
  266.       if it is an executable.  If not, try each of the defaults given on
  267.       the (NULL-terminated) argument list in turn. */
  268.    const char *command = NULL; //NXGetDefaultValue("Mail", defaultKey);
  269.    va_list ap;
  270.  
  271.    va_start(ap/*, defaultKey*/);
  272.    do
  273.    {
  274.       struct stat st;
  275.       if (command == NULL) continue;
  276.       if (command[0] != '/') break; /* XXX should check PATH? */
  277.       if (access(command, X_OK) < 0) continue;
  278.       if (stat(command, &st) < 0) continue;
  279.       if ((st.st_mode & S_IFMT) == S_IFREG) break;
  280.    }
  281.    while ((command = va_arg(ap, const char *)) != NULL);
  282.    va_end(ap);
  283.    if (command == NULL)
  284.    {
  285.       fprintf(stderr,"%s: cannot find executable for `%s'.\n", progname(), defaultKey);
  286.    }
  287.    return command;
  288. }
  289.  
  290. void main(int ac,char *av[])
  291. {
  292.    struct message_index *mi;
  293.    const char *decodeCmd=0;
  294.    const char*uncompressCmd=0;
  295.    const char *tarCmd=0;
  296.    int c;
  297.    int readflg=0,nowaitflg=0,verboseflg=0,multiflg=0,flagflg=0,deleteflg=0;
  298.    int pri=-1;
  299.    time_t mboxtime;
  300.    int mfd=-1,tocfd=-1;
  301.    FILE *tocf=NULL;
  302.    struct table_of_contents_header *toch=0;
  303.    int status=EXIT_SUCCESS;
  304.  
  305.    set_progname(av[0]);
  306.  
  307.    while((c=getopt(ac,av,"rfdvnp:VH"))!=EOF) switch(c)
  308.    {
  309.     case 'r':
  310.       readflg++;
  311.       if (flagflg||deleteflg) status=EXIT_USAGE;
  312.       break;
  313.     case 'f':
  314.       flagflg++;
  315.       if (readflg||deleteflg) status=EXIT_USAGE;
  316.       break;
  317.     case 'd':
  318.       deleteflg++;
  319.       if (readflg||flagflg) status=EXIT_USAGE;
  320.       break;
  321.     case 'n':
  322.       nowaitflg++;
  323.       break;
  324.     case 'v':
  325.       verboseflg++;
  326.       break;
  327.     case 'm': // XXX currently unused.
  328.       multiflg++;
  329.       break;
  330.     case 'p':
  331.       if (pri!=-1) status=EXIT_USAGE;
  332.       pri=atoi(optarg);
  333.       if (pri<0) status=EXIT_USAGE;
  334.       break;
  335.     case 'V':
  336.       status=EXIT_VERSION;
  337.       break;
  338.     case 'H':
  339.       status=EXIT_HELP;
  340.       break;
  341.     case '?':
  342.     default:
  343.       status=EXIT_USAGE;
  344.    }
  345.    if (status==EXIT_SUCCESS && optind>=ac) status=EXIT_USAGE;
  346.    handle_usage_help_version(status, USAGE, HELP);
  347.  
  348.    mi=readmail();
  349.  
  350.    if (!header || (headerlen<5) || strncmp(header,"From ",5))
  351.    {
  352.       fprintf(stderr,"%s: message is not in UNIX mailbox format\n", progname());
  353.       exit(EXIT_FAILURE);
  354.    }
  355.  
  356.    if (readflg) mi->status=' ';
  357.    else if (flagflg) mi->status='+';
  358.    else if (deleteflg) mi->status='d';
  359.  
  360.    if (pri>=0) message_set_priority(mi,pri);
  361.    
  362.    for(;optind<ac;optind++)
  363.    {
  364.       if (verboseflg) fprintf(stderr,"Entering %s...\n",av[optind]);
  365.       if (cd_mbox(av[optind],1)) goto next;
  366.       if (lock_mbox(nowaitflg)) goto next;
  367.  
  368.       mboxtime=mtime("mbox");
  369.       
  370.       if ((mfd=open("mbox",O_WRONLY|O_APPEND|O_CREAT,0644))==-1)
  371.       {
  372.      fprintf(stderr,"%s: %s: %s\n",progname(),av[optind],strerror(errno));
  373.      status=EXIT_FAILURE;
  374.      goto unlock;
  375.       }
  376.  
  377.       /* Even if we cannot update the table of contents,
  378.          we'll try our damnedest to deliver the message anyway. */
  379.  
  380.       if ((tocfd=open("table_of_contents",O_CREAT|O_RDWR,0644))==-1 ||
  381.       (tocf=fdopen(tocfd,"r+b"))==NULL)
  382.       {
  383.      fprintf(stderr,"%s: %s: %s\n",progname(),av[optind],strerror(errno));
  384.      status=EXIT_FAILURE;
  385.      //goto unlock;
  386.       }
  387.       else
  388.       {
  389.      toch=get_table_of_contents_header(tocf,1);
  390.      if (!toch)
  391.      {
  392.         fprintf(stderr,"%s: %s: warning: invalid table_of_contents\n",progname(),av[optind]);
  393.         status=EXIT_FAILURE;
  394.      }
  395.      else if (toch->mbox_time!=mboxtime)
  396.      {
  397.         fprintf(stderr,"%s: %s: warning: table_of_contents out of sync\n",progname(),av[optind]);
  398.         status=EXIT_FAILURE;
  399.      }
  400.       }
  401.  
  402.       /* FOR EACH */
  403.       lseek(mfd,0,SEEK_END);
  404.       mi->mes_offset=lseek(mfd,0,SEEK_CUR);
  405.  
  406.       if (mi->msgtype=='r')
  407.       {
  408.      char path[MAXPATHLEN];
  409.      FILE *f;
  410.  
  411.      /* Locate executables, if not already done so. */
  412.      if (!decodeCmd && !(decodeCmd=get_command("appnmailDecodeCommand","/NextApps/Mail.app/decode",NULL)))
  413.      {
  414.         /* ...not really a regular Mail default. */
  415.         status=EXIT_FAILURE;
  416.         goto unlock;
  417.      }
  418.      if (!uncompressCmd && !(uncompressCmd=get_command("UncompressCommand","/usr/bin/gunzip","/usr/ucb/uncompress",NULL)))
  419.      {
  420.         status=EXIT_FAILURE;
  421.         goto unlock;
  422.      }
  423.      if (!tarCmd && !(tarCmd=get_command("TarCommand","/NextApps/Mail.app/safetar","/usr/bin/gnutar",NULL)))
  424.      {
  425.         status=EXIT_FAILURE;
  426.         goto unlock;
  427.      }
  428.      strcpy(path,message_reference(mi));
  429.      if ((mkdir(path,0755))==-1)
  430.      {
  431.         fprintf(stderr,"%s: %s: %s: %s\n",progname(),av[optind],path,strerror(errno));
  432.         status=EXIT_FAILURE;
  433.         goto unlock;
  434.      }
  435.      sprintf(line,"cd %s && %s | %s | %s xf -",path,decodeCmd,uncompressCmd,tarCmd);
  436.      if (!(f=popen(line,"w")))
  437.      {
  438.         fprintf(stderr,"%s: %s: %s: %s\n",progname(),av[optind],path,strerror(errno));
  439.         status=EXIT_FAILURE;
  440.         goto unlock;
  441.      }
  442.      fwrite(content,contentlen,1,f);
  443.      if ((c=pclose(f))!=0)
  444.      {
  445.         fprintf(stderr,"%s: %s: %s failed (status %d)\n",progname(),av[optind],line,c);
  446.         status=EXIT_FAILURE;
  447.         goto unlock;
  448.      }
  449.          message_set_attachsize(mi,dirsize(path));
  450.          message_set_attachtime(mi,mtime(path));
  451.      /* Add message body *after* all failure points, to avoid TOC corruption. */
  452.      write(mfd,header,headerlen);
  453.          write(mfd,"\n",1);
  454.      // XXX should check status.
  455.       }
  456.       else
  457.       {
  458.      write(mfd,header,headerlen);
  459.          write(mfd,content,contentlen);
  460.      // XXX should check status.
  461.       }
  462.       if (toch)
  463.       {
  464.      toch->num_msgs++;
  465.      fseek(tocf,0,SEEK_END);
  466.      put_message_index(tocf,mi);
  467.       }
  468.       /* END FOR EACH */
  469.       
  470.       close(mfd), mfd=-1;
  471.       if (toch)
  472.       {
  473.      if (toch->mbox_time==mboxtime) toch->mbox_time=mtime("mbox");
  474.      fseek(tocf,0,SEEK_SET);
  475.      put_table_of_contents_header(tocf,toch);
  476.       }
  477.     unlock:
  478.       if (tocf!=NULL) fclose(tocf), tocf=NULL;
  479.       else if (tocfd>=0) close(tocfd), tocfd=-1;
  480.       if (mfd>=0) close(mfd), mfd=-1;
  481.       unlock_mbox();
  482.     next:;
  483.       if (toch) { free(toch); toch=0; }
  484.       uncd_mbox();
  485.    }
  486.    exit(status);
  487. }
  488.