1 /*****************************************************************************/
2 /*                                                                           */
3 /*                 (C) Copyright 1995-1997  Alberto Pasquale                 */
4 /*                 Portions (C) Copyright 1999 Per Lundberg                  */
5 /*                                                                           */
6 /*                   A L L   R I G H T S   R E S E R V E D                   */
7 /*                                                                           */
8 /*****************************************************************************/
9 /*                                                                           */
10 /* This source code is NOT in the public domain and it CANNOT be used or     */
11 /* distributed without written permission from the author.                   */
12 /*                                                                           */
13 /*****************************************************************************/
14 /*                                                                           */
15 /*   How to contact the author:  Alberto Pasquale of 2:332/504@fidonet       */
16 /*                               Viale Verdi 106                             */
17 /*                               41100 Modena                                */
18 /*                               Italy                                       */
19 /*                                                                           */
20 /*****************************************************************************/
21 
22 // FBlib.Cpp
23 
24 // This is the main module of FBlib.LIB, which is a library for
25 // (selectively) building the Maximus (tm) filebase.
26 
27 #include <bbsgenlb.hpp>
28 #include <apgenlib.hpp>
29 
30 #include <stdio.h>
31 #include <ctype.h>
32 #include <string.h>
33 #include <time.h>
34 #include <sys/stat.h>
35 
36 #include "namelist.hpp"
37 #include "fbd.hpp"
38 #include "fb.hpp"
39 #include "fblib.hpp"
40 
41 
42 struct AreaData {
43     word areanum;
44     BOOL LocBase;       // FALSE if the area DAT/DMP/IDX must not be written
45     char filesbbs[PATH_MAX];   // complete files.bbs name (with extension)
46     FILE *fdat, *fdmp;
47     word datpos;
48     dword dmpofs;
49     dword first;        // number of first idx entry in FBD data
50 };
51 
52 
strnupcpy(char * dest,const char * src,int n)53 char *strnupcpy (char *dest, const char *src, int n)
54 {
55     for (int i = 0; i < n; i++) {
56         *(dest ++) = (char) toupper (*src);
57         if (*src)
58             src ++;
59     }
60     return dest;
61 }
62 
63 
64                     // for GetEntry flags
65 #define _GE_NRM_    0
66 #define _GE_NODIR_  1       // do not read dir to get size and date
67 
68 static char *GetEntry (const char *filename, _dir *&entry, char *&path,
69                        word flags = _GE_NRM_);
70 
71 // gets data on filename, to be used for files that reside in a different dir.
72 // filename can include path information.
73 // entry and path must point to suitable buffers.
74 // On return path is changed to NULL if no path in filename;
75 // entry is changed to NULL if filename is offline.
76 // The return value is the fname (no path) and points to entry->fname,
77 // even if the file is offline and entry is changed to NULL.
78 // The return value is NULL in case of error.
79 
80 
81 
NewFBBS(const char * filepath,const char * filesbbs)82 static BOOL NewFBBS (const char *filepath, const char *filesbbs)
83 {
84     char files[PATH_MAX];
85     if (*filesbbs)
86         strcpy (files, filesbbs);
87     else
88         sprintf (files, "%sfiles.bbs", filepath);
89 
90     time_t datebbs, dateidx;
91 
92     datebbs = DosFileTime (files);
93     setext (files, ".IDX");
94     dateidx = DosFileTime (files);
95     if (dateidx == 0)
96         return FALSE;
97 
98     return (datebbs > dateidx);
99 }
100 
101 
AreaNameMatch(const char * areaname)102 BOOL FileBase::AreaNameMatch (const char *areaname)
103 {
104     char *name = AreaNameList->get (1);
105     while (name) {
106         if (fnamecmp (areaname, name) == 0)
107             return TRUE;
108         name = AreaNameList->get ();
109     }
110     return FALSE;
111 }
112 
113 
AreaPathMatch(const char * areapath)114 BOOL FileBase::AreaPathMatch (const char *areapath)
115 {
116     char *path = AreaPathList->get (1);
117     while (path) {
118         if (eqpath (areapath, path))
119             return TRUE;
120         path = AreaPathList->get ();
121     }
122     return FALSE;
123 }
124 
125 
126                 // -----------------> public
127 
128 
FileBase(char cont,int cpos,word flags)129 FileBase::FileBase (char cont, int cpos, word flags)
130 {
131     HardErrDisable ();
132 
133     this->cont = cont;
134     this->cpos = cpos;
135     this->flags = flags;
136 
137     AreaNameList = new namelist;
138     AreaPathList = new namelist;
139     fbd = new FBD;
140     ad = new AreaData;
141 }
142 
143 
~FileBase(void)144 FileBase::~FileBase (void)
145 {
146     delete ad;
147     delete fbd;
148     delete AreaPathList;
149     delete AreaNameList;
150 }
151 
152 
AreaOpen(word areanum,const char * filesbbs,BOOL NoLocBase)153 int FileBase::AreaOpen (word areanum, const char *filesbbs, BOOL NoLocBase)
154 {
155     char fullpath[PATH_MAX];
156 
157     strcpy (fullpath, filesbbs);
158     char *p = strrchr (fullpath, DIRSEP); // remove filename
159     if (!p)
160         p = strchr (fullpath, ':');
161     if (p)
162         *(p+1) = '\0';
163     else
164         *fullpath = '\0';
165 
166     int fpstat = Exist (fullpath);
167     if (!(fpstat & _ExDIR_))        // path for files.* not existent
168         return _BbsPathNotFound_;
169 
170     ad->LocBase = !NoLocBase && (fpstat & _ExWRITE_); // DAT/DMP/IDX
171 
172     fbd->AddArea (areanum);
173     ad->areanum = areanum;
174     ad->datpos = 0;
175 
176     if (!ad->LocBase)
177         if (NoLocBase)
178             return _OK_;
179         else
180             return _ReadOnly_;
181 
182     strcpy (fullpath, filesbbs);
183     setext (fullpath, ".DAT");
184 
185     ad->fdat = fopen (fullpath, "wb");
186     if (ad->fdat == NULL) {     // must be write protected
187         ad->LocBase = FALSE;
188         return _WriteProtected_;
189     }
190     setvbuf (ad->fdat, NULL, _IOFBF, 8192);
191 
192     setext (fullpath, ".DMP");
193     ad->fdmp = fopen (fullpath, "wb");
194     if (ad->fdmp == NULL) {     // this is error
195         fclose (ad->fdat);
196         ad->LocBase = FALSE;
197         return _Error_;
198     }
199     setvbuf (ad->fdmp, NULL, _IOFBF, 8192);
200 
201     if (fwrite ("\0\0\0", 3, 1, ad->fdmp) != 1) {
202         fclose (ad->fdat);
203         fclose (ad->fdmp);
204         ad->LocBase = FALSE;
205         return _Error_;
206     }
207 
208     strcpy (ad->filesbbs, filesbbs);
209 
210     ad->dmpofs = 3;
211     ad->first = fbd->GetCurn ();
212 
213     return _OK_;
214 }
215 
216 
WriteUniqueDmpLine(const char * text,FILE * f)217 static int WriteUniqueDmpLine (const char *text, FILE *f)   // 0 on success
218 {
219     const char *p = text;
220     while (*p) {
221         int linelen = strcspn (p, "\n");     // output line
222         if (linelen > 0) {
223             if (fwrite (p, linelen, 1, f) != 1)
224                 return -1;
225             p += linelen;
226         }
227         while (*p == '\n') {                    // change '\n' to ' '
228             if (fputc (' ', f) == EOF)
229                 return -1;
230             p ++;
231         }
232     }
233 
234     if (fputc ('\0', f) == EOF)         // NULL termination
235         return -1;
236 
237     return 0;
238 }
239 
240 
DmpWrite(const char * text,dword * ofs)241 int FileBase::DmpWrite (const char *text, dword *ofs)
242 {
243     *ofs = ad->dmpofs;
244     word dmpreclen = (word) (strlen (text) + 1);    // null terminated !
245     if (fwrite (&dmpreclen, sizeof (dmpreclen), 1, ad->fdmp) != 1)
246         return -1;
247     if (flags & _UniqueDmpLine_) {
248         if (WriteUniqueDmpLine (text, ad->fdmp))
249             return -1;
250     } else {
251         if (fwrite (text, dmpreclen, 1, ad->fdmp) != 1)
252             return -1;
253     }
254     ad->dmpofs += (dmpreclen + sizeof (dmpreclen));
255     return 0;
256 }
257 
258 
AreaAddEntry(const char * filename,word flag,const _stamp * cre_date,const _stamp * mod_date,dword size,const char * desc,const char * acs,const char * path)259 int FileBase::AreaAddEntry (const char *filename, word flag,
260                             const _stamp *cre_date, const _stamp *mod_date,
261                             dword size, const char *desc, const char *acs,
262                             const char *path)
263 {
264     if (ad->LocBase) {
265                         // write .dat
266         FDAT fd;
267         memset (&fd, 0, sizeof (fd));
268 
269         strnupcpy ((char *)fd.name, filename, MAX_FN_LEN);
270         fd.struct_len = sizeof (fd);
271         fd.flag = flag;
272         fd.fdate.msg_st = *mod_date;    // actual filedate
273         fd.udate.msg_st = *cre_date;    // upload filedate
274         fd.fsize = size;
275                                                 // .DMP records
276         if (DmpWrite (desc, &fd.desc))
277             return -1;
278 
279         if (path) {
280             if (DmpWrite (path, &fd.path))
281                 return -1;
282         }
283 
284         if (DmpWrite (acs, &fd.acs))
285             return -1;
286 
287         if (fwrite (&fd, sizeof (fd), 1, ad->fdat) != 1)    // .DAT record
288             return -1;
289     }
290 
291                     // store data for .idx
292 
293     fbd->Store (filename, ad->areanum, ad->datpos);
294 
295     ad->datpos ++;
296 
297     return 0;
298 }
299 
300 
AreaClose(void)301 int FileBase::AreaClose (void)
302 {
303     int error = 0;
304 
305     if (ad->LocBase) {
306         error |= fclose (ad->fdmp);
307         error |= fclose (ad->fdat);
308 
309                         // build files.idx
310 
311         char fullpath[PATH_MAX];
312         strcpy (fullpath, ad->filesbbs);
313         setext (fullpath, ".IDX");
314 
315         error |= fbd->Build (ad->first, fullpath);
316     }
317 
318     return error;
319 }
320 
321 
AddName(const char * areaname,const char * AreaDat)322 void FileBase::AddName (const char *areaname, const char *AreaDat)
323 {
324     if (!AreaDat) {
325         AreaNameList->add (areaname);
326         return;
327     }
328 
329     // if AreaDat -> add the Upload path !
330 
331     FAREADAT fadat;
332 
333     if (fadat.OpenFAreaDat (AreaDat))
334         return;
335 
336     FAH *fap;
337     BOOL WildName = (strpbrk (areaname, "?*") != NULL);
338     while ((fap = fadat.NextArea (FAD_AreasOnly)) != NULL) {
339         if (fnamecmp (PFAS (fap, name), areaname) == 0) {
340             if (*PFAS (fap, uppath) != '\0')
341                 AreaPathList->add (PFAS (fap, uppath));
342             if (!WildName)
343                 break;
344         }
345     }
346 }
347 
348 
AddPath(const char * areapath)349 void FileBase::AddPath (const char *areapath)
350 {
351     AreaPathList->add (areapath);
352 }
353 
354 
IsBuild(const FAH * fahp,word action)355 BOOL FileBase::IsBuild (const FAH *fahp, word action)
356 {
357     if (action & _FBSkipSlow_)
358         return !(fahp->fa.attribs & FA_SLOW);
359 
360     if (!(action & _FBUpdate_) && !(action & _FBMarkedOnly_))
361         return TRUE;
362 
363     if (action & _FBNewAsMarked_)
364         if (!(fahp->fa.attribs & FA_NONEW))
365             if (NewFBBS (PFAS (fahp, downpath), PFAS (fahp, filesbbs)))
366                 return TRUE;
367 
368     if (AreaNameMatch (PFAS (fahp, name)) ||
369         AreaPathMatch (PFAS (fahp, downpath)))
370             return TRUE;
371 
372     return FALSE;
373 }
374 
375 
GetAttribs(word attribs,word action)376 static word GetAttribs (word attribs, word action)
377 {
378     if ((attribs & (FA_AUTODATE|FA_MANDATE|FA_LISTDATE)) == 0)
379         if (action & _FBAutoDate_)
380             attribs |= FA_AUTODATE;
381         else
382             attribs |= FA_MANDATE;
383 
384     return attribs;
385 }
386 
387 
Build(const char * AreaDat,word action,const char * FullIdx,const char * NoDupeIdx,ShowF sf,DupeF df)388 int FileBase::Build (const char *AreaDat, word action, const char *FullIdx,
389                      const char *NoDupeIdx, ShowF sf, DupeF df)
390 {
391     int ret = 0;
392 
393     if (AreaDat) {
394         FAREADAT fadat;
395         if (fadat.OpenFAreaDat (AreaDat) != 0)
396             return -1;
397 
398         FAH *fahp;                              // Build marked areas
399         while ((fahp = fadat.NextArea (FAD_AreasOnly)) != NULL) {
400             if (fahp->fa.attribs & FA_NOINDEX)
401                 continue;
402             if (IsBuild (fahp, action)) {
403                 if (sf)
404                     sf (PFAS (fahp, name), PFAS (fahp, downpath), _BegProc_);
405                 word attribs = GetAttribs (fahp->fa.attribs, action);
406                 int AStat = BuildArea (fadat.AreaNum (),
407                                        PFAS (fahp, downpath),
408                                        PFAS (fahp, filesbbs),
409                                        PFAS (fahp, acs),
410                                        attribs,
411                                        fahp->fa.date_style,
412                                        action & _FBNoLocBase_);
413                 if (sf)
414                     sf (PFAS (fahp, name), PFAS (fahp, downpath), AStat);
415                 if (AStat == _Error_)
416                     ret = -1;
417             }
418         }
419     }
420 
421     ret |= fbd->Build ((action & _FBUpdate_) ? FBD_Update : FBD_BuildAll,
422                         FullIdx, NoDupeIdx, df);
423 
424     return ret;
425 }
426 
427 
BuildArea(word areanum,const char * filepath,const char * filesbbs,const char * acs,word fattr,sword date_style,BOOL NoLocBase)428 int FileBase::BuildArea (word areanum, const char *filepath,
429                          const char *filesbbs, const char *acs,
430                          word fattr, sword date_style, BOOL NoLocBase)
431 {
432     if (fattr & FA_AUTODATE)
433         if (!(Exist (filepath) & _ExDIR_))        // path not existent
434             return _FilePathNotFound_;
435 
436     char filbuf[PATH_MAX];
437     const char *files;
438 
439     if (*filesbbs)
440         files = filesbbs;
441     else {
442         strcpy (filbuf, filepath);
443         strcat (filbuf, "FILES.BBS");
444         files = filbuf;
445     }
446 
447     if ((fattr & FA_SLOW) && (*filesbbs == '\0'))
448         NoLocBase = TRUE;
449 
450     int AStat = AreaOpen (areanum, files, NoLocBase);
451 
452     if (AStat == _BbsPathNotFound_)
453         return AStat;
454 
455     FBBS *fbbs = new FBBS (files, PATH_MAX, DMP_BUF_SIZE, FBBSLSIZE, cont, cpos,
456       (fattr & FA_LISTDATE) ? Max2FbbsDateStyle (date_style) : FBBS_FLAG_NODATESIZE);
457 
458     word flag;
459     time_t date;
460     dword size;
461     char *filename = new char[PATH_MAX];
462     char *descbuf  = new char[DMP_BUF_SIZE];
463 
464     int res = fbbs->GetEntry (filename, descbuf, &flag, FBBS_GE_FIRST,
465                               &date, &size);
466     if (res < 0) {
467         delete fbbs;
468         delete[] descbuf;
469         delete[] filename;
470         if (AreaClose ())
471             return _Error_;
472         else
473             return _NoFilesBbs_;
474     }
475 
476     DIRcl *areadir = NULL;
477 
478     if (fattr & FA_AUTODATE)
479         areadir = new DIRcl (filepath);      // read directory
480 
481     byte *entrybuf = new byte[sizeof (_dir) + PATH_MAX - 1];
482     char *linebuf  = new char[DMP_BUF_SIZE];
483     char *pathbuf  = new char[PATH_MAX];
484 
485     int error = 0;
486 
487     while (res == 0) {
488 
489         _dir *entry = (_dir *) entrybuf;
490         char *path = pathbuf;
491         char *fname;        // points to filename, no path
492 
493         if (fattr & FA_AUTODATE) {
494 #ifndef UNIX
495             if (strpbrk (filename, "\\:"))       // with path
496 #else
497             if (strpbrk (filename, "/:"))       // with path
498 #endif
499                 fname = GetEntry (filename, entry, path);
500             else {                              // in current dir
501                 entry = areadir->Get (filename);  // search in dir
502                 path = NULL;
503                 fname = filename;
504             }
505         } else {        // FA_LISTDATE or FA_MANDATE
506             fname = GetEntry (filename, entry, path, _GE_NODIR_);
507             if (fattr & FA_LISTDATE) {
508                 entry->cdate = 0;
509                 entry->mdate = date;
510                 entry->fsize = size;
511             }
512         }
513 
514         if (fname) {
515             _stamp cre_date, mod_date;
516             dword fsize;
517 
518             if (entry) {
519                 unix2stamp (entry->cdate ? entry->cdate : entry->mdate, &cre_date);
520                 unix2stamp (entry->mdate, &mod_date);
521                 fsize = entry->fsize;
522             } else {            // offline
523                 *((dword *)(&cre_date)) = 0L;
524                 *((dword *)(&mod_date)) = 0L;
525                 fsize = 0;
526                 flag |= FF_OFFLINE;
527             }
528 
529             error |= AreaAddEntry (fname, flag, &cre_date, &mod_date,
530                                    fsize, descbuf, acs, path);
531             if (error)
532                 break;
533         }
534 
535         res = fbbs->GetEntry (filename, descbuf, &flag, FBBS_GE_NEXT, &date, &size);
536     }
537 
538     delete[] pathbuf;
539     delete[] linebuf;
540     delete[] entrybuf;
541 
542     delete fbbs;
543     delete[] descbuf;
544     delete[] filename;
545 
546     if (areadir)
547         delete areadir;
548 
549     error |= AreaClose ();
550 
551     if (error)
552         return _Error_;
553     else
554         return AStat;
555 }
556 
557 
GetEntry(const char * filename,_dir * & entry,char * & path,word flags)558 static char *GetEntry (const char *filename, _dir *&entry, char *&path,
559                        word flags) // gets data on filename
560 {
561     #ifndef __QNXNTO__
562     char *pathend = strrchr (filename, DIRSEP);
563     if (!pathend)
564         pathend = strchr (filename, ':');
565     #else
566     char *pathend = strrchr((char *)filename, DIRSEP);
567     if (!pathend)
568         pathend = strchr((char *)filename, ':');
569     #endif // __QNXNTO__
570 
571     const char *fname;
572     if (!pathend) {           // no path
573         path = NULL;
574         fname = filename;
575     } else {
576         int len = pathend - filename + 1;
577         if (len >= PATH_MAX)
578             return NULL;
579         strncpy (path, filename, len);
580         path[len] = '\0';
581         fname = filename + len;
582     }
583 
584     entry->namelen = (byte) strlen (fname);
585 
586   #if defined (__DOS__)
587     if (entry->namelen >= PATH_MAX)
588         return NULL;
589   #endif
590 
591     strcpy (entry->fname, fname);
592     char *ret = entry->fname;
593 
594     if (flags & _GE_NODIR_) {
595         entry->Got     = TRUE;
596         entry->fsize   = 0;
597         entry->cdate   = 0;
598         entry->mdate   = 0;
599     } else {
600         _ExistStat statbuf;
601         if (!(Exist (filename, &statbuf) & _ExFILE_)) {
602             entry = NULL;
603             return ret;
604         }
605 
606         entry->Got     = TRUE;
607         entry->fsize   = statbuf.size;
608         entry->cdate   = statbuf.ctime;
609         entry->mdate   = statbuf.mtime;
610     }
611 
612     return ret;
613 }
614 
615 
616 
617