1 /*# $Id$
2  *****************************************************************************
3  +===========================================================================+
4  | MsgPost/2 Version 2.0a-stable, Nov 2000   (C) 1992 by CodeLand Australia, |
5  |                                                      All Rights Reserved. |
6  | Written by Colin Wheat of Fidonet 3:690/613                               |
7  | Compiled using C SET/2                                                    |
8  |                                                                           |
9  | Modified to use 32-bit Squish API DLL and netmail bug fixed               |
10  | by Wouter Cloetens, 2:292/608.18@fidonet, 81:432/109@os2net               |
11  |                                                                           |
12  | Modified to use the SMAPI, added CHRS kludge,                             |
13  | fixed Y2K problems, ported to DOS, Unix and NT, added JAM support         |
14  | by Tobias Ernst, 2:2476/418@fidonet, tobi@bland.fido.de                   |
15  |                                                                           |
16  | Also includes modifications by:                                           |
17  | - Dmitry Sergienko @ 2:464/30.10,                                         |
18  | - and various members of the Husky project.                               |
19  |                                                                           |
20  |            MsgPost uses the Squish Message Base Level 0 MsgAPI            |
21  |                  Squish is a trademark of Scott J. Dudley                 |
22  |                                                                           |
23  |                                                                           |
24  | COMMAND LINE:                                                             |
25  |                                                                           |
26  |              COMMAND LINE ONLY                                            |
27  | -T<name>     Text source file path & name                                 |
28  | -K           Kill text file after processing                              |
29  | -C<name>     Configuration file path & name                               |
30  | -@<name>     List file name                                               |
31  | -?           Program help screen                                          |
32  |                                                                           |
33  |              CONFIGURATION OVERRIDES                                      |
34  | -M<name>     Message area path & name                                     |
35  | -N<addr>     Netmail format - send to address                             |
36  | -O<addr>     [Zone:]Net/Node[.Point][@Domain]                             |
37  | -P[cfhdkpru] Message priority flag(s)                                     |
38  | -F<fname>    Message addressed to first name                              |
39  | -L<lname>    Message addressed to last name                               |
40  | -W<name>     Message addressed from name                                  |
41  | -J<subj>     Message subject                                              |
42  | -1           First line of text file is subject line                      |
43  | -S<##>       Split long messages to ## Kb size (0-16)                     |
44  | -h<charset>  Specify charset kludge name to use, like "IBMPC" or "LATIN-1"|
45  |                                                                           |
46  | CONFIGURATION FILE:                                                       |
47  |                                                                           |
48  | Address:     [Zone:]Net/Node[.Point][@Domain]                             |
49  | Origin:      <Your system echomail identification>                        |
50  | Area:        <Message area path & name>                                   |
51  | Netmail:     [Zone:]Net/Node[.Point][@Domain]                             |
52  | MsgType:     <Echomail | Conference | Local | Matrix>                     |
53  | To:          <Some Name>                                                  |
54  | From:        <Your Name>                                                  |
55  | Subj:        <Your Subject>                                               |
56  | Attr:        <c|f|h|d|k|p|r|u|l>                                          |
57  | FakeNet:     <###>                                                        |
58  | NoSeenBy:                                                                 |
59  | Split:       <###>                                                        |
60  | Charset:     <charset kludge name>                                        |
61  |                                                                           |
62  | ERROR LEVELS:                                                             |
63  |                                                                           |
64  | 0 - Normal exit                                                           |
65  | 1 - Syntax exit                                                           |
66  | 2 - Out of memory                                                         |
67  | 3 - Configuration or text file not found                                  |
68  | 4 - No system address set                                                 |
69  | 5 - Message base open failed                                              |
70  | 6 - Names list file not found                                             |
71  | 7 - No message area given                                                 |
72  +===========================================================================+
73  ****************************************************************************/
74 
75 #include <stdio.h>
76 #include <stdlib.h>
77 #include <string.h>
78 #include <ctype.h>
79 #include <time.h>
80 #include <fcntl.h>
81 #include <sys/types.h>
82 #include <sys/stat.h>
83 
84 #include <huskylib/compiler.h>
85 
86 #ifdef __OS2__
87 
88 #define INCL_DOSDATETIME
89 #include <os2.h>
90 
91 #ifdef EXPENTRY
92 #undef EXPENTRY
93 #endif
94 
95 #endif
96 
97 #ifdef HAS_UNISTD_H
98 #include <unistd.h>
99 #endif
100 
101 #ifdef HAS_IO_H
102 #include <io.h>
103 #endif
104 
105 #ifdef HAS_SHARE_H
106 #include <share.h>
107 #endif
108 
109 #if defined(__NT__)
110 #define NOGDI
111 #define NOMSG
112 #define WIN32_LEAN_AND_MEAN
113 #include <windows.h>
114 #undef EXPENTRY
115 #endif
116 
117 #if defined(__UNIX__) || defined(__DJGPP__)
118 #define _MAX_DRIVE FILENAME_MAX
119 #define _MAX_DIR   FILENAME_MAX
120 #define _MAX_FNAME FILENAME_MAX
121 #define _MAX_EXT   FILENAME_MAX
122 #endif
123 
124 #ifdef __UNIX__
125 #ifndef O_TEXT
126 #define O_TEXT 0
127 #endif
128 #ifndef S_IREAD
129 #define S_IREAD 0
130 #endif
131 #ifndef S_IWRITE
132 #define S_IWRITE 0
133 #endif
134 #endif
135 
136 #include <smapi/msgapi.h>             /* Squish API header */
137 
138 #include "version.h"
139 
140 char *versionStr;
141 
142 #define MAX_BLOCK   16000       /* Maximum text size */
143 #define MAX_LINE    10000       /* Maximum lines     */
144 
145 #define MSGTYP_ECHO 1           /* Echomail msg type */
146 #define MSGTYP_CONF 2           /* Conf. msg type    */
147 #define MSGTYP_LOCL 3           /* Local msg type    */
148 #define MSGTYP_MATX 4           /* Netmail msg type  */
149 
150 /*
151     Echomail    - DestAddr=NO  PID=NO  Tear=YES Origin=YES SeenBy=YES
152     Conference  - DestAddr=NO  PID=YES Tear=NO  Origin=NO  SeenBy=NO
153     Local       - DestAddr=NO  PID=NO  Tear=YES Origin=NO  SeenBy=NO
154     Matrix      - DestAddr=YES PID=YES Tear=NO  Origin=NO  SeenBy=NO
155 */
156 
157 #define DEF_NADDR {0,0,0,0,{0}}
158 
159 typedef struct _naddr {
160     word zone;
161     word net;
162     word node;
163     word point;
164     char domain[64];
165 } NADDR;
166 
167 char *def_orig = "^KD <Esc> <F2> ^Z ^C (damnit!) q e quit exit !Q system";
168 
169 NADDR sy_addr=DEF_NADDR;        /* System address    */
170 NADDR fm_addr=DEF_NADDR;        /* Origin address    */
171 NADDR to_addr=DEF_NADDR;        /* Destination addr  */
172 unsigned int fakenet=0;         /* System fakenet    */
173 
174 int listflg=0;                  /* Run in list mode  */
175 int killtxtflg=0;               /* Txtfile kill flag */
176 int seenbyflg=1;                /* Seenby line flag  */
177 int addrflg=0;                  /* Address flag      */
178 int msgtyp=MSGTYP_ECHO;         /* Message type      */
179 int split_k=12;                 /* Message split     */
180 unsigned long seed;             /* MSGID seed        */
181 time_t time_now;                /* Creation time     */
182 int mn;                         /* Split unique #    */
183 
184 char exepath[80];               /* Executable path   */
185 char msgpath[80];               /* Message area name */
186 char txtpath[80];               /* Import text file  */
187 char cfgpath[80];               /* MsgPost CFG file  */
188 char lstpath[80];               /* Names list file   */
189 char charset[80];               /* charset kluge     */
190 
191 char str_to[XMSG_TO_SIZE];      /* Message to field  */
192 char str_from[XMSG_FROM_SIZE];  /* Message from fld  */
193 char str_subj[XMSG_SUBJ_SIZE];  /* Message subj fld  */
194 char str_orig[80];              /* Echo origin line  */
195 dword attr=MSGLOCAL;            /* Message attr      */
196 
197 char *lines[MAX_LINE];          /* Line ptr array    */
198 int linescount=0;               /* Line ptr count    */
199 long linesbytes=0L;             /* Total text bytes  */
200 int linesidx;                   /* Line ptr index    */
201 
202 char *textbuf=NULL;             /* Msg text buffer   */
203 long textcount;                 /* Msg text length   */
204 
205 static void  Quit (int status);
206 static int  ReadCfg (void);
207 static int  ReadTxt (void);
208 static int  SetMsgCfg (char *s);
209 static int  Process (MSGA *ap);
210 static void  WriteMsg (MSGA *ap);
211 static int  GetNum (int splitbytes);
212 static int  BuildText (int limit);
213 static void  SetAttr (char *p);
214 static int  ReadOrig (void);
215 static void  BuildTear (char *s);
216 static void  BuildHdr (XMSG *x, int num, int maxnum);
217 static void  BuildCtrl (char *str, int *len, int num, int maxnum);
218 static char *  AddrToStr (NADDR *addr);
219 static unsigned long  HsecTime (void);
220 static void  GetAddr (char *str, NADDR *addr);
221 #ifdef __EMX__
222 static void  AddSlash (char *str);
223 #endif
224 static void  StripSlash(char *str);
225 static void  StripCr (char *str);
226 static void  StrTrim (char *str);
227 static int  StrBlank (char *str);
228 static void  CvtUs (char *s);
229 static int  IsSpace (char c);
230 static char *  FancyStr (char *string);
231 static void  MakeExePath (char *pth);
232 static FILE *  ShFopen (char *name, char *fpmode);
233 static void  SetUp (int argc, char *argv[]);
234 static void  GetCmdLine (int argc, char *argv[]);
235 static void  Usage (void);
236 
main(int argc,char * argv[])237 int main (int argc, char *argv[])
238 {
239     int status=0;               /* Exit status                  */
240     int areatyp;                /* Message area type            */
241     struct _minf mi;            /* API structure                */
242     MSGA *ap;                   /* API area pointer             */
243 
244     versionStr = GenVersionStr( "mpost", VER_MAJOR, VER_MINOR, VER_PATCH,
245                                VER_BRANCH, cvs_date );
246 
247     printf("%s\n", versionStr);
248 
249 /*
250     printf("\nThe Fidonet/Squish/Jam Message Base Writer\n"
251            "(C) Copyright 1992 by CodeLand, 2002-2004 by Husky project.\n"
252            "All Rights Reserved\n");
253 */
254 
255     SetUp(argc,argv);           /* Read initial command line    */
256     if(!ReadCfg()) {            /* Read configuration file      */
257         printf("\n%cWARNING: Configuration file not found!\n\n",0x07);
258         /*        Quit(3); */
259     }
260 
261     if(!ReadTxt()) {       /* Read the message source text file */
262         printf("\n%cERROR: Text file not found!\n\n",0x07);
263         Quit(3);
264     }
265 
266     GetCmdLine(argc,argv);      /* Get command line overrides   */
267 
268     if(!addrflg) {              /* If no system address         */
269         printf("\n%cERROR: No address set!\n\n",0x07);
270         Quit(4);
271     }
272     if(!(*msgpath)) {
273         printf("\n%cERROR: No message base area given!\n\n",0x07);
274         Quit(7);
275     }
276 
277     /* Setup MsgApi */
278     mi.req_version=0;
279     mi.def_zone=(msgtyp==MSGTYP_MATX?sy_addr.zone:fm_addr.zone);
280 
281     /* If no origin set, try from the message area, else use default */
282     if(msgtyp==MSGTYP_ECHO)
283         if(!*str_orig) if(!ReadOrig()) strcpy(str_orig,def_orig);
284 
285     MsgOpenApi(&mi); /* Open the MsgApi */
286     switch(*msgpath)
287     {
288     case '$': areatyp=MSGTYPE_SQUISH; break;
289     case '!': areatyp=MSGTYPE_JAM; break;
290     default:  areatyp=MSGTYPE_SDM;
291     }
292     if(areatyp==MSGTYPE_SDM&&msgtyp!=MSGTYP_MATX) areatyp|=MSGTYPE_ECHO;
293 
294 
295     /* Open the message base */
296     if((ap=MsgOpenArea((byte *)msgpath+(areatyp != MSGTYPE_SDM),
297                        MSGAREA_NORMAL | MSGAREA_CRIFNEC,areatyp)
298        )==NULL) {
299         printf("\n%cERROR: Message base open failed with error code %d!\n\n",
300                0x07, msgapierr);
301         Quit(5);
302     }
303 
304 
305     MsgLock(ap); /* Lock the base */
306 
307     /* Write the message(s) */
308     if((status=Process(ap))!=0) {
309         printf("\n%cERROR: List file not found!\n\n",0x07);
310     }
311 
312     MsgUnlock(ap);
313 
314 
315     MsgCloseArea(ap);    /* Unlock & close the base  */
316 
317 
318     MsgCloseApi();                      /* Close the API            */
319 
320     if(killtxtflg) remove(txtpath);     /* Delete text source file  */
321     Quit(status);
322     return 0; /* remove bogus warnings */
323 }
324 
325 
326 /*
327 ** Quit ()
328 ** Exit the program
329 */
330 
Quit(int status)331 static void  Quit (int status)
332 {
333     /* Release allocated memory */
334     while(linescount) free(lines[--linescount]);
335     if(textbuf) free(textbuf);
336 
337     if(!status) printf("\nDone! (exit 0)\n");
338     exit(status);
339 }
340 
341 
342 /*
343 ** ReadCfg ()
344 ** Read configuration file
345 */
346 
ReadCfg(void)347 static int  ReadCfg (void)
348 {
349     FILE *fp;
350     char line[4098];
351 
352     if((fp=ShFopen(cfgpath,"r"))==NULL) return 0;
353     while(fgets(line,4096,fp)!=NULL) SetMsgCfg(line);
354     fclose(fp);
355 
356     return 1;
357 }
358 
359 
360 /*
361 ** ReadTxt ()
362 ** Read complete text file into line pointer array
363 */
364 
ReadTxt(void)365 static int  ReadTxt (void)
366 {
367     int llen, checkcfg=1, trimleading=1;
368     FILE *fp;
369     char *fbuf=NULL;
370     char line[4098];
371 
372     if(!*txtpath) { /* Allow for a blank msg */
373         lines[linescount]=strdup("");
374         ++linescount;
375         return 1;
376     }
377 
378 
379     if (strcmp(txtpath,"-"))
380     {
381         printf("Reading %s\n", FancyStr(txtpath));
382         if((fp=ShFopen(txtpath,"r"))==NULL) return 0;
383     }
384     else
385     {
386         printf("Reading text from standard input.\n");
387         fp = stdin;
388     }
389     fbuf=(char *)malloc(4096); /* Get extended file buffer */
390     if(fbuf!=NULL) setvbuf(fp,fbuf,_IOFBF,4096);
391 
392     while(fgets(line,4096,fp)!=NULL) {
393 
394         if(checkcfg) { /* Check for override configuration */
395             switch (SetMsgCfg(line))
396             {
397             case  1: continue;
398             case  0: checkcfg = 0; break;
399             case -1: checkcfg = 0; continue;
400             }
401         }
402 
403         StripCr(line); StrTrim(line); /* Clean up */
404 
405         /* Ignore leading blank lines */
406         if(trimleading) {
407             if(StrBlank(line)) continue;
408             else trimleading=0;
409         }
410 
411         strcat(line,"\r"); llen=strlen(line);
412         if((lines[linescount]=(char *)malloc(llen+1))==NULL) {
413             printf("\n%cERROR: Out of memory!\n\n",0x07);
414             if (fp != stdin)
415                 fclose(fp);
416             Quit(2);
417         }
418 
419         memcpy(lines[linescount],line,llen+1);
420         linesbytes+=(long)llen;
421         if(++linescount>=MAX_LINE) break;
422     }
423 
424     if(linescount) {
425         for(;;) { /* Strip trailing blank lines */
426             if(!StrBlank(lines[linescount-1])&&lines[linescount-1][0]!='\r')
427                 break;
428             linesbytes-=(long)strlen(lines[linescount-1]);
429             if(!--linescount) break;
430         }
431     }
432 
433     if (fp != stdin)
434         fclose(fp);
435     if(fbuf) free(fbuf);
436     return 1;
437 }
438 
439 
440 /*
441 ** SetMsgCfg ()
442 ** Set message configuration from configuration string
443 ** Returns TRUE for a valid cfg line processed
444 */
445 
SetMsgCfg(char * s)446 static int  SetMsgCfg (char *s)
447 {
448     char *p, *q, *r, sbuf[4098];
449 
450     strcpy(sbuf,s); /* Clone the line so we don't change the original */
451 
452     if((p=strtok(sbuf," \t\n\r"))!=NULL) {
453         if(*p==';'||*p=='%') return 1;
454         if((q=strtok(NULL,"\n\r"))!=NULL) {
455             r=q; while(*r==' '||*r=='\t') ++r; /* Strip leading */
456             StrTrim(r); /* Strip trailing */
457             if(*r) {
458                 if(!stricmp(p,"TO:")) {
459                     strncpy(str_to,r,XMSG_TO_SIZE-1);
460                     str_to[XMSG_TO_SIZE-1]='\0';
461                     return 1;
462                 }
463                 else if(!stricmp(p,"FROM:")) {
464                     strncpy(str_from,r,XMSG_FROM_SIZE-1);
465                     str_from[XMSG_FROM_SIZE-1]='\0';
466                     return 1;
467                 }
468                 else if(!stricmp(p,"SUBJ:")) {
469                     strncpy(str_subj,r,XMSG_SUBJ_SIZE-1);
470                     str_subj[XMSG_SUBJ_SIZE-1]='\0';
471                     return 1;
472                 }
473                 else if(!stricmp(p,"ORIGIN:")) {
474                     strncpy(str_orig,r,59);
475                     str_orig[59]='\0';
476                     return 1;
477                 }
478                 else if(!stricmp(p,"ADDRESS:")) {
479                     /* System address */
480                     GetAddr(r,&sy_addr);
481                     /* Set default message from address to system address */
482                     fm_addr=sy_addr;
483                     ++addrflg;
484                     return 1;
485                 }
486                 else if(!stricmp(p,"ATTR:")) {
487                     SetAttr(r);
488                     return 1;
489                 }
490                 else if(!stricmp(p,"AREA:")) {
491                     strncpy(msgpath,r,78); msgpath[78]='\0';
492                     StripSlash(msgpath);
493                     return 1;
494                 }
495                 else if(!stricmp(p,"NETMAIL:")) {
496                     GetAddr(r,&to_addr); msgtyp=MSGTYP_MATX;
497                     return 1;
498                 }
499                 else if(!stricmp(p,"MSGTYPE:")) {
500                     if(!stricmp(r,"ECHOMAIL")) {
501                         msgtyp=MSGTYP_ECHO; return 1;
502                     }
503                     else if(!stricmp(r,"CONFERENCE")) {
504                         msgtyp=MSGTYP_CONF; return 1;
505                     }
506                     else if(!stricmp(r,"LOCAL")) {
507                         msgtyp=MSGTYP_LOCL; return 1;
508                     }
509                     else if(!stricmp(r,"MATRIX")) {
510                         msgtyp=MSGTYP_MATX; return 1;
511                     }
512                 }
513                 else if(!stricmp(p,"FAKENET:")) {
514                     fakenet=(unsigned int)atol(r);
515                     return 1;
516                 }
517                 else if(!stricmp(p,"NOSEENBY:")) {
518                     seenbyflg=0;
519                     return 1;
520                 }
521                 else if(!stricmp(p,"SPLIT:")) {
522                     split_k=atoi(r);
523                     if(split_k<0||split_k>16) split_k=12;
524                     return 1;
525                 }
526                 else if (!stricmp(p,"CHARSET:")) {
527                     strncpy(charset,r,50);
528                     return 1;
529                 }
530                 else if ((!stricmp(p,"END")) && (!stricmp(r,"OF CONFIG"))) {
531                     return -1;
532                 }
533             }
534         }
535     }
536 
537     return 0;
538 }
539 
540 
541 /*
542 ** Process ()
543 ** Write out the messages
544 */
545 
Process(MSGA * ap)546 static int  Process (MSGA *ap)
547 {
548     int i=0, oldmsgtyp;
549     FILE *fp;
550     char *p, *q, line[4098];
551 
552     if(listflg) { /* List mode */
553         printf("Reading %s\n",FancyStr(lstpath));
554 
555         if((fp=ShFopen(lstpath,"r"))==NULL) return 6; /* Open list file */
556         oldmsgtyp=msgtyp;
557         for(;;) {
558             if(fgets(line,4096,fp)==NULL) break;
559             p=line; while(*p==' '||*p=='\t') ++p; /* Strip leading */
560             if((q=strchr(p,','))!=NULL) { /* Get optional netmail addr */
561                 GetAddr(q+1,&to_addr); msgtyp=MSGTYP_MATX;
562                 *q='\0';
563             }
564             else msgtyp=oldmsgtyp;
565             StripCr(p); StrTrim(p); /* Strip trailing */
566             if(!StrBlank(p)) {
567                 strncpy(str_to,p,XMSG_TO_SIZE-1);
568                 str_to[XMSG_TO_SIZE-1]='\0';
569                 printf("List message %3d to: %s\n",++i,str_to);
570                 WriteMsg(ap);
571             }
572         }
573         fclose(fp);
574     }
575     else WriteMsg(ap); /* Normal mode */
576 
577     return 0;
578 }
579 
580 
581 /*
582 ** WriteMsg ()
583 ** Write message(s)
584 */
585 
WriteMsg(MSGA * ap)586 static void  WriteMsg (MSGA *ap)
587 {
588     int i;
589     int split_num=1;                /* How many msgs        */
590     int splitbytes;                 /* Split size           */
591     MSGH *msg;                      /* Message pointer      */
592     XMSG xmsg;                      /* Xmsg structure       */
593     int ctrllen;                    /* Control info size    */
594     char ctrl[256];                 /* Control info         */
595     char tear[256];                 /* Tear line, origin    */
596 
597     time_now=time(NULL);            /* Get message time     */
598     seed=HsecTime();                /* Get MSGID seed       */
599 
600     linesidx=0;  /* Start loading from start of lines array */
601 
602     /* Get split messages unique number */
603     srand((unsigned)seed); mn=rand();
604 
605     /* Make tear/origin line for echomail/local */
606     BuildTear(tear);
607 
608     /* Set split message size */
609     if(!split_k) splitbytes=MAX_BLOCK;
610     else splitbytes=split_k*1024;
611     if(splitbytes>MAX_BLOCK) splitbytes=MAX_BLOCK;
612 
613     /* Adjust for tear and header */
614     if(msgtyp==MSGTYP_ECHO||msgtyp==MSGTYP_LOCL) splitbytes-=strlen(tear);
615     splitbytes-=250;
616 
617     /* Split or cut the text */
618     if(split_k&&linesbytes>(long)splitbytes) {
619         split_num=GetNum(splitbytes); /* Compute number of messages */
620 
621         /* Set to average message size */
622         splitbytes=(int)(linesbytes/(long)split_num);
623 
624         for(;;) { /* And adjust size back up so no text is lost */
625             if(GetNum(splitbytes)<=split_num) break;
626             ++splitbytes;
627         }
628     }
629 
630     printf("Writing %s  01/%02u ",FancyStr(msgpath),split_num);
631 
632     for(i=0;i<split_num;i++) {
633         printf("\b\b\b\b\b\b%02u/%02u ",i+1,split_num);
634 
635         if(!BuildText(splitbytes)) break;
636 
637         if(msgtyp==MSGTYP_ECHO||msgtyp==MSGTYP_LOCL) {
638             strcat(textbuf,tear); textcount+=(long)strlen(tear);
639         }
640 
641         if(i) attr&=~MSGFILE; /* Kill file attaches on 2nd, 3rd, etc */
642         BuildHdr(&xmsg,i+1,split_num);
643 
644         msg=MsgOpenMsg(ap,MOPEN_CREATE,0L); /* Create new message */
645         BuildCtrl(ctrl,&ctrllen,i+1,split_num);
646 #if 0
647         MsgWriteMsg(msg,FALSE,&xmsg,NULL,0L,textcount,ctrllen,(byte *)ctrl);
648         MsgWriteMsg(msg,TRUE,NULL,(byte *)textbuf,textcount,textcount,0L,NULL);
649 #else
650                                 /* write in one chunk, so that we can also use
651                                    the JAM API */
652         MsgWriteMsg(msg,FALSE,&xmsg,(byte *)textbuf,textcount,textcount,
653                     ctrllen,(byte *)ctrl);
654 #endif
655         MsgCloseMsg(msg);
656     }
657 
658     printf("\n");
659 }
660 
661 
662 /*
663 ** GetNum ()
664 ** Compute how many split messages to generate
665 */
666 
GetNum(int splitbytes)667 static int  GetNum (int splitbytes)
668 {
669     int i, result=1;
670     long len=0L;
671 
672     for(i=0;i<linescount;i++) { /* Scan through the lines */
673         len+=(long)strlen(lines[i]);
674         if(len>(long)splitbytes) {
675             ++result;
676             len=(long)strlen(lines[i]);
677         }
678     }
679     return result;
680 }
681 
682 
683 /*
684 ** BuildText ()
685 ** Fill the text buffer from the lines array
686 */
687 
BuildText(int limit)688 static int  BuildText (int limit)
689 {
690     int len;
691     char *p;
692 
693     if(!linescount) {
694         *textbuf='\0'; textcount=1L;
695         return 1; /* Allow for blank msg */
696     }
697     if(linesidx>=linescount) return 0;
698 
699     p=textbuf; textcount=0L;
700 
701     for(;;) {
702         len=strlen(lines[linesidx]);
703         if(textcount+(long)len>limit) break;
704         memcpy(p,lines[linesidx],len);
705         p+=len; textcount+=(long)len;
706         if(++linesidx>=linescount) break;
707     }
708     *p='\0'; ++textcount;
709 
710     return 1;
711 }
712 
713 
714 /*
715 ** SetAttr ()
716 ** Set message attribute flags
717 */
718 
SetAttr(char * p)719 static void  SetAttr (char *p)
720 {
721     while(*p) {
722         *p=tolower(*p);
723         switch (*p) {
724         case 'p': /* Private */
725                   attr|=MSGPRIVATE; break;
726         case 'c': /* Crash */
727                   attr|=MSGCRASH; attr&=~MSGHOLD;
728                   break;
729         case 'd': /* Direct */
730                   attr|=MSGXX2;
731                   break;
732         case 'f': /* File attach */
733                   attr|=MSGFILE; break;
734         case 'h': /* Hold */
735                   attr|=MSGHOLD; attr&=~MSGCRASH;
736                   break;
737         case 'k': /* Kill after sending */
738                   attr|=MSGKILL; break;
739         case 'r': /* File Request */
740                   attr|=MSGFRQ; break;
741         case 'u': /* File Update Request */
742                   attr|=MSGURQ; break;
743         case 'l': /* Local flag */
744                   attr&=~MSGLOCAL; /* Turn local flag OFF */
745                   break;
746         }
747         ++p;
748     }
749 }
750 
751 
752 /*
753 ** ReadOrig ()
754 ** Read message area origin file
755 */
756 
ReadOrig(void)757 static int  ReadOrig (void)
758 {
759     FILE *fp;
760     char *p, buf[128];
761 
762     if(*msgpath=='$') {
763         strcpy(buf,msgpath+1); strcat(buf,".SQO");
764     }
765     else if(*msgpath=='!') {
766         strcpy(buf,msgpath+1); strcat(buf,".JOR");
767     }
768     else {
769         strcpy(buf,msgpath); strcat(buf,"\\ORIGIN.");
770     }
771     printf("Reading %s\n",FancyStr(buf));
772 
773     if((fp=ShFopen(buf,"r"))==NULL) return 0;
774     fgets(buf,128,fp);
775     fclose(fp);
776 
777     p=buf; while(*p==' '||*p=='\t') ++p; /* Strip leading */
778     if(strlen(p)>59) p[59]='\0';
779     StripCr(p); StrTrim(p);
780     strcpy(str_orig,p);
781     return 1;
782 }
783 
784 
785 /*
786 ** BuildTear ()
787 ** Make tear, origin, & seenby lines
788 */
789 
BuildTear(char * s)790 static void  BuildTear (char *s)
791 {
792     char line[128];
793 
794     *s='\0';
795     if(msgtyp==MSGTYP_ECHO||msgtyp==MSGTYP_LOCL) {
796             sprintf(line,"\r--- %s\r", versionStr);
797 
798         if(msgtyp==MSGTYP_ECHO) {
799             /* Origin line */
800             sprintf(line," * Origin: %s (%s)\r",str_orig,AddrToStr(&fm_addr));
801             strcat(s,line);
802 
803             /* Seen-by line */
804             if(seenbyflg) {
805                 if(fm_addr.point&&fakenet) { /* Use point's fakenet */
806                     sprintf(line,"SEEN-BY: %u/%u\r",fakenet,fm_addr.point);
807                 }
808                 else sprintf(line,"SEEN-BY: %u/%u\r",fm_addr.net,fm_addr.node);
809                 strcat(s,line);
810             }
811         }
812     }
813 }
814 
815 
816 char *const Months[12] = { "Jan", "Feb", "Mar", "Apr", "May", "Jun",
817                            "Jul", "Aug", "Sep", "Oct", "Nov", "Dec" };
818 
819 
820 /*
821 ** BuildHdr ()
822 ** Set message header information
823 */
824 
BuildHdr(XMSG * x,int num,int maxnum)825 static void  BuildHdr (XMSG *x, int num, int maxnum)
826 {
827     union stamp_combo combo;
828     struct tm *tmdate;
829 
830     memset(x,'\0',sizeof(XMSG));
831 
832     x->attr=attr; /* Set message attributes */
833 
834     /* Set source and destination addresses */
835     x->orig=x->dest=*((struct _netaddr *)&fm_addr);
836     if(msgtyp==MSGTYP_MATX) x->dest=*((struct _netaddr *)&to_addr);
837 
838     strcpy((char *)x->to,str_to); strcpy((char *)x->from,str_from);
839 
840     if(maxnum>1) {
841         sprintf((char *)x->subj,"[%d of %d] ",num,maxnum);
842         strncat((char *)x->subj,str_subj,
843             XMSG_SUBJ_SIZE-1-strlen((char *)x->subj));
844         (x->subj)[XMSG_SUBJ_SIZE-1]='\0';
845     }
846     else strcpy((char *)x->subj,str_subj);
847 
848     tmdate=localtime(&time_now);
849     x->date_written=x->date_arrived=(TmDate_to_DosDate(tmdate,&combo))->msg_st;
850 
851     sprintf((char *) x->__ftsc_date,
852             "%02d %s %02d  %02d:%02d:%02d",
853             x->date_written.date.da,
854             Months[x->date_written.date.mo - 1],
855             (x->date_written.date.yr + 80) % 100,
856             x->date_written.time.hh,
857             x->date_written.time.mm,
858             x->date_written.time.ss << 1);
859 }
860 
861 
862 /*
863 ** BuildCtrl ()
864 ** Set message control information
865 */
866 
BuildCtrl(char * str,int * len,int num,int maxnum)867 static void  BuildCtrl (char *str, int *len, int num, int maxnum)
868 {
869     struct tm *t;
870     unsigned long time_new;
871     char ubuf[20], sbuf[80];
872 
873     *len=0;
874     time_new=HsecTime(); if(seed<time_new) seed=time_new;
875 
876     sprintf(str,"\01MSGID: %s %08lx",AddrToStr(&fm_addr),seed);
877 
878     if(msgtyp==MSGTYP_CONF||msgtyp==MSGTYP_MATX) {
879         sprintf(sbuf,"\01PID: %s", versionStr);
880         strcat(str,sbuf);
881     }
882 
883     sprintf(sbuf,"\01CHRS: %s 2",charset);
884     strcat(str,sbuf);
885 
886     /* ^ASPLIT: 30 Mar 90 11:12:34 @494/4       12345 02/03 +++++++++++ */
887     if(maxnum>1) {
888         t=localtime(&time_now);
889         sprintf(ubuf,"@%u/%u",fm_addr.net,fm_addr.node); ubuf[12]='\0';
890         sprintf(sbuf,"\01SPLIT: %02u %s %02u %02u:%02u:%02u %-12s %05u %02u/%02u +++++++++++",
891             t->tm_mday,Months[t->tm_mon],t->tm_year % 100,
892             t->tm_hour,t->tm_min,t->tm_sec,ubuf,mn,num,maxnum);
893         strcat(str,sbuf);
894     }
895 
896     *len=strlen(str)+1; ++seed;
897 }
898 
899 
900 /*
901 ** AddrToStr ()
902 ** Build address string
903 */
904 
AddrToStr(NADDR * addr)905 static char *  AddrToStr (NADDR *addr)
906 {
907     static char str[256];
908     char point[20], domain[70];
909 
910     if(addr->point) sprintf(point,".%u",addr->point);
911     else *point='\0';
912     if(*(addr->domain)) sprintf(domain,"@%s",addr->domain);
913     else *domain='\0';
914 
915     sprintf(str,"%u:%u/%u%s%s",addr->zone,addr->net,addr->node,point,domain);
916     return str;
917 }
918 
919 
920 /*
921 ** HsecTime ()
922 ** Get system date/time in hsec's
923 ** This function is required to return a unique number for MSGID creation.
924 */
925 
HsecTime(void)926 static unsigned long  HsecTime (void)
927 {
928     unsigned long i,j;
929 
930 #if defined(__OS2__) || defined(__NT__)
931 #ifdef __OS2__
932     DATETIME dt;
933     APIRET rc;
934 #else
935     SYSTEMTIME st;
936 #endif
937 
938     i = j = 0;
939     while (i==j || j == 0)
940     {
941 #ifdef __OS2__
942         rc = DosGetDateTime(&dt);
943         i=((dt.day+(((unsigned)dt.month*3057-3007)/100))*144000L) +
944             (dt.hours*360000L) +                    /* hours today: hsec    */
945             (dt.minutes*6000L) +                    /* minutes: hsec        */
946             (dt.seconds*100L) +                     /* seconds: hsec        */
947             dt.hundredths;                          /* hundreds of seconds  */
948 #else
949         GetSystemTime(&st);
950         i=((st.wDay+(((unsigned)st.wMonth*3057-3007)/100))*144000L) +
951             (st.wHour*360000L) +                    /* hours today: hsec    */
952             (st.wMinute*6000L) +                    /* minutes: hsec        */
953             (st.wSecond*100L) +                     /* seconds: hsec        */
954             (st.wMilliseconds/10);                  /* hundreds of seconds  */
955 #endif
956         if (j == 0)
957         j = i;
958     }
959 #else
960     j = time(NULL);
961     for (i=time(NULL); i==j; i=time(NULL));  /* this is extremely nasty! */
962 #endif
963 
964     return i;
965 }
966 
967 
968 /*
969 ** GetAddr ()
970 ** Get command line system address
971 */
972 
GetAddr(char * str,NADDR * addr)973 static void  GetAddr (char *str, NADDR *addr)
974 {
975     char *p;
976 
977     /* Zone */
978     if((p=strchr(str,':'))!=NULL) {
979         addr->zone=(unsigned int)atol(str); str=p+1;
980     }
981     else addr->zone=sy_addr.zone;
982 
983     /* Net */
984     if((p=strchr(str,'/'))!=NULL) {
985         addr->net=(unsigned int)atol(str); str=p+1;
986     }
987     else addr->net=sy_addr.net;
988 
989     /* Node */
990     addr->node=(unsigned int)atol(str);
991 
992     /* Point */
993     if((p=strchr(str,'.'))!=NULL) {
994         str=p+1; addr->point=(unsigned int)atol(str);
995     }
996     else addr->point=0;
997 
998     /* Domain */
999     if((p=strchr(str,'@'))!=NULL) {
1000         str=p+1; strcpy(addr->domain,str);
1001     }
1002     else *(addr->domain)='\0';
1003 }
1004 
1005 
1006 #ifdef __EMX__
1007 
1008 /*
1009 ** AddSlash (char *str)
1010 ** Add trailing back slash
1011 */
1012 
AddSlash(char * str)1013 static void  AddSlash (char *str)
1014 {
1015     char *p;
1016 
1017     p=str; while(*p) ++p;
1018     if(p!=str)
1019     {
1020        p--;
1021 #if defined(__OS2__) || defined(__NT__) || defined(__DJGPP__)
1022        if(*p!='\\') strcat(str,"\\");
1023 #else
1024        if(*p!='/') strcat(str,"/");
1025 #endif
1026     }
1027 }
1028 
1029 #endif
1030 
1031 
1032 /*
1033 ** StripSlash ()
1034 ** Strip trailing back slash
1035 */
1036 
StripSlash(char * str)1037 static void  StripSlash (char *str)
1038 {
1039     int i;
1040 
1041     for(i=strlen(str)-1;(str[i]=='\\')&&i>=0;i--) ;
1042     str[i+1]='\0';
1043 }
1044 
1045 
1046 /*
1047 ** StripCr ()
1048 ** Strip trailing cr/lf
1049 */
1050 
StripCr(char * str)1051 static void  StripCr (char *str)
1052 {
1053     int i;
1054 
1055     for(i=strlen(str)-1;(str[i]=='\r'||str[i]=='\n')&&i>=0;i--) ;
1056     str[i+1]='\0';
1057 }
1058 
1059 
1060 /*
1061 ** StrTrim ()
1062 ** Strip trailing white space
1063 */
1064 
StrTrim(char * str)1065 static void  StrTrim (char *str)
1066 {
1067     int i;
1068 
1069     for(i=strlen(str)-1;IsSpace(str[i])&&i>=0;i--) ;
1070     str[i+1]='\0';
1071 }
1072 
1073 
1074 /*
1075 ** StrBlank ()
1076 ** Is string blank?
1077 */
1078 
StrBlank(char * str)1079 static int  StrBlank (char *str)
1080 {
1081     for(;*str;str++) {
1082         if(!IsSpace(*str)) return 0;
1083     }
1084     return 1;
1085 }
1086 
1087 
1088 /*
1089 ** CvtUs ()
1090 ** Convert underscore characters to spaces
1091 */
1092 
CvtUs(char * s)1093 static void  CvtUs (char *s)
1094 {
1095     while(*s) { if(*s=='_') *s=' '; ++s; }
1096 }
1097 
1098 
1099 /*
1100 ** IsSpace ()
1101 ** If the character is whitespace
1102 */
1103 
IsSpace(char c)1104 static int  IsSpace (char c)
1105 {
1106     if(c==' '||c=='\t') return 1;
1107     return 0;
1108 }
1109 
1110 
1111 /*
1112 ** FancyStr ()
1113 ** Make It Look Like This
1114 */
1115 
FancyStr(char * string)1116 static char *  FancyStr (char *string)
1117 {
1118     int flag=0;
1119     char *s;
1120 
1121     s=string;
1122 #ifdef __UNIX__
1123     return s;
1124 #endif
1125     while(*string) {
1126         if(isalpha(*string)) {                  /* If alphabetic,     */
1127             if(flag) *string=tolower(*string);  /* already saw one?   */
1128             else {
1129                 flag = 1;                       /* first one, flag it */
1130                 *string=toupper(*string);       /* Uppercase it       */
1131             }
1132         }
1133         else flag=0;                            /* reset alpha flag   */
1134         ++string;
1135     }
1136     return s;
1137 }
1138 
1139 
1140 /*
1141 ** MakeExePath ()
1142 ** Get executable path
1143 */
1144 
MakeExePath(char * pth)1145 static void  MakeExePath (char *pth)
1146 {
1147 #ifdef __EMX__
1148     char drive[_MAX_DRIVE], dir[_MAX_DIR], name[_MAX_FNAME], ext[_MAX_EXT];
1149 
1150     _splitpath(pth,drive,dir,name,ext);
1151     strcpy(exepath,drive); strcat(exepath,dir);
1152     AddSlash(exepath);
1153 #else
1154     char *cp;
1155 
1156     strcpy(exepath, pth);
1157     for (cp = exepath + strlen(exepath); *cp != '/' && *cp != '\\'; cp--)
1158     {
1159         if (cp == exepath)
1160         {
1161             break;
1162         }
1163     }
1164     if (cp == exepath)
1165     {
1166         strcpy(exepath, "./");
1167     }
1168     else
1169     {
1170         *(cp+1) = '\0';
1171     }
1172 #endif
1173 
1174 }
1175 
1176 
1177 /*
1178 ** ShFopen ()
1179 ** Share aware fopen function using SH_DENYNONE attribute
1180 */
1181 
ShFopen(char * name,char * fpmode)1182 static FILE *  ShFopen (char *name, char *fpmode)
1183 {
1184     FILE *fp;           /* Temporary stram pointer */
1185     int fd;             /* Temporary file handle   */
1186     unsigned access=0;  /* Required access         */
1187     unsigned mode=0;    /* Required access mode    */
1188     char *fpm=fpmode;
1189     char c;
1190 
1191     /* Check first mode character */
1192     if((c=*fpmode++) == 'r') { access=O_RDONLY; }
1193     else if(c=='w') { access=O_CREAT|O_WRONLY|O_TRUNC; mode=S_IWRITE; }
1194     else if(c=='a') { access=O_WRONLY|O_CREAT|O_APPEND; mode=S_IWRITE; }
1195     else return NULL;
1196 
1197     /* Check for '+' read/write */
1198     c=*fpmode++;
1199     if(c=='+'||(*fpmode=='+'&&c=='b')) {
1200         if(c=='+') c=*fpmode;
1201         /* Same modes, but both read and write */
1202         access=(access&~(O_WRONLY|O_RDONLY))|O_RDWR;
1203         mode=S_IREAD|S_IWRITE;
1204     }
1205 
1206     /* Set text or binary access */
1207     if('b'==c) { access|=O_BINARY; }
1208     else { access|=O_TEXT; }
1209 
1210     /* Open the file */
1211     if((fd=sopen(name,access,SH_DENYNONE,mode))==-1) return NULL;
1212 
1213     /* Open the stream */
1214     if((fp=fdopen(fd,fpm))==NULL) close(fd);
1215 
1216     return fp; /* Return the stream pointer */
1217 }
1218 
1219 
1220 /*
1221 ** SetUp ()
1222 ** Initial program setup
1223 */
1224 
SetUp(int argc,char * argv[])1225 static void  SetUp (int argc, char *argv[])
1226 {
1227     int i;
1228     int textflg=0;
1229     char *p;
1230 
1231     if(argc<2) Usage();
1232     MakeExePath(argv[0]);
1233 
1234     *str_orig=*msgpath=*txtpath='\0';
1235     sprintf(cfgpath,"%sMPost.Cfg",exepath);
1236     sprintf(lstpath,"%sMPost.Lst",exepath);
1237     strcpy(str_to,"All");
1238     strcpy(str_from, versionStr);
1239     strcpy(str_subj,"Automated Posting");
1240     strcpy(charset,"IBMPC");
1241 
1242     /* Get initial command line */
1243     for(i=1;i<argc;i++) {
1244         p=argv[i];
1245         if(*p=='-'||*p=='/') {
1246             switch(tolower(*(++p))) {
1247                 case 't': strcpy(txtpath,++p);
1248                           ++textflg;
1249                           break;
1250                 case 'c': strcpy(cfgpath,++p);
1251                           break;
1252                 case 'k': killtxtflg=1;
1253                           break;
1254                 case '@': listflg=1; if(*++p) strcpy(lstpath,p);
1255                           break;
1256                 case 'm': /* Process these commands later */
1257                 case 'n':
1258                 case 'p':
1259                 case 'f':
1260                 case 'l':
1261                 case 'j':
1262                 case 'w':
1263                 case 'o':
1264                 case 'h':
1265                 case 's': break;
1266                 case '?':
1267                 default : Usage();
1268             }
1269         }
1270         else Usage();
1271     }
1272 
1273     if(!textflg) {
1274         printf("\n%cERROR: No text file supplied!\n\n",0x07);
1275         Quit(3);
1276     }
1277 
1278     /* Get the text storage */
1279     if((textbuf=malloc(MAX_BLOCK+1024))==NULL) {
1280         printf("\n%cERROR: Out of memory!\n\n",0x07);
1281         Quit(2);
1282     }
1283     memset(textbuf,'\0',MAX_BLOCK+1024);
1284 }
1285 
1286 
1287 /*
1288 ** GetCmdLine ()
1289 ** Get command line parameters
1290 */
1291 
GetCmdLine(int argc,char * argv[])1292 static void  GetCmdLine (int argc, char *argv[])
1293 {
1294     int i, gotsubj=0;
1295     char *p, s[60], f[XMSG_TO_SIZE], l[XMSG_TO_SIZE];
1296 
1297     *f=*l='\0'; /* Clear the name buffers */
1298     for(i=1;i<argc;i++) {
1299         p=argv[i];
1300         if(*p=='-'||*p=='/') {
1301             switch(tolower(*(++p))) {
1302                 case 'h': strcpy(charset,++p);
1303                           break;
1304                 case 'm': strcpy(msgpath,++p); StripSlash(msgpath);
1305                           break;
1306                 case 'n': strcpy(s,++p); GetAddr(s,&to_addr);
1307                           msgtyp=MSGTYP_MATX;
1308                           break;
1309                 case 'p': attr=MSGLOCAL; SetAttr(++p);
1310                           break;
1311                 case 'f': strncpy(f,++p,XMSG_TO_SIZE-1);
1312                           f[XMSG_TO_SIZE-1]='\0';
1313 #ifdef __NT__
1314                           CharToOem(f, f);
1315 #endif
1316                           CvtUs(f);
1317                           break;
1318                 case 'l': strncpy(l,++p,XMSG_TO_SIZE-1);
1319                           l[XMSG_TO_SIZE-1]='\0';
1320 #ifdef __NT__
1321                           CharToOem(l, l);
1322 #endif
1323                           CvtUs(l);
1324                           break;
1325                 case 'j': strncpy(str_subj,++p,XMSG_SUBJ_SIZE-1);
1326                           str_subj[XMSG_SUBJ_SIZE-1]='\0';
1327 #ifdef __NT__
1328                           CharToOem(str_subj, str_subj);
1329 #endif
1330                           ++gotsubj;
1331                           break;
1332                 case 'w': strncpy(str_from,++p,XMSG_FROM_SIZE-1);
1333                           str_from[XMSG_FROM_SIZE-1]='\0';
1334 #ifdef __NT__
1335                           CharToOem(str_from, str_from);
1336 #endif
1337                           CvtUs(str_from);
1338                           break;
1339                 case 'o': strcpy(s,++p); GetAddr(s,&fm_addr);
1340                           if(sy_addr.net==0&&sy_addr.node==0) {
1341                               /* Set system address to message from address */
1342                               sy_addr=fm_addr;
1343                           }
1344                           ++addrflg;
1345                           break;
1346                 case 's': split_k=atoi(++p);
1347                           if(split_k<0||split_k>16) split_k=12;
1348                           break;
1349             }
1350         }
1351     }
1352 
1353     if(gotsubj) {
1354         if( (!(attr&MSGFILE)) &&
1355             (!(attr&MSGFRQ)) &&
1356             (!(attr&MSGURQ))
1357         ) CvtUs(str_subj);
1358     }
1359 
1360     if(*f||*l) { /* Load command line name override */
1361         strncpy(str_to,f,XMSG_TO_SIZE-1);
1362         str_to[XMSG_TO_SIZE-1]='\0';
1363         if(*l&&strcmp(l,"NLN")) { /* Use last name if exist & is valid */
1364             i=XMSG_TO_SIZE-1-strlen(f);
1365             strncat(str_to," ",i); strncat(str_to,l,i-1);
1366             str_to[XMSG_TO_SIZE-1]='\0';
1367         }
1368     }
1369 }
1370 
1371 
1372 /*
1373 ** Usage ()
1374 ** Syntax command line display and exit
1375 */
1376 
Usage(void)1377 static void  Usage (void)
1378 {
1379    puts("\nUsage:  mpost [-switch -switch ... ]\n"
1380         "\nCommand line only:\n"
1381         "\t-T<name>      - Text source file path & name (use - for stdin)\n"
1382         "\t-K            - Kill text file after processing\n"
1383         "\t-C<name>      - Configuration file path & name\n"
1384         "\t-@<name>      - Names list file mode\n"
1385         "\t-?            - Program help screen\n"
1386         "\nConfiguration Overrides:\n"
1387         "\t-M<name>      - Message area path & name\n"
1388         "\t-N<addr>      - Netmail format - send to address\n"
1389         "\t-O<addr>      - Message origin address\n"
1390         "\t-P<cfhdkprul> - Message priority flag(s)\n"
1391         "\t-F<fname>     - Message addressed to first name\n"
1392         "\t-L<lname>     - Message addressed to last name\n"
1393         "\t-W<name>      - Message addressed from name\n"
1394         "\t-J<subj>      - Message subject (NO spaces)\n"
1395         "\t-1            - First line of text file is subject line\n"
1396         "\t-S<##>        - Split long messages to ## Kb size (0-16)\n"
1397         "\t-h<charset>   - Specify charset kludge name to use"
1398     );
1399 
1400     Quit(1);
1401 }
1402