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