home *** CD-ROM | disk | FTP | other *** search
/ OS/2 Shareware BBS: 35 Internet / 35-Internet.zip / qmail11.zip / qypmail.cpp < prev    next >
C/C++ Source or Header  |  1997-03-11  |  14KB  |  542 lines

  1. /*
  2.   Module:  QYPMAIL.CPP
  3.  
  4.   Author:  William A. Rossi
  5.            194 Peck St.
  6.            Franklin, MA 02038
  7.  
  8.            bill@rossi.com
  9.  
  10.   Version: 1.1
  11.  
  12.   Release Date: 11-MAR-1997
  13.  
  14.   Description:  QYPMAIL is a local delivery agent for OS/2 SENDMAIL.
  15.                 It has been tested under TCP/IP version 3.0 and 3.5,
  16.                 and should work under TCP/IP version 2.0.  It is
  17.                 designed to be interoperable with LAMAIL, and the
  18.                 various shareware utilities that are also LAMAIL 
  19.                 compatible.
  20.  
  21.   *********************************************************************
  22.  
  23.   The code contained in this file is in the public domain. Specifically,
  24.   I give to the public domain all rights for future licensing of the 
  25.   source code, all resale rights, and all publishing rights. 
  26.  
  27.   If you find this code useful, or have any comments I can be reached by
  28.   email and snail mail at the above addresses.
  29.  
  30.   Disclaimer of Warranty
  31.  
  32.   THIS PACKAGE IS RELEASED "AS IS" WITHOUT WARRANTY OF ANY KIND, EITHER 
  33.   EXPRESSES OR IMPLIED, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED 
  34.   WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE.
  35.   YOU ASSUME THE ENTIRE RISK OF USING THE PROGRAM AND THE COST OF ALL 
  36.   NECESSARY SERVICING, REPAIR OR CORRECTION.
  37.  
  38.   IN NO EVENT UNLESS REQUIRED BY APPLICABLE LAW OR AGREED TO IN WRITING
  39.   WILL THE AUTHOR BE LIABLE TO YOU FOR DAMAGES, INCLUDING ANY 
  40.   GENERAL, SPECIAL, INCIDENTAL OR CONSEQUENTIAL DAMAGES ARISING OUT OF 
  41.   THE USE OR INABILITY TO USE THE PROGRAM (INCLUDING BUT NOT LIMITED
  42.   TO LOSS OF DATA OR DATA BEING RENDERED INACCURATE OR LOSSES SUSTAINED 
  43.   BY YOU OR THIRD PARTIES OR A FAILURE OF THE PROGRAM TO OPERATE WITH 
  44.   ANY OTHER PROGRAMS), EVEN IF THE AUTHOR HAS BEEN ADVISED OF THE 
  45.   POSSIBILITY OF SUCH DAMAGES.
  46.  
  47. */
  48.  
  49.  
  50. #define INCL_DOSDATETIME
  51. #define INCL_DOSPROCESS
  52. #define INCL_DOSFILEMGR
  53. #include <os2.h>
  54.  
  55. #include <stdio.h>
  56. #include <stdlib.h>
  57. #include <time.h>
  58. #include <string.h>
  59.  
  60. #ifdef TEST
  61. #define MAIL_SOURCE "D:\\CFILES\\QYPMAIL\\TESTMAIL.TXT"
  62. #endif      
  63.  
  64. /* Class to manage mail delivery */
  65.  
  66. class MailData
  67. {
  68.   private:
  69.   void ReadSettings(char **Settings); /* Parse settings in config file */
  70.   void SetMailFname();       /* Create a file name to store mail in */
  71.   public:
  72.   char MailFname[20];
  73.   char MailPath[CCHMAXPATH]; /* Directory to deposit mail in */
  74.   char UpdateInbox;          /* Set to true if inbox needs updating */
  75.   char Command[256];         /* Optional command string */
  76.   
  77.   /* Stuff for building INBOX.NDX for LAMAIL compatibility   */
  78.   char FromUser[9];          /* User mail is from            */
  79.   char FromNode[9];          /* Node mail is from            */
  80.   char MailDate[9];          /* Date received                */
  81.   char MailTime[6];          /* Time received                */
  82.   char RealName[128];        /* User's real name             */
  83.   char Subject[128];         /* Subject line                 */
  84.   char FromAddr[128];        /* From address                 */
  85.  
  86.   ULONG GetUserSettings(char *UserName); /* Read a users settings from cfg file */
  87.   ULONG ReadMail();  /* Read in the actual mail and process it */
  88. };
  89.  
  90. /* Globals */
  91.  
  92. char *EtcPath;
  93.  
  94.  
  95. /* void Pad(char *str, int n)
  96.  *
  97.  * Pads a string str to length n by adding space characters.
  98.  *
  99.  */
  100.  
  101. void Pad(char *str, int n)
  102. {
  103.   int len, i;
  104.   char *p;
  105.  
  106.   len = strlen(str);
  107.   p = str+len;
  108.   for (i=0; i<(n-len); i++)
  109.     *p++=' ';
  110.   *p = 0;
  111. }
  112.  
  113. /* void ClearHiddenBit(HFILE fh)
  114.  *
  115.  * Clears the hidden attribute 
  116.  *
  117.  */
  118.  
  119. ULONG ClearHiddenBit(HFILE fh)
  120. {
  121.   FILESTATUS3 Status;
  122.   ULONG rc;
  123.  
  124.   rc = DosQueryFileInfo(fh, FIL_STANDARD, &Status, sizeof(Status));
  125.   Status.attrFile &= ~(ULONG)FILE_HIDDEN;
  126.   rc = DosSetFileInfo(fh, FIL_STANDARD, &Status, sizeof(Status));
  127.   return rc;
  128. }
  129.  
  130. /* ULONG MailData::ReadMail()
  131.  *
  132.  * Reads in an email message and processes it according to the current
  133.  * settings.
  134.  *
  135.  */
  136.  
  137. ULONG MailData::ReadMail()
  138. {
  139.   FILE *InputFP;                    /* Input file pointer             */
  140.   HFILE OutputFH;                   /* Output file handle             */
  141.   char MailLine[256];               /* Line of input mail text        */
  142.   char OutputFname[CCHMAXPATH];     /* Name of the output file        */
  143.   DATETIME DateTime;                /* Current date and time          */
  144.   char DidSubject = 0, DidFrom = 0; /* Flags indicating if we parsed From and Subject headers */
  145.   ULONG rc;                         /* Return code                    */
  146.   ULONG Action;                     /* Used by DosOpen                */
  147.   ULONG BytesWritten;               /* Used by DosWrite               */
  148.  
  149. #ifdef TEST
  150.   InputFP = fopen(MAIL_SOURCE, "r");
  151. #else
  152.   InputFP = stdin;                  /* Read mail from stdin */
  153. #endif
  154.  
  155.   /* Create the output file name by adding the MailFname to the config
  156.      file specifed MailPath */
  157.  
  158.   strcpy(OutputFname, MailPath);
  159.   strcat(OutputFname, "\\");
  160.   strcat(OutputFname, MailFname);
  161.  
  162.   /* Open the output file for writing.  Use DosOpen so that we can
  163.      open the file with the hidden attribute set.  This prevents 
  164.      other programs like a POP3 server from seeing the file before
  165.      we are done with it. */
  166.   
  167.   rc = DosOpen(OutputFname,                     /* File name          */
  168.                &OutputFH,                       /* Output file handle */
  169.                &Action,                         /* Action taken       */
  170.                0,                               /* File size          */
  171.                FILE_HIDDEN,                     /* File Attribute     */
  172.                OPEN_ACTION_CREATE_IF_NEW |      /* Open flags         */
  173.                  OPEN_ACTION_REPLACE_IF_EXISTS,
  174.                OPEN_SHARE_DENYREADWRITE |       /* Open mode          */
  175.                  OPEN_ACCESS_WRITEONLY,
  176.                NULL);                           /* EA buffer          */
  177.  
  178.   if (rc)   /* Return to caller if an error */
  179.     return 1;
  180.  
  181.   /* Read in the mail -- one line at a time from the input file */
  182.  
  183.   while (fgets(MailLine, 256, InputFP))
  184.   {
  185.     /* Replace trailing '\n' with "\r\n" */
  186.  
  187.     if (MailLine[strlen(MailLine)-1] == '\n')
  188.     {
  189.       MailLine[strlen(MailLine)-1] = 0;
  190.       strcat(MailLine, "\r\n");
  191.     }
  192.  
  193.     /* Write the mail line out to the output file */
  194.     DosWrite(OutputFH, MailLine, strlen(MailLine), &BytesWritten); 
  195.  
  196.     /* Process the From: header when we come across it */
  197.     if (strncmp(MailLine, "From:", 5) == 0 && !DidFrom)
  198.     {
  199.       char *p, *q, *id;
  200.       int i;
  201.  
  202.       DidFrom = 1;     /* Note that we have already processed the From line */
  203.       *FromAddr = 0;
  204.       *FromUser = 0;
  205.       *FromNode = 0;
  206.  
  207.       /* Find the from address.  Do this by searching for the '@' character
  208.          and then working backwards towards a whitespace character, then
  209.          we will have the user name */
  210.  
  211.       p = strstr(MailLine, "@");  /* Search for '@' */
  212.       if (p)
  213.       {
  214.         while (p!=MailLine && *p != ' ')
  215.           p--;
  216.         if (*p == ' ')
  217.           p++;
  218.     
  219.         id = p;
  220.         if (*p == '<')
  221.           p++; 
  222.         q = FromAddr;        /* Copy the From address */ 
  223.         while ( *p > ' ')
  224.           *q++ = *p++;
  225.         *q = 0;
  226.         if (*(q-1) == '>')
  227.           *(q-1) = 0;
  228.  
  229.         p = id;             /* Get the from user */
  230.         if (*p == '<')
  231.           p++;
  232.         q = FromUser;
  233.         i = 0;
  234.         while ( *p != '@')
  235.         {
  236.           *q++ = *p++;
  237.           i++;
  238.           if (i>7)
  239.             break;
  240.         }
  241.         *q = 0;
  242.  
  243.         p++;
  244.         q = FromNode;       /* Get the from node */
  245.         i = 0;
  246.         while ( *p != '.' && *p > ' ')
  247.         {
  248.           *q++ = *p++;
  249.           i++;
  250.           if (i>7)
  251.             break;
  252.         }
  253.         *q = 0;
  254.       }
  255.  
  256.  
  257.       /* Try to get the real name.  Assuming that the address is 
  258.          of the form: RealName <user@domain> */
  259.  
  260.       strcpy(RealName, FromAddr);  /* Get the from address */
  261.       p = strstr(MailLine, "<"); 
  262.       if (p)
  263.       {
  264.         *p = 0;
  265.         strcpy(RealName, MailLine+6);
  266.       }
  267.     }
  268.  
  269.     /* Process the Subject: header line when we come across it */
  270.  
  271.     if (strncmp(MailLine, "Subject:", 8) == 0 && !DidSubject)
  272.     {
  273.       char *p;
  274.  
  275.       DidSubject = 1; 
  276.       for (p=MailLine; *p; p++)
  277.         if (*p < ' ')
  278.           *p = 0;
  279.  
  280.       strncpy(Subject, MailLine+9, 100);
  281.     }
  282.   } /* end while() */
  283.  
  284.   /* Make sure we end the mail with a blank line */
  285.  
  286.   strcpy(MailLine, "\r\n");
  287.   DosWrite(OutputFH, MailLine, strlen(MailLine), &BytesWritten); 
  288.  
  289.  
  290.   DosResetBuffer(OutputFH);  /* Flush the file */
  291.   ClearHiddenBit(OutputFH);  /* Clear the hidden bit */ 
  292.   DosClose(OutputFH);   /* Close the output mail file */
  293.  
  294.   /* Update the LAMAIL style inbox.ndx file if the config file says
  295.      we should */
  296.  
  297.   if (UpdateInbox)
  298.   {
  299.     FILE *InboxFP;
  300.     char InboxFname[CCHMAXPATH];
  301.     int i;
  302.  
  303.     DosGetDateTime(&DateTime);
  304.     sprintf(MailDate, "%2.2d/%2.2d/%2.2d", DateTime.year%100, DateTime.month, DateTime.day);
  305.     sprintf(MailTime, "%2.2d:%2.2d", DateTime.hours, DateTime.minutes);
  306.  
  307.     strcpy(InboxFname, MailPath);
  308.     strcat(InboxFname, "\\INBOX.NDX");
  309.   
  310.     /* Inbox file may be temporarily locked by a mail reader program.
  311.        Attempt to open it 5 times then fail */ 
  312.     for (i=0; i<5; i++)
  313.     {
  314.       InboxFP = fopen(InboxFname, "a");
  315.       if (InboxFP)
  316.         break;
  317.       DosSleep(1000);  /* Wait one second */
  318.     }
  319.  
  320.     if (InboxFP == NULL)
  321.       return 1;
  322.    
  323.     Pad(FromUser, 8);
  324.     Pad(FromNode, 8);
  325.     MailFname[8] = 0; 
  326.     fprintf(InboxFP, "         %s %s %s %s              %s %s                %s"
  327.                      "\001%s\001\001%s\n", FromUser, FromNode, MailFname,
  328.                      MailFname+9, MailDate, MailTime, RealName, Subject,
  329.                      FromAddr);
  330.     MailFname[8] = '.';
  331.     fclose(InboxFP); 
  332.   }
  333.  
  334. #ifdef TEST
  335.   printf("From User: %s\n", FromUser);
  336.   printf("From Node: %s\n", FromNode);
  337.   printf("Real Name: %s\n", RealName);
  338.   printf("Subject:   %s\n", Subject);
  339.   printf("From Addr: %s\n", FromAddr);
  340.   printf("Mail Date: %s\n", MailDate);
  341.   printf("Mail Time: %s\n", MailTime);
  342. #endif
  343.  
  344.   if (*Command)   /* If there is a command  to be issued */
  345.   { 
  346.     char CommandStr[256];
  347.     char *p, *q;
  348.  
  349.     p = Command;
  350.     q = CommandStr;
  351.  
  352.     while (*p)
  353.     {
  354.       if (*p == '%')
  355.       {
  356.         switch (p[1]) 
  357.         {
  358.           case 'N':  /* Macro for mail file name */
  359.             *q = 0;
  360.             strcpy(q, OutputFname);
  361.             q = q+strlen(q);
  362.             p+=2;
  363.             break;
  364.      
  365.           case 'F':  /* Macro for from address */
  366.             *q = 0;
  367.             strcpy(q, FromAddr);
  368.             q = q+strlen(q);
  369.             p+=2;
  370.             break;
  371.  
  372.           default:
  373.             *q++ = *p++;
  374.             *q++ = *p++;
  375.             break;
  376.         }
  377.       }
  378.       else
  379.       {
  380.         *q++ = *p++;
  381.       }
  382.     }
  383. #ifdef TEST
  384.     printf("Command: %s\n", CommandStr);
  385. #endif
  386.     system(CommandStr);   /* Do the command */
  387.   }
  388.  
  389.   return 0;
  390. }  
  391.  
  392. void MailData::SetMailFname()
  393. {
  394.   char Base36Set[] = "ABCDEFGHIJKLMNOPQRSTUVWXY"
  395.              "Z0123456789";
  396.   int i = 0;
  397.   time_t Time;
  398.   DATETIME DateTime;
  399.  
  400.   Time = time(NULL);
  401.   DosGetDateTime(&DateTime);
  402.  
  403.   MailFname[0] = Base36Set[DateTime.hundredths/3];
  404.  
  405.   for (i=0; i<7; i++)
  406.   {
  407.     MailFname[i+1] = Base36Set[Time%36];
  408.     Time = Time / 36;
  409.   }
  410.   MailFname[8] = 0;
  411.   strcat(MailFname, ".MA_");
  412. }
  413.  
  414.  
  415. void MailData::ReadSettings(char **Settings)
  416. {
  417.   strcpy(MailPath, Settings[1]);  /* Get Mail path */
  418.   strupr(Settings[2]);
  419.   if (*Settings[2] == 'Y')
  420.     UpdateInbox = 1;
  421.   else
  422.     UpdateInbox = 0;
  423.  
  424.   strcpy(Command, Settings[3]);
  425. }
  426.  
  427.  
  428. ULONG MailData::GetUserSettings(char *UserName)
  429. {
  430.   char CfgFname[CCHMAXPATH];
  431.   char User[80];
  432.   char buf[80];
  433.   char MatchFound = 0;
  434.   char *p;
  435.   char *Settings[4];
  436.   int i;
  437.   FILE *CfgFP;
  438.   
  439.   /* Get the user name */
  440.  
  441.   strcpy(User, UserName);
  442.   strupr(User);
  443.  
  444.   /* Open the configuration file */
  445.   strcpy(CfgFname, EtcPath);
  446.   strcat(CfgFname, "\\QYPMAIL.CFG");
  447.  
  448.   CfgFP = fopen(CfgFname, "r");
  449.   if (CfgFP == NULL)
  450.     return 1;
  451.  
  452.   while (fgets(buf, 80, CfgFP))
  453.   {
  454.     for (p=buf; *p; p++)   /* Remove trailing '\n' and '\r' chars */
  455.       if (*p == '\n' || *p == '\r')
  456.       {
  457.         *p = 0;
  458.         break;
  459.       }
  460.  
  461.     /* Remove trailing spaces */
  462.     for (p=buf+strlen(buf)-1; *p==' '; p--)
  463.       *p = 0;
  464.  
  465.     /* Break up config line into fields.  Line is comma delimetied */
  466.     p = strstr(buf, ",");
  467.     i = 0;
  468.     Settings[0] = buf;
  469.  
  470.     while (p != NULL)  /* Replace commas with NULLs */
  471.     {
  472.       *p++ = 0;
  473.       i++;
  474.       Settings[i] = p;
  475.       p = strstr(p, ",");
  476.     }
  477.  
  478.     if (i != 3)   /* If all fields not specified */
  479.       continue;
  480.  
  481.     strupr(Settings[0]);  /* First setting is the user name */
  482.     if (strcmp(Settings[0], User) == 0)
  483.     {
  484.       MatchFound = 1;
  485.       ReadSettings(Settings);
  486.     }
  487.  
  488.     if (strcmp(Settings[0], "DEFAULT") == 0 && !MatchFound)
  489.       ReadSettings(Settings);
  490.   }
  491.  
  492.   fclose(CfgFP);  /* Close the configuration file */
  493.  
  494.   SetMailFname();
  495.  
  496.   return 0;
  497. }
  498.  
  499.  
  500. /* Command line arguments -- Takes two args -- the prog. name, the userid */
  501.  
  502. int main(int argc, char *argv[])
  503. {
  504.   char MailFname[CCHMAXPATH];
  505.   MailData *Mail;
  506.  
  507.   if (argc != 3)
  508.   {
  509.     int i;
  510.     printf("QYPMAIL: Invalid number of arguments\nQYPMAIL: ");
  511.     for (i=0; i<argc; i++)
  512.       printf(" \"%s\" ", argv[i]);
  513.     printf("\n");
  514.     return 1;
  515.   }
  516.  
  517.   /* Get stuff from the environment */
  518.  
  519.   EtcPath = getenv("ETC");
  520.   if (EtcPath == NULL)
  521.   {
  522.     printf("ETC environment variable not defined!!!\n");
  523.     return 1;
  524.   }
  525.  
  526.   Mail = new MailData;
  527.   if (Mail->GetUserSettings(argv[2]))
  528.   {
  529.     delete Mail;
  530.     return 1;
  531.   }
  532.  
  533.   if (Mail->ReadMail())
  534.   {
  535.     delete Mail;
  536.     return 1;
  537.   }
  538.  
  539.   delete Mail;
  540.   return 0;
  541. }
  542.