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 mkindex.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 #include "reader.h"
36 #include "readlib.h"
37 #include "ansi.h"
38 #include "qlib.h"
39 #include "makemail.h"
40 
41 /*
42  * BEGIN THE MAKE INDEX ROUTINES
43  *
44  */
45 
46 /*
47  * MkInReadMsg, does the low-level read when called from MkInLoopRd().
48  */
49 ATP_INLINE atp_ERROR_T
MkInReadMsg(const unsigned long MaxChars,FILE * rm_fs,const size_t NbBlocs)50 MkInReadMsg(const unsigned long MaxChars, FILE * rm_fs, const size_t NbBlocs)
51 {
52     atp_ERROR_T ret_code = ATP_OK;
53     byte *ptr = (byte *) rbuf + block_SIZE;
54 
55     /* Read the message..... */
56     if (fread(ptr, block_SIZE, NbBlocs, rm_fs) != NbBlocs) {
57 	printf("max char %lu\n", MaxChars);
58 	printf("%s messages.dat\n", txt[58]);	/* "error reading file " */
59 	ret_code = ATP_ERROR;
60     } else {
61 	/* Translate to normal Line Feed */
62 	unsigned long i = 0L;
63 	for (; i < MaxChars; i++) {
64             /*@-strictops */
65 	    if ((byte)*ptr == QWK_LINE_FEED) /*@=strictops */
66 		*ptr = '\n';
67 	    ptr++;
68 	}
69 	/*@-strictops */
70 	while (*(--ptr) == SPC_CHAR) /*@=strictops */
71 	    *ptr = NUL_CHAR ;		/* delete padding spaces */
72     }
73     return ret_code;
74 }
75 
76 static /*@i5*/ FILE *fs = NULL, *fd = NULL, *fx = NULL, *pfd = NULL, *pfx = NULL;
77 static unsigned NamLen = 0;
78 static char Qmail[HDRSIZE];
79 static char *UserName;
80 static atp_BOOL_T persmail;
81 
82 
83 /*
84  * MkInUserName, try to get user name from control.dat, or atprc.
85  */
86 ATP_INLINE atp_BOOL_T
MkInUserName(void)87 MkInUserName(void)
88 {
89     atp_BOOL_T ret_code = TRUE;
90     if ((UserName = get_cntrl_str(usrnm)) == NULL
91 	&& (UserName = get_atprc_str(usr1nm)) == NULL) {
92 	/* this point should never be reached */
93 	ret_code = FALSE;
94     } else {
95 	NamLen = strlen(UserName);
96     }
97     return ret_code;
98 }
99 
100 
101 /*
102  * MkInOpen, first routine called by MkIndex.
103  */
104 static atp_BOOL_T
MkInOpen(const char * SrcDir,const char * DestDir,long * old_last)105 MkInOpen(const char *SrcDir, const char *DestDir, long *old_last)
106 {
107     atp_BOOL_T ret_code = FALSE;
108     char Src[MAXPATHS];		/* Source file Message.DAT */
109     char Pdst[MAXPATHS];	/* Personal conference file */
110     char Pidx[MAXPATHS];	/* Personal index file     */
111 
112     /* Load new CONTROL.DAT file. */  /*@-strictops */
113     if (RdCn_ReadControl(SrcDir) != ATP_OK) { /*@=strictops */
114 	/* "error in control.dat" */
115 	printf("%s !\n", txt[14]);
116     } else {
117 	yellow();
118 	/* "adding msgs and creating indexes" */
119 	printf("%s...\n", txt[71]);
120 	cyan();
121 	/*@-usedef */
122 	/* Open MESSAGES.DAT */
123 	verify_new_file(Src, SrcDir, MSG_FILE);
124 	/*@=usedef */
125 	printf("Open %s\n", Src);
126 
127 	if ((fs = fopen(Src, "rb")) == NULL) {
128 	    /* "unable to open file" */
129 	    printf("%s %s...\n", txt[51], Src);
130 	} else if (fread(Qmail, header_SIZE, one_record, fs) != one_record) {
131 	    /* "error reading file" */
132 	    printf("%s %s\n", txt[58], Src);
133 	    fclose(fs);
134 	} else {
135 	    /* Open personal conference and index files  */
136 	    sprintf(Pdst, "%s%c%d.cnf", DestDir, SEP, PERS_CONF);
137 
138 	    /* Open Conference file */  /*@-strictops */
139 	    if (OpenCon(&pfd, &fs, Pdst) != ATP_ERROR) {  /*@=strictops */
140 		fseek(pfd, 0L, SEEK_END);	/* Go to end of personal idx file... */
141 		*old_last = ftell(pfd);		/* Take size of existing messages */
142 		sprintf(Pidx, "%s%c%d.idx", DestDir, SEP, PERS_CONF);
143 
144 		/* Open Conference Index */ /*@-strictops */
145 		if (OpenCon(&pfx, &fs, Pidx) != ATP_ERROR) {  /*@=strictops */
146 		    fseek(pfx, 0L, SEEK_END);
147 		    MoveWork(DestDir, SrcDir);
148 		    persmail = FALSE;
149 		    ret_code = MkInUserName();
150 		}
151 	    }
152 	}
153     }
154     return ret_code;
155 }
156 
157 static int iconf = 0;
158 static long oldcount = 0;
159 static long count = 0;
160 
161 /*
162  * MkInShow, print the conference number and name while building Index.
163  */
164 static void
MkInShow(void)165 MkInShow(void)
166 {
167     printf("Conference %d \t[ %-15s ]%c  %3ld New %s %s \n",
168 	   ConfNumbers[iconf], ConfNames[iconf],
169 	   persmail ? '*' : ' ', count - oldcount,
170 	   (count - oldcount) > 1 ? "Messages" : "Message",
171 	   persmail ? txt[103] : txt[108]);
172 }
173 
174 
175 /*
176  * MkInClose, cleanup and close files before exiting MkIndex().
177  */
178 static void
MkInClose(atp_ERROR_T ret_code,const int conf,const long old_last,const char * DestDir)179 MkInClose( atp_ERROR_T ret_code, const int conf, const long old_last, const char *DestDir)
180 {
181     char tmpbuf[MAXPATHS];  /*@-strictops */
182     if (ret_code == ATP_OK && conf >= 0) /*@=strictops */
183 	MkInShow();
184     /* outerr: */
185     printf("\n");
186     fclose(fs);
187     fclose(fd);
188     fclose(pfd);
189     fclose(fx);
190     fclose(pfx);
191     /* if no pre-existing or new personal messages, delete personal cnf. */
192     if (old_last == 0L) {
193 	sprintf(tmpbuf, "%s%c%d.idx", DestDir, SEP, PERS_CONF);
194 	do_unlink(tmpbuf);
195 	sprintf(tmpbuf, "%s%c%d.cnf", DestDir, SEP, PERS_CONF);
196 	do_unlink(tmpbuf);
197     }
198 }
199 
200 
201 static int conf;
202 
203 /*
204  * MkInDoOpenFiles, setup and execute the open routines.
205  *  called by MkInReOpen().
206  */
207 static atp_BOOL_T
MkInDoOpenFiles(const char * Dst,char * Idx,const char * DestDir,long * last)208 MkInDoOpenFiles(const char *Dst, char *Idx, const char *DestDir, long *last)
209 {
210     atp_BOOL_T ret_code = TRUE;
211     /* Open Conference file */ /*@-strictops */
212     if (OpenCon(&fd, &fs, Dst) == ATP_ERROR) { /*@=strictops */
213 	fclose(pfx);
214 	fclose(pfd);
215 	ret_code = FALSE;
216     } else {
217 	fseek(fd, 0L, SEEK_END);	/* Go to end of file... */
218 	*last = ftell(fd);	/* Take size of existing messages */
219 	sprintf(Idx, "%s%c%d.idx", DestDir, SEP, conf);
220 	/* Open Conference Index */  /*@-strictops */
221 	if (OpenCon(&fx, &fs, Idx) == ATP_ERROR) {  /*@=strictops */
222 	    fclose(pfx);
223 	    fclose(pfd);
224 	    ret_code = FALSE;
225 	} else {
226 	    fseek(fx, 0L, SEEK_END);
227 	    count = (ftell(fx) / IDXSIZE);
228 	    oldcount = count;
229 	    printf("%d : %ld\r", iconf, count - oldcount);
230 	    fflush(stdout);
231 	}
232     }
233     return ret_code;
234 }
235 
236 /*
237  * MkInReopen, closes old conference dbase and opens a new conference dbase.
238  */
239 static atp_BOOL_T
MkInReOpen(atp_BOOL_T * ffllgg,const char * DestDir,long * last)240 MkInReOpen(atp_BOOL_T *ffllgg, const char *DestDir, long *last)
241 {
242     char Dst[MAXPATHS];		/* Conference data  file   */
243     char Idx[MAXPATHS];		/* Conference index file   */
244     if (*ffllgg) {		/* after first pass, close file pointers */
245 	fclose(fx);
246 	fclose(fd);
247     }
248     *ffllgg = TRUE;		/* first pass flag set true */
249     if (conf >= 0)		/* close up old before starting new */
250 	MkInShow();
251     persmail = FALSE;
252     count = oldcount = 0;
253     conf = readCnum(Qmail + HBinConfN);
254     /* here is where we do bounds checking */
255     for (iconf = 0; iconf <= LastConf; iconf++)
256 	if (conf == ConfNumbers[iconf])
257 	    break;
258     if (iconf > LastConf) {
259 	/* this unmungs the number by overwriting probable space character */
260 	Qmail[HBinConfN + 1] = (byte) 0;
261 	(void)memcpy((char *) rbuf, (char *) Qmail, header_SIZE);
262 	conf = readCnum(Qmail + HBinConfN);
263 	for (iconf = 0; iconf <= LastConf; iconf++)
264 	    if (conf == ConfNumbers[iconf])
265 		break;
266 	if (iconf > LastConf) {	/* last resort force valid conf num. */
267 	    const unsigned high_byte = 0xff00;
268 	    const unsigned low_byte = 0x00ff;
269 	    unsigned uconf;
270 	    iconf = 0;
271 	    conf = ConfNumbers[0];
272 	    uconf = (unsigned) conf;
273 	    Qmail[HBinConfN] = (byte) (uconf & low_byte);
274 	    Qmail[HBinConfN + 1] = (byte) ((uconf & high_byte) >> 8);
275 	    (void)memcpy(rbuf, Qmail, header_SIZE);
276 	}
277     }
278     /* Open new files  */
279     sprintf(Dst, "%s%c%d.cnf", DestDir, SEP, conf);
280     return MkInDoOpenFiles(Dst, Idx, DestDir, last);
281 }
282 
283 
284 /*
285  * MkIn_calc_block_count, calculate/adjust record count for splitting messages.
286  */
287 ATP_INLINE unsigned long
MkIn_calc_block_count(size_t * NbBlocs,size_t TotBlocs)288 MkIn_calc_block_count(size_t *NbBlocs, size_t TotBlocs)
289 {
290     unsigned long MaxChars;
291     char tqbuf[10];
292     if ( TotBlocs < *NbBlocs)
293 	*NbBlocs = (size_t) TotBlocs;
294     MaxChars = *NbBlocs * block_SIZE;
295     sprintf(tqbuf, "%lu", (unsigned long)(*NbBlocs + (size_t)1));
296     str2mem(rbuf + HSizeMsg, "      ");		/* 6 spaces */
297     str2mem(rbuf + HSizeMsg, tqbuf);
298     return MaxChars;
299 }
300 
301 static atp_BOOL_T persave;
302 static long pcount;
303 
304 /*
305  * MkInPers, write out personal messages to their own conference area.
306  */
307 static atp_ERROR_T
MkInPers(const size_t blk_ct,long * old_last,const unsigned long MaxChars,const char * DestDir)308 MkInPers(const size_t blk_ct, long *old_last, const unsigned long MaxChars, const char *DestDir)
309 {
310     atp_ERROR_T ret_code = ATP_OK;
311     if (fwrite(rbuf, block_SIZE, blk_ct, pfd) != blk_ct) {
312 	/* "error writing file " */
313 	perror("MkInPers()");
314 	printf("%s %s%c%d.cnf\n", txt[73], DestDir, SEP, PERS_CONF);
315 	ret_code = ATP_ERROR;
316     } else
317 	*old_last += (long) MaxChars + header_SIZE;
318     return ret_code;
319 }
320 
321 
322 /*
323  * MkInWritePidx, write out personal message conference index.
324  */
325 ATP_INLINE atp_BOOL_T
MkInWritePidx(unsigned long MaxChars,long former_last)326 MkInWritePidx(unsigned long MaxChars, long former_last)
327 {
328     atp_BOOL_T ret_code;
329     /*@-strictops */
330     if (WriteIndex(pfx, pcount, MaxChars, former_last) == ATP_ERROR) {
331 	ret_code = FALSE;	/*@-strictops */
332     } else {
333 	pcount++;
334 	ret_code = TRUE;
335     }
336     return ret_code;
337 }
338 
339 
340 /*
341  * MkInLoopRd, loop for reading in raw messages from packet.
342  */
343 ATP_INLINE atp_ERROR_T
MkInLoopRd(size_t TotBlocs,size_t NbBlocs,long * old_last,long * rlast,const char * DestDir)344 MkInLoopRd(size_t TotBlocs, size_t NbBlocs,
345 	   long *old_last, long *rlast, const char *DestDir)
346 {
347     atp_ERROR_T ret_code = ATP_OK;
348     long last = *rlast;
349     unsigned long MaxChars = NbBlocs * block_SIZE ;
350 
351     /* loop till complete message is read */
352     for (;;) {
353 	/* if message will be split ... */
354 	if (TotBlocs != NbBlocs)
355 	    MaxChars = MkIn_calc_block_count(&NbBlocs, TotBlocs);
356 
357 	/* 'last' initialized to -912L, error in ftell() sets 'last' to -1L */
358 	assert( last != -912L );
359         /*@-strictops */
360 	/* Write message index */
361 	if (last < 0L || WriteIndex(fx, count, MaxChars, last) == ATP_ERROR) {	/*@=strictops */
362 	    /* "msg too big" */
363 	    printf("Error writing fidx MkIndex(): last = %ld\n", last);
364 	    ret_code = ATP_ERROR;
365 	    break;
366 	}
367 	count++;
368 	/* Write Personal Index */
369 	if (persave && !MkInWritePidx(MaxChars, *old_last)){
370 	    printf("Error writing pidx MkIndex()\n");	/* "msg too big" */
371 	    ret_code = ATP_ERROR;
372 	    break;
373 	}
374 	/* Display running total to screen. */
375 	printf("%d : %ld\r", iconf, count - oldcount);
376 	fflush(stdout);
377 
378 	/*@-strictops */
379 	/* Read the message ... */
380 	if (MaxChars && (ret_code = MkInReadMsg(MaxChars, fs, NbBlocs)) == ATP_ERROR)/*@=strictops */
381 	    break;
382 
383 	/* Write the message ... */
384 	if (fwrite(rbuf, block_SIZE, NbBlocs + 1, fd) != NbBlocs + 1) {	/* And copy it.         */
385 	    printf("%s %d.cnf\n", txt[73], conf);	/* "error writing file " */
386 	    ret_code = ATP_ERROR;
387 	    break;
388 	}
389 	/*@-strictops */
390 	/* Write any personal messages to personal area ... */
391 	if (persave && (ret_code = MkInPers(NbBlocs + 1, old_last, MaxChars, DestDir)) == ATP_ERROR) {/*@=strictops */
392 	    break;
393 	} else {
394 	    /* keep track of our progress ... */
395 	    last += (long) (MaxChars + header_SIZE);
396 	    if (TotBlocs >= NbBlocs)
397 		TotBlocs -= NbBlocs;
398 	    if (TotBlocs <= 0)
399 		break;
400 	}
401     }				/* end of for(;;) */
402     *rlast = last;
403     return ret_code;
404 }
405 
406 /*
407  * MkInGetMemory, try to re-allocate more memory while reading in new messages.
408  */
409 ATP_INLINE size_t
MkInGetMemory(size_t NbBlocs)410 MkInGetMemory(size_t NbBlocs)
411 {
412     reup( NbBlocs * block_SIZE + block_SIZE );
413     return (get_RbufRecs() - (size_t)1) ;
414 }
415 
416 /*
417    This is where we read messages.dat the main message file from
418    the BBS. The file is parsed into new seperate .cnf files for each
419    conference, each with its own index. While scanning the main file
420    we also look for a match between UserName (global variable) and
421    Qmail.ForWhom field in each message header. When a match is found
422    the message is also saved to a personal conference file with its
423    own index, and a flag "persmail" is set which shows that personal
424    mail has been received. Note that the index files sent in the QWK
425    packet are ignored and never used by this program.
426  */
427 
428 
429 /*
430  * MkIndex - tosses messages.dat thus creating conferences and indexes.
431  */
432 atp_ERROR_T
MkIndex(const char * SrcDir,const char * DestDir)433 MkIndex(const char *SrcDir, const char *DestDir)
434 {
435     atp_ERROR_T ret_code = ATP_OK;
436     long old_last = 0L;		/* size of pre-existing personal conference */
437 
438     if (MkInOpen(SrcDir, DestDir, &old_last)) {
439 	atp_BOOL_T ffllgg = FALSE;
440 	long last = -912L;	/* initialize to arbitrary debugging constant */
441 	conf = -1;
442 	pcount = (ftell(pfx) / IDXSIZE);
443 	for (;;) {
444 	    size_t NbBlocs, TotBlocs;
445 
446 	    if (fread(Qmail, header_SIZE, one_record, fs) != one_record)
447 		break;		/* End of file         */
448             /*@-strictops */
449 	    if (Qmail[HStatus] == NUL_CHAR || (byte) Qmail[HStatus] == DOS_SPACE)
450 		break;		/* End of file  J mail ?  */  /*@=strictops */
451 
452 	    /* Another conference ? */
453 	    (void)memcpy(rbuf, Qmail, header_SIZE);
454 	    if ((readCnum(Qmail + HBinConfN)) != conf
455 	        && !MkInReOpen(&ffllgg, DestDir, &last)) {
456 		ret_code = ATP_ERROR ;
457 		break ;
458 	    }
459 	    /* check for a personal message */
460 	    persave = FALSE;
461 	    if (strnicmp(UserName, Qmail + HForWhom, NamLen)==SUCCESS){
462 		set_pmail(TRUE,pmMkIndex);	/* set global personal mail flag true */
463 		persmail = TRUE;	/* set local personal mail flag true  */
464 		persave = TRUE;
465 	    }
466 	    /* check buffer size and re-allocate if needed */
467 	    TotBlocs = NbBlocs = atoi(Qmail + HSizeMsg) - 1;
468 	    if (NbBlocs + (size_t)1 > get_RbufRecs() )
469 		NbBlocs = MkInGetMemory(NbBlocs);
470 	    ret_code = MkInLoopRd(TotBlocs, NbBlocs, &old_last, &last, DestDir);
471 	    /*@-strictops */
472 	    if (ret_code == ATP_ERROR) /*@=strictops */
473 		break;
474 	}			/* end of for(;;) */
475 	MkInClose(ret_code, conf, old_last, DestDir);
476 	free_string(UserName);
477     }
478     return ret_code;
479 }
480 
481 
482 /* end of mkindex.c */
483