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