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