1 /*
2      ATP QWK MAIL READER FOR READING AND REPLYING TO QWK MAIL PACKETS.
3      Copyright (C) 1992, 1993, 1997  Thomas McWilliams
4      Copyright (C) 1990  Rene Cougnenc
5 
6      This program is free software; you can redistribute it and/or modify
7      it under the terms of the GNU General Public License as published by
8      the Free Software Foundation; either version 2, or (at your option)
9      any later version.
10 
11      This program is distributed in the hope that it will be useful,
12      but WITHOUT ANY WARRANTY; without even the implied warranty of
13      MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
14      GNU General Public License for more details.
15 
16      You should have received a copy of the GNU General Public License
17      along with this program; if not, write to the Free Software
18      Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
19 */
20 
21 /*
22 qlib.c
23 */
24 
25 #include <stdio.h>
26 #include <fcntl.h>
27 #include <sys/types.h>
28 #include <sys/stat.h>
29 #include <stdlib.h>
30 #include <string.h>
31 #if defined(HAVE_UTIME_H)
32 # include <utime.h>
33 #endif
34 
35 #define _ATP_QLIB_C 1
36 #include "reader.h"
37 #include "readlib.h"
38 #include "ansi.h"
39 #include "qlib.h"
40 #include "makemail.h"
41 #ifdef __LCLINT__
42 #endif
43 
44 /*
45  * StripLSpace - strip white space on left side of string.
46  */
47 void
StripLSpace(char * ptr)48 StripLSpace(char *ptr)
49 {
50     while (/*@i2*/ *ptr == SPC_CHAR || *ptr == TAB_CHAR)
51 	ShiftLeft(ptr, 1);
52 }
53 
54 
55 /*
56  * free_buffer - overwrite old buffer with garbage before discarding.
57  */
58 void
free_buffer(void * s,size_t len)59 free_buffer(void *s, size_t len)
60 {
61     assert(s != NULL);
62     if (s != NULL) {
63 	(void)memset(s, (int)'G', len);
64 	free(s);
65     }
66 }
67 
68 
69 /*
70  * free_string - overwrite an old string with garbage before discarding.
71  */
72 void
free_string(char * s)73 free_string(char *s)
74 {
75     assert(s != NULL);
76     free_buffer(s, strlen(s)+(size_t)1);
77 }
78 
79 
80 /*
81  * do_unlink - unlink file and report any errors via perror().
82  */
83 void
do_unlink(const char * fname)84 do_unlink(const char *fname)
85 {
86     if (unlink(fname) == FAILURE) {
87 	puts("do_unlink(),");
88 	perror(fname);
89 	(void)sleep(3);
90     }
91 }
92 
93 
94 /*
95  * readCnum - convert 13 bit little-endian integer in to ordinary integer.
96  */
97 int
readCnum(const char * ptr)98 readCnum(const char *ptr)
99 {
100     /* only the first 5 bits of the high byte are guaranteed valid */
101     const byte valid_high_bits = (byte)0x1f;
102     byte p, q;
103     unsigned int j;
104 
105     p = (byte) *ptr;
106     ptr++;
107     q = (byte) *ptr;
108     q &= valid_high_bits;
109     j = (unsigned int) q;
110     j <<= 8;
111     j += (unsigned int) p;
112 
113     return (j);
114 }
115 
116 
117 /*
118  * findCindex - find actual index into array so as to locate conference name.
119  */
120 int
findCindex(const int n)121 findCindex(const int n)
122 {
123     int i = LastConf;
124     for (; i >= 0; i--) {
125 	if (ConfNumbers[i] == n)
126 	    break;
127     }
128     return i;
129 }
130 
131 /*
132  * verify_new_file - checks access rights and handles upper case under Unix.
133  */
134 atp_ERROR_T
verify_new_file(char * Src,const char * SrDir,const char * pilgrim)135 verify_new_file(char *Src, const char *SrDir, const char *pilgrim)
136 {
137     atp_ERROR_T ret_code = ATP_OK;
138     sprintf(Src, "%s%c%s", SrDir, SEP, pilgrim);
139     if (access(Src, F_OK) == FAILURE) {
140 #ifdef SYS_UNIX
141 	/* try the upper-case variant of the name */
142 	const size_t path_len = strlen(SrDir);
143 	/*@-returnval */
144 	strupr(Src + path_len + (size_t)1);
145 	if (access(Src, F_OK) == FAILURE) {
146 	    strlwr(Src + path_len + (size_t)1);
147 	    ret_code = ATP_ERROR;
148 	}
149 	/*@=returnval */
150 #else
151 	ret_code = ATP_ERROR;
152 #endif
153     }
154     return ret_code;
155 }
156 
157 /*
158  * WriteIndex - add an index struct to the index file.
159  */
160 atp_ERROR_T
WriteIndex(FILE * wi_fx,const long wi_count,const unsigned long wi_Size,const long wi_Offset)161 WriteIndex(FILE * wi_fx, const long wi_count, const unsigned long wi_Size, const long wi_Offset)
162 {
163     atp_ERROR_T ret_code = ATP_OK;
164     struct MyIndex Yndex;
165 
166     Yndex.LastRead = wi_count ? 0L : VIRGINDX;	/* no count implies new index */
167     Yndex.MaxMsg = 0L;		/* future use */
168     Yndex.MsgNum = wi_count;
169     Yndex.Offset = wi_Offset;
170     Yndex.Size = wi_Size;
171 
172     if (fwrite(&Yndex, index_SIZE, one_record, wi_fx) != one_record) {
173 	/* "error writing index file " */
174 	printf("%s !\n", txt[74]);
175 	(void) sleep(3);
176 	ret_code = ATP_ERROR;
177     }
178     return ret_code;
179 }
180 
181 
182 /*
183  * OpenCon - open a ".cnf" or an "idx"  file for read/write.
184  */
185 atp_ERROR_T
OpenCon(FILE ** oc_pf,FILE ** oc_fs,const char * dspath)186 OpenCon(FILE ** oc_pf, FILE ** oc_fs, const char *dspath)
187 {
188     atp_ERROR_T ret_code = ATP_OK;
189 
190     if (access(dspath, F_OK) == FAILURE ) {	/* Create if not exist */
191 	*oc_pf = fopen(dspath, "wb");
192 	fclose(*oc_pf);
193     }
194     if ((*oc_pf = fopen(dspath, "r+b")) == NULL) {
195 	ret_code = ATP_ERROR;
196 	/* "unable to open file" */
197 	printf("%s %s...\n", txt[51], dspath);
198 	if (*oc_fs != NULL)
199 	    fclose(*oc_fs);
200     }
201     return ret_code;
202 }
203 
204 
205 #if defined(HAVE_FTIME)		/* msdos Borland Turbo C++ */
206 /*
207  * fcopy_ftime, sync destination file time to souce file time.
208  */
209 ATP_INLINE atp_ERROR_T
fcopy_ftime(int dst,int src)210 fcopy_ftime(int dst, int src)
211 {
212     struct ftime dsrctime;
213 
214     getftime(src, &dsrctime);
215     setftime(dst, &dsrctime);
216     return ATP_OK;
217 }
218 
219 #elif defined(HAVE_UTIME)	/* Unix, MSC */
220 
221 /*
222  * fcopy_utime, sync destination file time to souce file time.
223  */
224 ATP_INLINE atp_ERROR_T
fcopy_utime(const char * destin,const char * source)225 fcopy_utime(const char *destin, const char *source)
226 {
227     atp_ERROR_T ret_code = ATP_OK;
228     struct stat srctime;
229     struct utimbuf restime;
230 
231     if (stat(source, &srctime) == FAILURE) {
232 	printf("%s %s\n", txt[49], source);	/* "unable to read file" */
233 	ret_code = ATP_ERROR;
234     } else {
235 	restime.actime = srctime.st_atime;
236 	restime.modtime = srctime.st_mtime;
237 	/*@-returnval */
238 	utime(destin, &restime);
239 	/*@=returnval */
240     }
241     return ret_code;
242 }
243 #endif
244 
245 static const size_t fcopy_buf_size = FCPYBUF;
246 
247 /*
248  * fcopy_loop, the heart of of the fcopy team actually does the copying.
249  */
250 ATP_INLINE atp_ERROR_T
fcopy_loop(int dst,int src,char * buf)251 fcopy_loop(int dst, int src, char *buf)
252 {
253     atp_ERROR_T ret_code = ATP_OK;
254     int rb;
255 
256     for (;;) {
257 	if ((rb = read(src, buf, fcopy_buf_size)) > 0) {
258 	    if (write(dst, buf, (size_t) rb) != rb) {
259 		perror("fcopy_loop() write:");
260 		(void) sleep(1);
261 		ret_code = ATP_ERROR;
262 		break;
263 	    }
264 	} else {
265 	    if (rb < 0) {
266 		perror("fcopy_loop() read:");
267 		(void) sleep(1);
268 		ret_code = ATP_ERROR;
269 	    }
270 	    break;
271 	}
272     }
273     return ret_code;
274 }
275 
276 
277 #ifdef __TURBOC__
278 /* turbo c: suppress unused parameter warnings for next function only */
279 #pragma argsused
280 #endif
281 
282 /*
283  * fcopy_fin, finish the work for fcopy and and then clean up.
284  *  note: the arguments 'source' and 'destin' aren't used when HAVE_FTIME
285  *        is defined. This is not an really error.
286  */
287 ATP_INLINE atp_ERROR_T
fcopy_fin(int dst,int src,const char * destin,const char * source,char * buf)288 fcopy_fin(int dst, int src, const char *destin, const char *source, char *buf)
289 {
290     atp_ERROR_T ret_code;
291 
292     /*@-strictops */
293     if ((ret_code = fcopy_loop(dst, src, buf)) == ATP_OK) { /*@=strictops */
294 #if defined(HAVE_FTIME)		/* msdos Borland Turbo C++ */
295 	ret_code = fcopy_ftime(dst, src);
296 #elif defined(HAVE_UTIME)	/* Unix, MSC */
297 	ret_code = fcopy_utime(destin, source);
298 #endif
299     }
300     (void) close(src);
301     (void) close(dst);
302     return ret_code;
303 }
304 
305 #ifdef __TURBOC__
306 /* turbo c: suppress unused parameter warnings for next function only */
307 #pragma argsused
308 #endif
309 
310 /*
311  * fcopy, fast copy of a file: returns ATP_OK on success ATP_ERROR on error.
312  *  It is helpful to be able to duplicate the source file's time stamp
313  *  too. While not absolutely needed for ATP, there are times when
314  *  it is useful when dealing with mail packets from systems whose time
315  *  clock is out of synch with yours. This is a fine point and if you
316  *  are porting ATP to a new system, you may comment out the time functions
317  *  in fcopy_fin() to ease initial porting with no serious side effects.
318  */
319 static atp_ERROR_T
fcopy(const char * source,const char * destin)320 fcopy(const char *source, const char *destin)
321 {
322 #ifdef SYS_UNIX
323     const mode_t perms = 0666;
324 #else
325     const int perms = (S_IREAD | S_IWRITE);
326 #endif
327     char *buf;
328     int src, dst;
329     atp_ERROR_T ret_code = ATP_ERROR;
330     const int acc = (O_RDWR | O_CREAT | O_TRUNC);
331 
332     if ((buf = (char *) malloc(fcopy_buf_size)) == NULL) {
333 	printf("%s", txt[1]);	/* memory allocation failed */
334     } else {
335 	if ((src = open(source, O_RDONLY)) == FAILURE) {
336 	    printf("%s %s\n", txt[49], source);		/* "unable to read file" */
337 	} else if ((dst = open(destin, acc, perms)) == FAILURE) {
338 	    printf("%s %s\n", txt[50], source);		/* "unable to create file" */
339 	} else {
340 	    ret_code = fcopy_fin(dst, src, destin, source, buf);
341 	}
342 	free_buffer(buf, fcopy_buf_size);
343     }
344     return ret_code;
345 }
346 
347 
348 /*
349  * Work2Home, moves bulletins/news/etc. to atp home BBS directory.
350  */
351 static void
Work2home(const char * DstDir,const char * SrDir,const char * pilgrim)352 Work2home(const char *DstDir, const char *SrDir, const char *pilgrim)
353 {
354     if ( /*@i2 */ *pilgrim != NUL_CHAR && *pilgrim != SPC_CHAR) {
355 	/* Source file Message.DAT */
356 	char Src[MAXPATHS];
357 	if (verify_new_file(Src, SrDir, pilgrim) == ATP_OK) {
358 	    char Dst[MAXPATHS];
359 	    sprintf(Dst, "%s%c%s", DstDir, SEP, pilgrim);
360 	    /* Conference data  file   */
361 	    if (access(Dst, F_OK) == SUCCESS && unlink(Dst) == FAILURE) {
362 		perror("Work2home()");
363 		printf("Can't remove destination %s %c\n", Dst, BELL);
364 		/*@-returnval */
365 		(void) sleep(3);
366 		/*@=returnval */
367 	    }
368 #if defined(HAVE_LINK)
369 	    /* try linking first */
370 	    if (link(Src, Dst) == FAILURE)
371 		/* unix will try copying too */
372 #endif
373 		/*@-strictops */
374 		if (fcopy(Src, Dst) == ATP_ERROR)      /*@=strictops */
375 		{
376 		    printf("Can't link source %s\n", Src);
377 		    printf("Check destination permissions %s\n", Dst);
378 		    exit(EXIT_FAILURE);
379 		}
380 	    do_unlink(Src);
381 	}
382     }
383 }
384 
385 
386 
387 /*
388  * BEGIN READ CONTROL.DAT ROUTINES
389  *
390  */
391 
392 /* maximum number of characters for call to fget() */
393 #define FGET_RD_SIZE 126
394 
395 /* common to all ReadControl() subroutines */
396 static FILE *rdc_fp;
397 
398 /*
399  * RdCn_skiplines, skips and ignores lines in control.dat.
400  */
401 static atp_BOOL_T
RdCn_skiplines(int ct,char * buf)402 RdCn_skiplines(int ct, char *buf)
403 {
404     int i = 0;
405     atp_BOOL_T ret_code = TRUE;
406     assert(ct > 0);
407     for (; i < ct && ret_code; i++)
408 	ret_code = fget(buf, FGET_RD_SIZE, rdc_fp);
409     return ret_code;
410 }
411 
412 /* global data exported by this module */
413 int         LastConf    = -1;  /* index to last conference in arrays */
414 conf_name  *ConfNames   = NULL;
415 int        *ConfNumbers = NULL;
416 atp_BOOL_T *ConfActive  = NULL;
417 
418 
419 static int t_LastConf = -1;
420 static conf_name  *t_ConfNames   = NULL;
421 static int        *t_ConfNumbers = NULL;
422 static atp_BOOL_T *t_ConfActive  = NULL;
423 
424 /*
425  * RdCn_t_invalidate, trash temp data and temp pointers.
426  */
427 static void
RdCn_t_invalidate(void)428 RdCn_t_invalidate(void)
429 {
430     /* invalidate temporary data and pointers */
431     t_LastConf = -1;
432     t_ConfActive = NULL;
433     t_ConfNumbers = NULL;
434     t_ConfNames = NULL;
435 }
436 
437 
438 /*
439  * RdCn_test_failed_malloc, free partial mallocs after failure.
440  */
441 static void
RdCn_test_failed_malloc(atp_BOOL_T success)442 RdCn_test_failed_malloc(atp_BOOL_T success)
443 {
444     if (!success) {
445         if (t_ConfActive != NULL)
446             free(t_ConfActive);
447         if (t_ConfNumbers != NULL)
448             free(t_ConfNumbers);
449         assert(t_ConfNames == NULL);
450         RdCn_t_invalidate();
451         clear();
452         fprintf(stderr, "%s  RdCn_do_malloc()\n", txt[1]);
453     }
454 }
455 
456 
457 /*
458  * RdCn_do_malloc, static helper function for ReadControl().
459  * malloc global arrays
460  */
461 static atp_BOOL_T
RdCn_do_malloc(const int index_to_last_element_of_array)462 RdCn_do_malloc(const int index_to_last_element_of_array)
463 {
464     const size_t array_size = (size_t) (index_to_last_element_of_array + 1);
465     const size_t new_bcnt = array_size * (size_t)sizeof(atp_BOOL_T);
466     const size_t new_icnt = array_size * (size_t)sizeof(int);
467     const size_t new_scnt = array_size * (size_t)CNF_NAM_SIZ;
468     atp_BOOL_T result = FALSE;
469     assert(-1 < index_to_last_element_of_array);
470 
471     if ((t_ConfActive = (atp_BOOL_T *) malloc(new_bcnt)) != NULL)
472 	if ((t_ConfNumbers = (int *) malloc(new_icnt)) != NULL)
473 	    if ((t_ConfNames = (conf_name *) malloc(new_scnt)) != NULL)
474 		result = TRUE;
475 
476     RdCn_test_failed_malloc(result);
477     return result;
478 }
479 
480 /*
481  * RdCn_do_free, de-allocates old arrays used by previous BBS message base.
482  */
483 static void
RdCn_do_free(const int CLast,atp_BOOL_T * CActive,int * CNumbers,conf_name * CNames)484 RdCn_do_free(const int CLast, atp_BOOL_T *CActive, int *CNumbers, conf_name *CNames)
485 {
486     const size_t old_bcnt = (size_t) ((CLast+1) * sizeof(atp_BOOL_T));
487     const size_t old_icnt = (size_t) ((CLast+1) * sizeof(int));
488     const size_t old_scnt = (size_t) ((CLast+1) * CNF_NAM_SIZ);
489     if (CActive != NULL)
490 	free_buffer(CActive, old_bcnt);
491     if (CNumbers != NULL)
492 	free_buffer(CNumbers, old_icnt);
493     if (CNames != NULL)
494 	free_buffer(CNames, old_scnt);
495 }
496 
497 /*
498  * RdCn_get_conf_names, static helper function for ReadControl().
499  */
500 static atp_BOOL_T
RdCn_get_conf_names(char * buf)501 RdCn_get_conf_names(char *buf)
502 {
503     int i, numconf = 0 ;
504     atp_BOOL_T ret_code = TRUE;
505     for (i = 0; i < t_RCONF_IDX; i++) {
506 	if (fget(buf, FGET_RD_SIZE, rdc_fp) && (numconf = atoi(buf)) >= 0
507 	    && fget(t_ConfNames[i], 16, rdc_fp)) {
508 	    StripLSpace(t_ConfNames[i]);
509 	    t_ConfNumbers[i] = numconf;
510 	} else {
511 	    ret_code = FALSE;
512 	    break;
513 	}
514     }
515     /* initialize personal and replies conference name and index */
516     if (ret_code) {
517 	t_ConfNumbers[t_PCONF_IDX] = PERS_CONF;
518 	t_ConfNumbers[t_RCONF_IDX] = REPL_CONF;
519 	/* "personal" */
520 	strncpy(t_ConfNames[t_PCONF_IDX], txt[103], (size_t)(CNF_NAM_SIZ - 1));
521 	/* "replies " */
522 	strncpy(t_ConfNames[t_RCONF_IDX], txt[104], (size_t)(CNF_NAM_SIZ - 1));
523 	*(t_ConfNames[t_PCONF_IDX]+(CNF_NAM_SIZ - 1)) = (char) 0 ;
524 	*(t_ConfNames[t_RCONF_IDX]+(CNF_NAM_SIZ - 1)) = (char) 0 ;
525     }
526     return ret_code;
527 }
528 
529 static char Welcome[16];
530 static char News[16];
531 static char GoodBye[16];
532 static char NewFiles[] = "newfiles.dat";
533 static char DoorId[] = "door.id";
534 static char nothing[] = "";
535 
536 static char *BoardName = NULL ;	/* Name of BBS read from control.dat */
537 static char t_BoardName[BBS_NM_LEN+2];	/* Name of BBS read from control.dat */
538 static char *UserName = NULL ; 	        /* user name taken from control.dat */
539 static char t_UserName[50]; 	/* temp user name taken from control.dat */
540 
541 /*
542  * fatal_malloc - exit with error message on malloc error.
543  */
544 void
test_fatal_malloc(void * ptr,const char * filename,int line_num)545 test_fatal_malloc(void *ptr, const char *filename, int line_num)
546 {
547     if (ptr == NULL) {
548         fprintf(stderr, "%s  %s:%d\n", txt[1], filename, line_num);
549         exit(EXIT_FAILURE);
550     }
551 }
552 
553 
554 /*
555  * get_cntrl_str - returns strdup of control.dat variable based on token.
556  *  tokens are:  usrnm, boardnm
557  */
558 char *
get_cntrl_str(const cntrl_str_t token)559 get_cntrl_str(const cntrl_str_t token)
560 {
561     char *strnm = NULL;
562     switch (token) {
563     case usrnm:
564 	strnm = strdup(UserName);
565 	break;
566     case boardnm:
567 	strnm = strdup(BoardName);
568 	break;
569     default:
570 	/* switch() case error */
571 	error_switch_case((int)token, __FILE__, __LINE__);
572 	strnm = nothing ;
573     }
574     test_fatal_malloc(strnm, __FILE__, __LINE__);
575     return strnm;
576 }
577 
578 
579 /*
580  * MoveBulletins, move bulletins from workdir to atp bbs dir.
581  */
582 static void
MoveBulletins(const char * DestDir,const char * SrcDir)583 MoveBulletins(const char *DestDir, const char *SrcDir)
584 {
585     char **bulletin_name = NULL;
586     size_t bulletin;
587 #ifdef DIRENTRY
588     const size_t b_count = FindMatches(SrcDir, "blt-", &bulletin_name);
589 #else				/* msdog and windoze */
590     size_t b_count;
591     char mwbuf[MAXPATHS];
592     sprintf(mwbuf, "%s%c%s", SrcDir, SEP, "blt-");
593     b_count = FindMatches((char *) mwbuf, &bulletin_name);
594 #endif
595     if (bulletin_name != NULL) {
596 	for (bulletin = 0; bulletin < b_count; bulletin++, bulletin_name++) {
597 	    Work2home(DestDir, SrcDir, *bulletin_name);
598 	    free_string(*bulletin_name);
599 	}
600 	bulletin_name -= b_count;
601 	free(bulletin_name);
602     }
603 }
604 
605 
606 /*
607  * MoveWork - wrapper for Work2Home().
608  */
609 void
MoveWork(const char * DestDir,const char * SrcDir)610 MoveWork(const char *DestDir, const char *SrcDir)
611 {
612     /* Update Control.dat in bbs dir */
613     Work2home(DestDir, SrcDir, CNTRL_FILE);
614 
615     /* Update Newfiles.dat in bbs dir */
616     Work2home(DestDir, SrcDir, NewFiles);
617 
618     /* Update Welcome file in bbs dir */
619     Work2home(DestDir, SrcDir, Welcome);
620 
621     /* Update GoodBye file in bbs dir */
622     Work2home(DestDir, SrcDir, GoodBye);
623 
624     /* Update News file in bbs dir */
625     Work2home(DestDir, SrcDir, News);
626 
627     /* Update GoodBye file in bbs dir */
628     Work2home(DestDir, SrcDir, DoorId);
629 
630     /* Move bulletins to bbs dir */
631     MoveBulletins(DestDir, SrcDir);
632 }
633 
634 /*
635  * get_wm_ptr, returns strdup() of control.dat auxillary file names.
636  */
637 static char *
get_wm_ptr(welcome_msg_t msg_name)638 get_wm_ptr( welcome_msg_t msg_name )
639 {
640     char *wm_ptr = NULL;
641     switch (msg_name) {
642     case wm_hello:
643 	wm_ptr = Welcome;
644 	break;
645     case wm_news:
646 	wm_ptr = News;
647 	break;
648     case wm_goodbye:
649 	wm_ptr = GoodBye;
650 	break;
651     case wm_newfiles:
652 	wm_ptr = NewFiles;
653 	break;
654     case wm_door_id:
655 	wm_ptr = DoorId;
656 	break;
657     default:
658 	/* switch() case error */
659 	error_switch_case((int)msg_name, __FILE__, __LINE__);
660     }
661     return wm_ptr;
662 }
663 
664 /*
665  * WelcomeMsg - displays the BBS welcome file.
666  */
667 void
WelcomeMsg(const welcome_msg_t msg_name,const char * mfile)668 WelcomeMsg( const welcome_msg_t msg_name, const char *mfile)
669 {
670     char tmp[MAXPATHS];
671     if (get_isempty()) {
672 	EmptyMsg();
673 	/*@-strictops */
674     } else if (msg_name == wm_bulletin
675 	       || (mfile = get_wm_ptr(msg_name)) != NULL) {
676 	sprintf(tmp, "%s%s%c%s", HomePath, CurBoard, SEP, mfile);
677 	if (*mfile != NUL_CHAR && access(tmp, R_OK) == SUCCESS) {
678 	    /*@=strictops */
679 	    sprintf(tmp, "%s%s", HomePath, CurBoard);
680 	    view(tmp, mfile);
681 	} else {
682 	    printf("%s %s\n", txt[67], tmp);	/* "No file" */
683 	}
684     }
685 }
686 
687 /*
688  * RdCn_get_misc_names, static helper function for ReadControl().
689  *  gets Welcome, News, Goodbye names
690  */
691 static void
RdCn_get_misc_names(char * buf)692 RdCn_get_misc_names(char *buf)
693 {
694     strcpy(Welcome, "welcome");
695     strcpy(News, "news");
696     strcpy(GoodBye, "script0");
697     if (fget(buf, 15, rdc_fp)) {
698 	buf[15] = NUL_CHAR;
699 	strcpy(Welcome, strlwr(buf));
700 	if (fget(buf, 15, rdc_fp)) {
701 	    buf[15] = NUL_CHAR;
702 	    strcpy(News, strlwr(buf));
703 	    if (fget(buf, 15, rdc_fp)) {
704 		buf[15] = NUL_CHAR;
705 		strcpy(GoodBye, strlwr(buf));
706 	    }
707 	}
708     }
709 }
710 
711 /*
712  * RdCn_get_user_names, static helper function for ReadControl().
713  */
714 static atp_BOOL_T
RdCn_get_user_names(char * buf)715 RdCn_get_user_names(char *buf)
716 {
717     atp_BOOL_T ret_code = FALSE;
718     t_UserName[0] = NUL_CHAR ;
719     if (fget(t_UserName, 50, rdc_fp) && RdCn_skiplines(3, buf))
720 	ret_code = t_UserName[0] == NUL_CHAR ? FALSE : TRUE ;
721     else {
722 	char CONSPTR tp = get_atprc_str(usr1nm);
723 	strcpy(t_UserName,tp);
724 	free_string(tp);
725 	ret_code = t_UserName[0] == NUL_CHAR ? FALSE : TRUE ;
726     }
727     return ret_code;
728 }
729 
730 
731 /*
732  * RdCn_switch_to_new_bbs, assign new pointers and free old arrarys.
733  */
734 static void
RdCn_switch_to_new_bbs(void)735 RdCn_switch_to_new_bbs(void)
736 {
737     RdCn_do_free(LastConf, ConfActive, ConfNumbers, ConfNames);
738     if (UserName != NULL)
739 	free_string(UserName);
740     if (BoardName != NULL)
741 	free_string(BoardName);
742     BoardName = strdup(t_BoardName);
743     UserName = strdup(t_UserName);
744     /* memory allocation error test */
745     if (BoardName == NULL || UserName == NULL)
746 	test_fatal_malloc(NULL, __FILE__, __LINE__);
747     LastConf = t_LastConf;
748     ConfActive = t_ConfActive;
749     ConfNumbers = t_ConfNumbers;
750     ConfNames = t_ConfNames;
751     RdCn_t_invalidate();
752 }
753 
754 
755 /*
756  * RdCn_Fin, finish up reading control.dat.
757  */
758 static atp_ERROR_T
RdCn_Fin(char * tmp)759 RdCn_Fin(char *tmp)
760 {
761     atp_ERROR_T ret_code = ATP_ERROR;
762     assert(t_LastConf == -1 && t_ConfNumbers == NULL && t_ConfNames == NULL && t_ConfActive == NULL);
763     /* get new LastConf and add 2 for the Personal and Reply conferences */
764     t_LastConf = atoi(tmp) + 2;
765     if (0 <= t_LastConf && t_LastConf <= MAXCONF && RdCn_do_malloc(t_LastConf)) {
766 	if (RdCn_get_conf_names(tmp)) {
767 	    RdCn_switch_to_new_bbs();
768 	    assert(t_LastConf == -1 && t_ConfNumbers == NULL && t_ConfNames == NULL && t_ConfActive == NULL);
769 	    RdCn_get_misc_names(tmp);
770 	    printf("\n %s\n\n", BoardName);
771 	    ret_code = ATP_OK;
772 	} else {
773 	    RdCn_do_free(t_LastConf, t_ConfActive, t_ConfNumbers, t_ConfNames);
774 	    RdCn_t_invalidate();
775 	}
776     }
777     return ret_code;
778 }
779 
780 
781 /*
782  * RdCn_ReadControl - parses control.dat file.
783  */
784 atp_ERROR_T
RdCn_ReadControl(const char * Path)785 RdCn_ReadControl(const char *Path)
786 {
787     atp_ERROR_T ret_code = ATP_ERROR;
788     char tmp[MAXPATHS];
789     /*@-strictops */
790     if (verify_new_file(tmp, Path, CNTRL_FILE) == ATP_OK
791 	&& (rdc_fp = fopen(tmp, "rb")) != NULL
792 	&& fget(t_BoardName, BBS_NM_LEN, rdc_fp)
793 	&& RdCn_skiplines(5, tmp)
794 	&& RdCn_get_user_names(tmp)
795 	&& fget(tmp, FGET_RD_SIZE, rdc_fp)) {
796 	ret_code = RdCn_Fin(tmp);
797     }
798     /*@=strictops */
799     assert(t_LastConf == -1 && t_ConfNumbers == NULL && t_ConfNames == NULL && t_ConfActive == NULL);
800     fclose(rdc_fp);
801     return ret_code;
802 }
803 
804 /*
805  * RdCn_main_init - sets up "empty" bbs on program initialization.
806  */
807 atp_BOOL_T
RdCn_main_init(void)808 RdCn_main_init(void)
809 {
810     atp_BOOL_T ret_code;
811     char CONSPTR tusrnm = get_atprc_str(usr1nm);
812     assert(t_LastConf == -1 && t_ConfNumbers == NULL && t_ConfNames == NULL && t_ConfActive == NULL);
813     strcpy(t_UserName, tusrnm);
814     free_string(tusrnm);
815     strcpy(t_BoardName, txt[10]);
816     t_LastConf = 0;
817     ret_code = RdCn_do_malloc(t_LastConf);
818     if (ret_code) {
819 	strcpy(t_ConfNames[0], txt[9]);
820 	*t_ConfActive = FALSE;
821 	*t_ConfNumbers = (int)0;
822 	RdCn_switch_to_new_bbs();
823 	set_CurBoard(BoardName, cb_RdCn_main_init);
824     }
825     assert(t_LastConf == -1 && t_ConfNumbers == NULL && t_ConfNames == NULL && t_ConfActive == NULL);
826     return ret_code ;
827 }
828 
829 /*
830  * RdCn_exit_free - called by main_exit() in atpmain.c before shutting down.
831  */
832 void
RdCn_exit_free(void)833 RdCn_exit_free(void)
834 {
835    assert(ConfActive != NULL && ConfNumbers != NULL && ConfNames != NULL);
836    RdCn_do_free(LastConf, ConfActive, ConfNumbers, ConfNames);
837 }
838 
839 static int ActvCnt = 0 ;
840 /*
841  * get_ActvCnt - returns number of active conferences.
842  */
843 int
get_ActvCnt(void)844 get_ActvCnt(void)
845 {
846     assert( -1 < ActvCnt );
847     return ActvCnt;
848 }
849 
850 
851 /*
852  * ActvConf - update array which tracks active conferences.
853  */
854 void
ActvConf(void)855 ActvConf(void)
856 {
857     int i;
858     char tmp[MAXPATHS];
859 #ifdef __MSDOS__
860     struct ffblk merv;
861 
862     ActvCnt = 0;
863     for (i = 0; i <= LastConf; i++)
864 	ConfActive[i] = FALSE;
865     sprintf(tmp, "%s%s%c*.idx", HomePath, CurBoard, SEP);
866 
867     if (!findfirst(tmp, &merv, 0)) {
868 	i = findCindex(atoi(merv.ff_name));
869 	if (i >= 0) {
870 	    ConfActive[i] = TRUE;
871 	    ActvCnt = 1;
872 	}
873 	while (!findnext(&merv)) {
874 	    i = findCindex(atoi(merv.ff_name));
875 	    if (i >= 0) {
876 		ConfActive[i] = TRUE;
877 		ActvCnt++;
878 	    }
879 	}
880     }
881 #else				/* unix et al */
882     ActvCnt = 0;
883 
884     for (i = 0; i <= LastConf; i++) {
885 	cnf_path(tmp, ConfNumbers[i]);
886 	if (access(tmp, F_OK) == SUCCESS) {
887 	    ConfActive[i] = TRUE;
888 	    ActvCnt++;
889 	} else {
890 	    ConfActive[i] = FALSE;
891 	}
892     }
893 #endif
894     set_ReplyExist( ConfActive[RCONF_IDX], rexist_ActvConf);
895 }
896 
897 
898 /*
899  * Chk4RepCnf - checks if reply conference exists and prompts for deletion.
900  */
901 void
Chk4RepCnf(const char * tpath)902 Chk4RepCnf(const char *tpath)
903 {
904     char Tcnfbuf[MAXPATHS];
905     char Tidxbuf[MAXPATHS];
906 
907     sprintf(Tcnfbuf, "%s%c%d.cnf", tpath, SEP, REPL_CONF);
908     sprintf(Tidxbuf, "%s%c%d.idx", tpath, SEP, REPL_CONF);
909     set_ReplyExist(FALSE, rexist_Chk4RepCnf);
910     /* does reply conference exist ? */
911     if (access(Tcnfbuf, F_OK) == SUCCESS ) {
912 	char prmbuf[80];
913 	red();
914 	high();
915 	/* "Warning ! file already exist " "delete it ?" */
916 	printf("%s   %s %d.cnf %s %s...!\n", txt[2], txt[62], REPL_CONF, ConfNames[RCONF_IDX], txt[63]);
917 	sprintf(prmbuf, "            %s ", txt[64]);
918 	if (YesNo(YES, prmbuf)) {
919 	    do_unlink(Tcnfbuf);
920 	    do_unlink(Tidxbuf);
921 	    ConfActive[RCONF_IDX] = FALSE;
922 	} else
923 	    set_ReplyExist(TRUE, rexist_Chk4RepCnf);
924     }
925 }
926 
927 
928 /*
929  * ListConfOut, called bye display_conf_names().
930  */
931 static const int pers = -2;
932 static const int reps = -1;
933 
934 ATP_INLINE int
ListConfOut(int conf_array_index)935 ListConfOut(int conf_array_index)
936 {
937     if (conf_array_index == pers) {		/* List personal conference first */
938 	if (get_pmail())
939 	    blink();
940 	conf_array_index = PCONF_IDX;
941     }
942     if (conf_array_index == reps)		/* List reply conference next */
943 	conf_array_index = RCONF_IDX;
944     red();
945     high();
946     printf("\n\t\t%4d ", ConfNumbers[conf_array_index]);
947     clear();
948     blue();
949     printf("......... ");
950     magenta();
951     if ((conf_array_index == PCONF_IDX) && get_pmail())
952 	blink();
953     high();
954     printf("%-15s ", ConfNames[conf_array_index]);
955     clear();
956     blue();
957     printf("......... ");
958     if ((conf_array_index == PCONF_IDX) && get_pmail())
959 	blink();
960     green();
961     high();
962     if (!ConfActive[conf_array_index]) {
963 	printf("%s", txt[79]);	/* "Inactive" */
964     } else {
965 	magenta();
966 	printf("%s", txt[80]);	/* "active" */
967     }
968     if (conf_array_index == PCONF_IDX) {
969 	clear();
970 	conf_array_index = pers;
971     } else if (conf_array_index == RCONF_IDX) {
972 	conf_array_index = reps;
973     }
974     return conf_array_index;
975 }
976 
977 
978 /*
979  * display_bbs_name, called by ListConf().
980  */
981 static void
display_bbs_name(void)982 display_bbs_name(void)
983 {
984     int i;
985     const int underline_width = (int) (strlen(txt[78]) + strlen(BoardName) + 1);
986     CLSCRN();
987     magenta();
988     high();
989     printf("\r%s %s\n", txt[78], BoardName);	/* "Liste des conf. sur" */
990     clear();
991     red();
992     printf("\r");
993     for (i = 0; i < underline_width; i++)
994 	(void)putchar('-');
995     (void)putchar('\n');
996 }
997 
998 
999 /*
1000  * display_conf_names, called by ListConf().
1001  */
1002 static void
display_conf_names(void)1003 display_conf_names(void)
1004 {
1005     int line, cnf_index;
1006     ActvConf();
1007     for (cnf_index = pers, line = 0; cnf_index < RCONF_IDX; line++, cnf_index++) {
1008 	if (line > (get_ScrnRows() - 6)) {
1009 	    line = -2;
1010 	    printf("\n");
1011 	    if (!more(YES))
1012 		break;
1013 	    cyan();
1014 	    CLSCRN();
1015 	}
1016 	cnf_index = ListConfOut(cnf_index);
1017     }
1018 }
1019 
1020 
1021 /*
1022  * ListConf - prints conference numbers/names/status.
1023  */
1024 void
ListConf(void)1025 ListConf(void)
1026 {
1027     if (get_isempty()) {
1028 	EmptyMsg();
1029     } else {
1030 	display_bbs_name();
1031 	display_conf_names();
1032 	clear();
1033 	printf("\n");
1034     }
1035 }
1036 /********************* end of ReadControl() section ***********************/
1037