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 /*   How to contact the author:  Alberto Pasquale of 2:332/504@fidonet       */
11 /*                               Viale Verdi 106                             */
12 /*                               41100 Modena                                */
13 /*                               Italy                                       */
14 /*                                                                           */
15 /*****************************************************************************/
16 
17 #include "bbsgenlb.hpp"
18 #include <apgenlib.hpp>
19 #include <string.h>
20 #include <fb.h>
21 #include <ctype.h>
22 #include <fcntl.h>
23 #include <unistd.h>
24 
Max2FbbsDateStyle(sword date_style)25 word Max2FbbsDateStyle (sword date_style)
26 {
27     switch (date_style) {                       // as in Max.Prm date_style
28         case 0:
29             return FBBS_FLAG_DATE_USA;
30         case 1:
31             return FBBS_FLAG_DATE_EURO;
32         case 2:
33             return FBBS_FLAG_DATE_JAPAN;
34         case 3:
35             return FBBS_FLAG_DATE_SCIENT;
36     }
37 
38     return FBBS_FLAG_NODATESIZE;
39 }
40 
41 
isComment(const char * line)42 static BOOL isComment (const char *line)
43 {
44     if ((line[0] <= ' ') || (line[0] > '~'))  // empty, ctrl, space, high ascii
45         return TRUE;
46     if ((line[0] == '-') && ((line[1] == ' ') || (line[1] == '\t'))) // comment
47         return TRUE;
48     return FALSE;
49 }
50 
51 
RdOpen()52 int FBBS::RdOpen ()
53 {
54     repeatln = FALSE;
55 
56     if (f) {
57         rewind (f);
58         return 0;
59     }
60 
61     if (WrClose ())
62         return -1;
63 
64     f = fopen (filesbbs, "rt");
65     if (!f)
66         return -1;
67     setvbuf (f, NULL, _IOFBF, 8192);
68 
69     return 0;
70 }
71 
72 
RdClose()73 void FBBS::RdClose ()
74 {
75     if (f) {
76         fclose (f);
77         f = NULL;
78     }
79 }
80 
81 
WrOpen()82 int FBBS::WrOpen ()
83 {
84     if (fileh != -1)
85         return 0;
86 
87     RdClose ();
88     fileh = open (filesbbs, O_WRONLY | O_APPEND);
89     if (fileh == -1)
90         return -1;
91 
92     return 0;
93 }
94 
95 
WrClose()96 int FBBS::WrClose ()
97 {
98     int err = 0;
99     if (fileh != -1) {
100         err |= (close (fileh) == -1);
101         fileh = -1;
102     }
103     return err;
104 }
105 
106 
Trunc()107 int FBBS::Trunc ()
108 {
109     RdClose ();
110     if (WrClose ())
111         return -1;
112 
113     FILE *ft = fopen (filesbbs, "wt");
114     if (!ft)
115         return -1;
116     if (fclose (ft))
117         return -1;
118 
119     return 0;
120 }
121 
122 
Fgetln()123 char *FBBS::Fgetln ()
124 {
125     if (repeatln) {
126         repeatln = FALSE;
127         return lastget;
128     }
129 
130     lastget = _fgets (line, linesize, f);
131     return lastget;
132 }
133 
134 
135 
FBBS(const char * path,int filesize,int descsize,int linesize,char cont,int cpos,word fbbsflags,SkipFile sf,void * ptr)136 FBBS::FBBS (const char *path, int filesize, int descsize, int linesize,
137             char cont, int cpos, word fbbsflags, SkipFile sf, void *ptr)
138 {
139     strcpy (filesbbs, path);
140 
141     if (*filefrompath (path) == '\0')
142         strcat (filesbbs, "files.bbs");
143 
144     this->filesize = filesize;
145     this->descsize = descsize;
146     this->linesize = linesize;
147     this->cont = cont;
148     this->cpos = cpos;
149     this->fbbsflags = fbbsflags;
150     this->sf = sf;
151     this->ptr = ptr;
152     t = NULL;
153 
154     line = new char[linesize];
155 
156     repeatln = FALSE;
157     lastget = NULL;
158     f = NULL;
159     fileh = -1;
160 }
161 
162 
~FBBS()163 FBBS::~FBBS ()
164 {
165     delete[] line;
166     WrClose ();
167     RdClose ();
168     if (t)
169         fclose (t);
170 }
171 
172 
IsInTokLst(const char * line,size_t toklen,const char * toklst)173 static bool IsInTokLst (const char *line, size_t toklen, const char *toklst)
174 {
175     WordStore ws ((char *)toklst);
176 
177     char *tok = ws.GetFirst ();
178     while (tok) {
179         if (strlen (tok) == toklen)
180             if (strncasecmp (line, tok, toklen) == 0)
181                 return true;
182         tok = ws.GetNext ();
183     }
184 
185     return false;
186 }
187 
188 
GetDesc(const char * file,char * desc,word * flag,byte action,const char * repl,time_t * date,dword * size)189 int FBBS::GetDesc (const char *file, char *desc, word *flag, byte action,
190                    const char *repl, time_t *date, dword *size)
191 {
192     if (flag)
193         *flag = 0;
194     if (date)                   	// default inits
195         *date = 0;
196     if (size)
197         *size = ULONG_MAX;
198 
199     FILE *tmp = NULL;
200     char tmpname[PATH_MAX];
201 
202     if (!repl)          		// make sure the repl pointer is not NULL
203         repl = "";
204     if (access (filesbbs, 0)) {		// if files.bbs does not exist
205     	strcpy (tmpname, filesbbs);
206     	tmp = fopen(tmpname, "wt"); 	// create empty file with correct umask
207 	if (!tmp)
208 	  return -1;
209 	if (fclose(tmp) == EOF)
210 	  return -1;
211         return -2;
212     }
213     if (RdOpen ())
214         return -1;
215 
216     if ((action & FBBS_REMOVE) || (*repl)) {    // tmp file must be used
217         strcpy (tmpname, filesbbs);
218         setext (tmpname, ".$$$");
219         tmp = fopen (tmpname, "wt");
220         if (!tmp)
221             return -1;
222         setvbuf (tmp, NULL, _IOFBF, 8192);
223     }
224 
225     BOOL filedone = FALSE,
226          repldone = FALSE;
227     int filelen = strlen (file);
228 
229     int res = 0;
230     while (Fgetln ()) {
231         if (!isComment (line)) {
232             int thislen = strcspn (line, " \t");
233             if (thislen == filelen)
234                 if (strncasecmp (line, file, filelen) == 0) {
235                     char *p = line+thislen;
236                     p += strspn (p, " \t");
237                     res |= descopy (p, desc, flag, date, size,
238                                     (action & FBBS_REMOVE) ? NULL : tmp);
239                     if (res)
240                         break;
241                     filedone = TRUE;
242                     continue;
243                 }
244             if (*repl)
245             if (IsInTokLst (line, thislen, repl)) {
246                 res |= descopy ();
247                 if (res)
248                     break;
249                 repldone = TRUE;
250                 continue;
251             }
252         }
253         if (tmp)
254             res |= (_fputs (line, tmp) == EOF);
255         if (res)
256             break;
257     }
258 
259     if (tmp) {
260         res |= (fclose (tmp) == EOF);
261         tmp = NULL;
262 
263         if ((res == 0) &&
264             (repldone || (filedone && (action & FBBS_REMOVE)))) {
265             RdClose ();
266             tunlink (filesbbs, 20);
267             res |= rename (tmpname, filesbbs);
268         } else
269             unlink (tmpname);
270     }
271 
272     if (res)
273         return -1;
274 
275     res = 0;
276     if (!filedone)
277         res |= 1;
278     if (*repl && !repldone)
279         res |= 2;
280 
281     return res;
282 }
283 
284 
GetGenEntry(char * file,char * desc,word * flag,BOOL first,time_t * date,dword * size)285 int FBBS::GetGenEntry (char *file, char *desc, word *flag, BOOL first,
286                        time_t *date, dword *size)
287 {
288     if (flag)
289         *flag = 0;
290     if (date)                   // default inits
291         *date = 0;
292     if (size)
293         *size = ULONG_MAX;
294 
295     if (first || !f) {
296 
297         if (RdOpen ())
298             return -1;
299 
300         if (t) {
301             fclose (t);
302             t = NULL;
303         }
304         if (sf) {       // open temporary file
305             char filetmp[PATH_MAX];
306             strcpy (filetmp, filesbbs);
307             setext (filetmp, "$$0");
308             t = fopen (filetmp, "wt");
309             if (!t)
310                 return -1;
311         }
312     }
313 
314     BOOL EntryRemoved = FALSE;
315 
316     int res = 0;
317     BOOL found = (Fgetln () != NULL);
318     BOOL comment = FALSE;
319     if (found) {
320         comment = isComment (line);
321         if (comment) {
322             *file = '\0';
323             strzcpy (desc, line, descsize);
324             if (t)
325                 res |= (_fputs (line, t) == EOF);
326         } else {
327             const char *p = line;
328             GetName (p, file, filesize);  // get file name
329             if (sf)
330                 EntryRemoved = sf (file, ptr);
331             res |= descopy (p, desc, flag, date, size, EntryRemoved ? NULL : t);
332         }
333     }
334 
335     if (res || !found) {
336         if (t) {
337             res |= fclose (t);
338             t = NULL;
339             char filetmp[PATH_MAX];
340             strcpy (filetmp, filesbbs);
341             setext (filetmp, "$$0");
342             if (!res) {         // rename new bbs on old one
343                 RdClose ();
344                 tunlink (filesbbs, 20);
345                 res |= rename (filetmp, filesbbs);
346             } else
347                 unlink (filetmp);
348         }
349         if (res)
350             return -1;
351         else
352             return 1;
353     }
354 
355     if (EntryRemoved)
356         return 3;
357     if (comment)
358         return 2;
359     return 0;
360 }
361 
362 
GetEntry(char * file,char * desc,word * flag,BOOL first,time_t * date,dword * size)363 int FBBS::GetEntry (char *file, char *desc, word *flag, BOOL first,
364                     time_t *date, dword *size)
365 {
366     int ret;
367     do {
368         ret = GetGenEntry (file, desc, flag, first, date, size);
369         first = FBBS_GE_NEXT;
370     } while (ret == 2);       // skip comment lines
371     return ret;
372 }
373 
374 
GetFileFlags(const char * & desc)375 static word GetFileFlags (const char *&desc)
376 {
377     char flags[10];
378     word flag = FF_FILE;            // get flags if available
379     while (*desc == '/') {
380         GetName (desc, flags, sizeof (flags)); // get flag string
381         char *f = flags;
382         while (*f) {
383             switch (tolower (*f)) {
384                 case 't':
385                     flag |= FF_NOTIME;
386                     break;
387                 case 'b':
388                     flag |= FF_NOBYTES;
389                     break;
390             }
391             f ++;
392         }
393     }
394     return flag;
395 }
396 
397 
Flags2Df(word fbbsflags)398 static byte Flags2Df (word fbbsflags)
399 {
400     if (fbbsflags & FBBS_FLAG_NODATESIZE)
401         return DF_DEFAULT;
402 
403     switch (fbbsflags & FBBS_FLAG_DATEFORMAT) {
404         case FBBS_FLAG_DATE_USA:
405             return DF_USA;
406         case FBBS_FLAG_DATE_EURO:
407             return DF_EURO;
408         case FBBS_FLAG_DATE_JAPAN:
409             return DF_JAPAN;
410         case FBBS_FLAG_DATE_SCIENT:
411             return DF_SCIENT;
412     }
413 
414     return DF_DEFAULT;
415 }
416 
417 
GetDateSize(const char * & desc,time_t * datep,dword * sizep,word fbbsflags)418 static void GetDateSize (const char *&desc, time_t *datep, dword *sizep, word fbbsflags)
419 {
420     if (fbbsflags & FBBS_FLAG_NODATESIZE) {
421         if (datep)
422             *datep = 0;
423         if (sizep)
424             *sizep = ULONG_MAX;
425         return;
426     }
427 
428     byte date_format = Flags2Df (fbbsflags);
429 
430     const int toksize = 10;
431     char tok[2][toksize];
432     const char *p = desc;
433 
434     GetName (p, tok[0], toksize);
435     GetName (p, tok[1], toksize);
436 
437     time_t date;
438     dword size;
439     int isize = 1; // let's suppose tok[1] contains size
440 
441     date = dates2unix (tok[0], date_format);
442 
443     if (date == 0) {    // if first item is not date, let's try with second one
444         isize = 0;
445         date = dates2unix (tok[1], date_format);
446     }
447 
448     if (date) {
449         char *endptr;
450         size = strtoul (tok[isize], &endptr, 10);
451         if ((*endptr != '\0') || (size == ULONG_MAX)) { // token is not length only
452             date = 0;
453             size = 0;
454         }
455     } else
456         size = 0;
457 
458     if (date)
459         desc = p;
460 
461     if (datep)
462         *datep = date;
463     if (sizep)
464         *sizep = size;
465 }
466 
467 
468 
descopy(const char * p,char * desc,word * flag,time_t * date,dword * size,FILE * t)469 int FBBS::descopy (const char *p, char *desc, word *flag, time_t *date, dword *size,
470                    FILE *t)
471 {
472     if (!p && (desc || flag || date || size))
473         return -1;
474 
475     int desclen = 0;
476 
477     if (p) {
478         GetDateSize (p, date, size, fbbsflags);
479         word flags = GetFileFlags (p);
480         if (flag)
481             *flag = flags;
482         if (desc)
483             desclen = descAddLine (p, desc, 0);
484     }
485 
486     int res = 0;
487     if (t)
488         res |= (_fputs (line, t) == EOF);
489 
490     if (cpos != -1) {
491         while (Fgetln ()) {                  // get continuation lines (no '\n')
492             int spacelen = strspn (line, " \t");
493             char *firstchar = line+spacelen;
494             if (cont != ' ') {
495                 if ((spacelen == 0) && (cpos != 0))
496                     break;
497                 if (*firstchar != cont)       // not a continuation line
498                     break;
499                 else {
500                     firstchar ++;       // skip cont char
501                     if ((*firstchar == ' ') && !(fbbsflags & FBBS_FLAG_NOCONTSPACE))
502                         firstchar ++;     // skip space following cont
503                 }
504             } else {
505                 if ((spacelen < cpos) && *firstchar) // not a continuation line
506                     break;
507                 if (spacelen > cpos)
508                     firstchar = line + cpos;
509             }
510                         // this is a continuation (description) line
511             if (desc)
512                 desclen = descAddLine (firstchar, desc, desclen);
513             if (t)
514                 res |= (_fputs (line, t) == EOF);
515         }
516 
517         repeatln = TRUE;        // next Fgetln will repeat last result
518     }
519 
520     if (res)
521         return -1;
522     return 0;
523 }
524 
525 
descAddLine(const char * p,char * desc,int desclen)526 int FBBS::descAddLine (const char *p, char *desc, int desclen)
527 {
528     int remsize = descsize - desclen;
529     if (remsize < 3)       // space for '\n', at least one char, '\0'
530         return desclen;
531 
532     if (desclen > 0) {
533         desc[desclen++] = '\n';   // add separator
534         remsize --;
535     }
536 
537     char *tn = stpzcpy (desc+desclen, p, remsize);
538     return int (tn - desc);
539 }
540 
541 
PutGenEntry(const char * file,const char * desc,word flag,const char * befdesc,time_t date,dword size)542 int FBBS::PutGenEntry (const char *file, const char *desc, word flag,
543                        const char *befdesc, time_t date, dword size)
544 {
545     if (WrOpen ())
546         return -1;
547 
548     if (!(flag & FF_SAFE)) {
549         char *p = (char *)desc;
550         while (*p) {          // remove trojan control chars
551             if ((*p < ' ') && (*p != '\n'))
552                 *p = ' ';
553             p++;
554         }
555     }
556 
557     BOOL isComment = FALSE;     // is this a comment ?
558     if (!file)
559         isComment = TRUE;
560     else if (*file == '\0')
561         isComment = TRUE;
562 
563     int err = 0;
564 
565     if (!isComment) {
566         const char *b = desc;                     // start of 1st line
567         const char *e = b + strcspn (b, "\n");      // end of 1st line
568         int linelen = int (e - b);          // lenght of 1st line
569 
570         int befdesclen = 0;
571         if (befdesc)
572             befdesclen = strlen (befdesc);
573 
574         int filelen = strlen (file);
575         err |= (write (fileh, file, filelen) == -1);  // filename
576         int spacelen = __max (13 - filelen, 1);
577         err |= (write (fileh, "             ", spacelen) == -1);  // blanks
578 
579         if (!(fbbsflags & FBBS_FLAG_NODATESIZE)) {
580             char sizes[13]; char dates[10];
581             sprintf (sizes, "%7lu ", size);
582             err |= (write (fileh, sizes, strlen (sizes)) == -1);  // size
583             unix2dates (date, dates, Flags2Df (fbbsflags));
584             strcat (dates, " ");
585             err |= (write (fileh, dates, strlen (dates)) == -1);  // date
586         }
587 
588         int flaglen = 0;                            // flags
589         if (flag & (FF_NOTIME|FF_NOBYTES)) {
590             char flags[5];
591             flags[flaglen++] = '/';
592             if (flag & FF_NOTIME)
593                 flags[flaglen++] = 't';
594             if (flag & FF_NOBYTES)
595                 flags[flaglen++] = 'b';
596             flags[flaglen++] = ' ';
597             flags[flaglen] = '\0';
598             err |= (write (fileh, flags, flaglen) == -1);
599         }
600 
601         if (befdesclen)
602             err |= (write (fileh, befdesc, befdesclen) == -1);
603         int writelen = __min (linelen, linesize - filelen - spacelen - flaglen - befdesclen - 2);
604         if (writelen)
605             err |= (write (fileh, desc, writelen) == -1);
606         err |= (write (fileh, "\n", 1) == -1);
607 
608         if (cpos != -1) {     // output continuation lines
609                                           // init start of continuation lines
610             char blank[] = "                                                                                ";
611             if ((size_t)cpos > sizeof (blank) - 3) // space for cont, ' ', '\0'
612                 cpos = sizeof (blank) - 3;
613             int contlen = cpos;
614 
615             if (cont != ' ') {
616                 blank[cpos] = cont;
617                 if (fbbsflags & FBBS_FLAG_NOCONTSPACE)
618                     contlen += 1;
619                 else
620                     contlen += 2;       // include cont and space
621             }
622 
623             while (*e) {            // while lines available
624                 b = e + 1;              // begin on char following '\n'
625                 e = b + strcspn (b, "\n");   // end of line
626                 linelen = int (e - b);  // length of line
627                 err |= (write (fileh, blank, contlen) == -1);  // blanks and cont
628                 if (linelen)
629                     err |= (write (fileh, b, __min (linelen, linesize - contlen - 2)) == -1);  // desc line
630                 err |= (write (fileh, "\n", 1) == -1);
631             }
632         }
633 
634     } else {            // isComment
635         int writelen = strlen (desc);
636         if (writelen)
637             err |= (write (fileh, desc, writelen) == -1);
638         err |= (write (fileh, "\n", 1) == -1);
639     }
640 
641     return err;
642 }
643 
644 
SetDesc(const char * file,const char * desc,word flag,const char * befdesc,time_t date,dword size)645 int FBBS::SetDesc (const char *file, const char *desc, word flag, const char *befdesc,
646                    time_t date, dword size)
647 {
648     int err = 0;
649 
650     err |= PutGenEntry (file, desc, flag, befdesc, date, size);
651     err |= WrClose ();
652 
653     return err;
654 }
655 
656 
657 
658