1 /******************************************************************************
2  * FIDOCONFIG --- library for fidonet configs
3  ******************************************************************************
4  * afixcmd.c : common areafix commands
5  *
6  * Compiled from hpt/areafix hpt/toss hpt/pkt
7  * by Max Chernogor <mihz@mail.ru>, 2:464/108@fidonet
8  *
9  * This file is part of FIDOCONFIG library (part of the Husky FIDOnet
10  * software project)
11  *
12  * This is free software; you can redistribute it and/or modify it
13  * under the terms of the GNU General Public License as published
14  * by the Free Software Foundation; either version 2, or (at your option)
15  * any later version.
16  *
17  * FIDOCONFIG library is distributed in the hope that it will be useful,
18  * but WITHOUT ANY WARRANTY; without even the implied warranty of
19  * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
20  * General Public License for more details.
21  *
22  * You should have received a copy of the GNU General Public License
23  * along with FIDOCONFIG library; see the file COPYING.  If not, write
24  * to the Free Software Foundation, 675 Mass Ave, Cambridge, MA 02139, USA
25  *
26  * See also http://www.gnu.org
27  *****************************************************************************
28  * $Id$
29  */
30 #include <stdlib.h>
31 #include <string.h>
32 #include <ctype.h>
33 #include <errno.h>
34 #include <sys/types.h>
35 #include <sys/stat.h>
36 
37 #include <huskylib/huskylib.h>
38 
39 #ifdef HAS_UNISTD_H
40 #   include <unistd.h>
41 #endif
42 
43 #ifdef HAS_IO_H
44 #   include <io.h>
45 #endif
46 
47 #ifdef HAS_STRINGS_H
48 #   include <strings.h>
49 #endif
50 
51 /* export functions from DLL */
52 #define DLLEXPORT
53 #include <huskylib/huskyext.h>
54 
55 #include "afixcmd.h"
56 #include "common.h"
57 
expandCfgLine(char * cfgline)58 char* expandCfgLine(char* cfgline)
59 {
60    cfgline = trimLine(cfgline);
61    cfgline = shell_expand(cfgline);
62    cfgline = vars_expand(cfgline);
63    return cfgline;
64 }
65 
FindTokenPos4Link(char ** confName,char * ftoken,char * fftoken,s_link * link,long * start,long * end)66 int FindTokenPos4Link(char **confName, char* ftoken, char *fftoken, s_link *link, long* start, long* end)
67 {
68    char* cfgline, *line, *token, *linkConfName;
69    long   linkstart=0;
70 
71    *start=0; *end=0;
72 
73    if (init_conf(*confName))
74       return 0;
75 
76    while ((cfgline = configline()) != NULL) {
77       cfgline = expandCfgLine(cfgline);
78       line = cfgline;
79       token = strseparate(&line, " \t");
80       if (!token || strcasecmp(token, "link")) {
81          nfree(cfgline);
82          continue;
83       }
84 linkliner:
85       nfree(cfgline);
86       for (;;) {
87          if ((cfgline = configline()) == NULL) {
88             close_conf();
89             return 0;
90          }
91          cfgline = expandCfgLine(cfgline);
92          if (!*cfgline) {
93             nfree(cfgline);
94             continue;
95          }
96          line = cfgline;
97          token = strseparate(&line, " \t");
98          if (!token) {
99             nfree(cfgline);
100             continue;
101          }
102          if (stricmp(token, "link") == 0)
103             goto linkliner;
104          if (stricmp(token, "aka") == 0) break;
105          nfree(cfgline);
106       }
107       token = strseparate(&line, " \t");
108       if (!token || testAddr(token, link->hisAka) == 0) {
109          nfree(cfgline);
110          continue;
111       }
112       nfree(cfgline);
113       linkstart = get_hcfgPos();
114       linkConfName = sstrdup(getCurConfName());
115       /* try to find alternative token
116          ex.: areaFixPwd should be inserted after defaultPwd */
117       if (fftoken) {
118          for (;;) {
119             if ((cfgline = configline()) == NULL) {
120 /* !!! val: delete this if no bugs !!!
121                *start = *end   = linkstart;
122                *confName = linkConfName;
123                close_conf();
124                return 0;*/
125                fseek(get_hcfg(), linkstart, SEEK_SET);
126                break;
127             }
128             cfgline = expandCfgLine(cfgline);
129             if (!*cfgline) {
130                nfree(cfgline);
131                continue;
132             }
133             line = cfgline;
134             token = strseparate(&line, " \t");
135             if (token && stricmp(token, "link") == 0)
136             {
137 /* !!! val: delete this if no bugs !!!
138                *start = *end   = linkstart;
139                *confName = linkConfName;
140                close_conf();
141                return 0;*/
142                fseek(get_hcfg(), linkstart, SEEK_SET);
143                break;
144             }
145             if (token && stricmp(token, fftoken) == 0) break;
146             nfree(cfgline);
147          }
148          nfree(cfgline);
149          linkstart = get_hcfgPos();
150          linkConfName = sstrdup(getCurConfName());
151       }
152       for (;;) {
153          if ((cfgline = configline()) == NULL) {
154             *start = *end   = linkstart;
155             *confName = linkConfName;
156             close_conf();
157             return 0;
158          }
159          cfgline = expandCfgLine(cfgline);
160          if (!*cfgline) {
161             nfree(cfgline);
162             continue;
163          }
164          line = cfgline;
165          token = strseparate(&line, " \t");
166          if (token && stricmp(token, "link") == 0)
167          {
168             *start = *end   = linkstart;
169             *confName = linkConfName;
170             close_conf();
171             return 0;
172          }
173          if (token && stricmp(token, ftoken) == 0) break;
174          nfree(cfgline);
175       }
176       /*  remove line */
177       nfree(cfgline);
178       *start = getCurConfPos();
179       *end   = get_hcfgPos();
180       *confName = sstrdup(getCurConfName());
181       close_conf();
182       return 1;
183    }
184    return 0;
185 }
186 
InsertCfgLine(char * confName,char * cfgLine,long strbeg,long strend)187 int InsertCfgLine(char *confName, char* cfgLine, long strbeg, long strend)
188 {
189     char *line = NULL, *newname = NULL, *p;
190     FILE *f_conf, *f_newconf;
191     long endpos,curpos,cfglen;
192     int  openro = 0;
193 
194     f_conf = f_newconf = NULL;
195     if ((strbeg == 0 && strend == 0) || confName == NULL)
196 	return 0;
197 
198     if ((f_conf = fopen(confName, "r+b")) == NULL) {
199 	if ((f_conf = fopen(confName, "rb")) == NULL) {
200 	    w_log(LL_ERR, "Cannot open config file %s: %s\n", confName, strerror(errno));
201 	    return 0;
202 	}
203 	openro = 1;
204     }
205     if (fseek(f_conf, 0L, SEEK_END) != 0) {
206 	w_log(LL_ERR, "Cannot seek config file %s: %s\n", confName, strerror(errno));
207 	fclose(f_conf);
208 	return 0;
209     }
210     endpos = ftell(f_conf);
211     curpos = strend;
212     cfglen = endpos - curpos;
213     newname = (char *)smalloc(strlen(confName) + 5);
214     strcpy(newname, confName);
215     p=strrchr(newname, '.');
216     if (p==NULL || strchr(p, PATH_DELIM))
217 	strcat(newname, ".tmp");
218     else
219 	strcpy(p, ".tmp");
220     if ( (f_newconf = fopen(newname, "wb")) == NULL ) {
221         /* we have no write access to this directory? */
222         /* change config "in place" */
223         if (openro) {
224             w_log(LL_ERR, "Cannot open temp file %s: %s\n", newname, strerror(errno));
225             nfree(newname);
226             fclose(f_conf);
227             return 0;
228         }
229         nfree(newname);
230         line = (char*) smalloc((size_t) cfglen);
231         fseek(f_conf, curpos, SEEK_SET);
232         if (fread(line, sizeof(char), cfglen, f_conf) != (size_t)cfglen) {
233             w_log(LL_ERR, "Cannot read config file %s: %s\n", confName, strerror(errno));
234             nfree(line);
235             fclose(f_conf);
236             return 0;
237         }
238         fseek(f_conf, strbeg, SEEK_SET);
239         setfsize( fileno(f_conf), strbeg );
240         if (cfgLine) /*  line not deleted */
241         {
242             if (fprintf(f_conf, "%s%s", cfgLine, cfgEol()) != (int)(strlen(cfgLine)+strlen(cfgEol())))
243                 w_log(LL_ERR, "Cannot write config file %s: %s\n", confName, strerror(errno));
244         }
245         if (fwrite(line, sizeof(char), cfglen, f_conf) != (size_t)cfglen ||
246             fflush(f_conf) != 0)
247         {
248             w_log(LL_ERR, "Cannot write config file %s: %s\n", confName, strerror(errno));
249         }
250         fclose(f_conf);
251         nfree(line);
252     } else {
253         /* make new config-file and rename it */
254 #ifdef __UNIX__
255         struct stat st;
256         if (fstat(fileno(f_conf), &st) == 0)
257             fchmod(fileno(f_newconf), (st.st_mode & 01777) | 0400);
258 #endif
259         line = (char*) smalloc(cfglen > strbeg ? cfglen : strbeg);
260         fseek(f_conf, 0L, SEEK_SET);
261         if (fread(line, sizeof(char), strbeg, f_conf) < (size_t)strbeg) {
262             w_log(LL_ERR, "Cannot read config file %s: %s\n", confName, strerror(errno));
263 errwriteconf:
264             fclose(f_conf);
265             fclose(f_newconf);
266             unlink(newname);
267             nfree(line);
268             nfree(newname);
269             return 0;
270         }
271         if (fwrite(line, sizeof(char), strbeg, f_newconf) < (size_t)strbeg) {
272             w_log(LL_ERR, "Cannot write config file %s: %s\n", newname, strerror(errno));
273             goto errwriteconf;
274         }
275         if (cfgLine) {
276             if (fprintf(f_newconf, "%s%s", cfgLine, cfgEol()) != (int)(strlen(cfgLine)+strlen(cfgEol())))
277             {
278                 w_log(LL_ERR, "Cannot write config file %s: %s\n", newname, strerror(errno));
279                 goto errwriteconf;
280             }
281         }
282         fseek(f_conf, curpos, SEEK_SET);
283         if (fread(line, sizeof(char), cfglen, f_conf) != (size_t)cfglen) {
284             w_log(LL_ERR, "Cannot read config file %s: %s\n", confName, strerror(errno));
285             goto errwriteconf;
286         }
287         if (fwrite(line, sizeof(char), cfglen, f_newconf) != (size_t)cfglen ||
288             fflush(f_newconf) != 0) {
289             w_log(LL_ERR, "Cannot write config file %s: %s\n", newname, strerror(errno));
290             goto errwriteconf;
291         }
292         fclose(f_newconf);
293         fclose(f_conf);
294         nfree(line);
295         /* save old config as *.bak? */
296 /*
297 #ifndef __UNIX__
298         unlink(confName);
299 #endif
300 */
301 /*        if (rename(newname, confName)) { */
302         if (move_file(newname, confName,1)) {
303             w_log(LL_ERR, "Cannot rename config file %s->%s: %s\n", newname, confName, strerror(errno));
304             nfree(newname);
305             return 0;
306         }
307         nfree(newname);
308     }
309     return 1;
310 }
311 
312     /*  opt = 0 - AreaFix */
313     /*  opt = 1 - AutoPause */
Changepause(char * confName,s_link * link,int opt,int type)314 int Changepause(char *confName, s_link *link, int opt, int type)
315 {
316     long  strbeg=0;
317     long  strend=0;
318 
319     FindTokenPos4Link(&confName, "pause", NULL, link, &strbeg, &strend);
320 
321     link->Pause ^= type;
322 
323     if       (link->Pause == NOPAUSE) {
324         if(InsertCfgLine(confName, "Pause off", strbeg, strend))
325             w_log('8', "areafix: system %s set active",	aka2str(link->hisAka));
326     } else if(link->Pause == (ECHOAREA|FILEAREA)) {
327         if(InsertCfgLine(confName, "Pause on", strbeg, strend))
328             w_log('8', "%s: system %s set passive",
329             opt ? "autopause" : "areafix", aka2str(link->hisAka));
330     } else if(link->Pause == ECHOAREA) {
331         if(InsertCfgLine(confName, "Pause Earea", strbeg, strend))
332             w_log('8', "%s: system %s set passive only for echoes",
333             opt ? "autopause" : "areafix", aka2str(link->hisAka));
334     } else {
335         if(InsertCfgLine(confName, "Pause Farea", strbeg, strend))
336             w_log('8', "%s: system %s set passive only for file echoes",
337             opt ? "autopause" : "areafix", aka2str(link->hisAka));
338     }
339     nfree(confName);
340     return 1;
341 }
342 
testAddr(char * addr,hs_addr hisAka)343 int testAddr(char *addr, hs_addr hisAka)
344 {
345     hs_addr aka = {0};
346     parseFtnAddrZS(addr, &aka);
347     if (addrComp(aka, hisAka)==0) return 1;
348     return 0;
349 }
350 
DelLinkFromString(char * line,hs_addr linkAddr)351 int DelLinkFromString(char *line, hs_addr linkAddr)
352 {
353     int rc = 1;
354     char *end = NULL;
355     char *beg = NULL;
356 
357     w_log(LL_FUNC, "::DelLinkFromString() begin");
358 
359     beg = strrchr(line, '"'); /* seek end comment pointer (quote char) */
360     if(!beg)  beg = line;     /* if not found then seek from begin */
361     beg++;                    /* process next token */
362     while(*beg)               /* while not end of string */
363     {
364         while(*beg && isspace(*beg)) beg++; /* skip spaces */
365         if(*beg && isdigit(*beg) && testAddr(beg, linkAddr))
366         {
367             rc = 0;
368             break;
369         }
370         while(*beg && !isspace(*beg)) beg++; /* skip token */
371     }
372     if(rc == 0) /* beg points to begin of unsubscribed address */
373     {
374         end = beg;
375         while(*beg && !isspace(*beg)) beg++; /* skip token */
376         while(*beg && !isdigit(*beg)) beg++; /* find for next link */
377         if(beg && *beg)
378         {
379             memmove(end,beg,strlen(beg)+1);
380         }
381         else
382         {
383             end--;
384             *end = '\0';
385         }
386     }
387 
388     w_log(LL_FUNC, "%::DelLinkFromString() end");
389     return rc;
390 }
391 
IsAreaAvailable(char * areaName,char * fileName,char ** desc,int retd)392 int IsAreaAvailable(char *areaName, char *fileName, char **desc, int retd) {
393     FILE *f;
394     char *line, *token, *running;
395 
396     if (fileName==NULL || areaName==NULL) return 0;
397 
398     if ((f=fopen(fileName,"r")) == NULL) {
399 	w_log('8',"Allfix: cannot open file \"%s\"",fileName);
400 	return 0;
401     }
402 
403     while ((line = readLine(f)) != NULL) {
404 	line = trimLine(line);
405 	if (line[0] != '\0') {
406 
407 	    running = line;
408 	    token = strseparate(&running, " \t\r\n");
409 
410 	    if (token && areaName && stricmp(token, areaName)==0) {
411 		/*  return description if needed */
412 		if (retd) {
413 		    *desc = NULL;
414 		    if (running) {
415 			/* strip "" at the beginning & end */
416 			if (running[0]=='"' && running[strlen(running)-1]=='"') {
417 			    running++; running[strlen(running)-1]='\0';
418 			}
419 			/* change " -> ' */
420 			token = running;
421 			while (*token!='\0') {
422 			    if (*token=='"') *token='\'';
423 			    token++;
424 			}
425 			xstrcat(&(*desc), running);
426 		    }
427 		}
428 		nfree(line);
429 		fclose(f);
430 		return 1;
431 	    }
432 	}
433 	nfree(line);
434     }
435     /*  not found */
436     fclose(f);
437     return 0;
438 }
439 
Addlink(s_fidoconfig * config,s_link * link,s_area * area)440 void Addlink(s_fidoconfig *config, s_link *link, s_area *area)
441 {
442     char *ExclMask;
443     UINT i;
444 
445     if(area)
446     {
447         s_arealink *arealink;
448         area->downlinks = srealloc(area->downlinks, sizeof(s_arealink*)*(area->downlinkCount+1));
449         arealink = area->downlinks[area->downlinkCount] = (s_arealink*) scalloc(1, sizeof(s_arealink));
450         arealink->link = link;
451         area->downlinkCount++;
452 
453         setLinkAccess(config, area, arealink);
454 
455         if (config->readOnlyCount) {
456             for (i=0; i < config->readOnlyCount; i++) {
457                 if(config->readOnly[i].areaMask[0] != '!') {
458                     if (patimat(area->areaName, config->readOnly[i].areaMask) &&
459                         patmat(aka2str(link->hisAka), config->readOnly[i].addrMask)) {
460                             arealink->import = 0;
461                     }
462                 } else {
463                     ExclMask = config->readOnly[i].areaMask;
464                     ExclMask++;
465                     if (patimat(area->areaName, ExclMask) &&
466                         patmat(aka2str(link->hisAka), config->readOnly[i].addrMask)) {
467                             arealink->import = 1;
468                     }
469                 }
470             }
471         }
472 
473         if (config->writeOnlyCount) {
474             for (i=0; i < config->writeOnlyCount; i++) {
475                 if(config->writeOnly[i].areaMask[0] != '!') {
476                     if (patimat(area->areaName, config->writeOnly[i].areaMask) &&
477                         patmat(aka2str(link->hisAka), config->writeOnly[i].addrMask)) {
478                             arealink->aexport = 0;
479                     }
480                 } else {
481                     ExclMask = config->writeOnly[i].areaMask;
482                     ExclMask++;
483                     if (patimat(area->areaName, ExclMask) &&
484                         patmat(aka2str(link->hisAka), config->writeOnly[i].addrMask)) {
485                             arealink->aexport = 1;
486                     }
487                 }
488             }
489         }
490     }
491 }
492 
RemoveLink(s_link * link,s_area * area)493 void RemoveLink(s_link *link, s_area *area)
494 {
495     if(area) /* remove link from echoarea */
496     {
497         int i = isAreaLink(link->hisAka, area);
498         if (i != -1) {
499             nfree(area->downlinks[i]);
500             area->downlinks[i] = area->downlinks[area->downlinkCount-1];
501             area->downlinkCount--;
502         }
503     }
504 }
505 
506 
507 /* ---------------- areafix checking stuff --------------*/
508 
509 /* test area-link pair to mandatory */
mandatoryCheck(s_area * area,s_link * link)510 int mandatoryCheck(s_area *area, s_link *link)
511 {
512     int i;
513 
514     w_log(LL_FUNC, __FILE__ "::mandatoryCheck()");
515 
516     if (grpInArray(area->group,link->optGrp,link->numOptGrp)&&link->mandatory){
517       w_log(LL_FUNC, __FILE__ "::mandatoryCheck() rc=1");
518       return 1;
519     }
520     if (link->numOptGrp==0 && link->mandatory){
521       w_log(LL_FUNC, __FILE__ "::mandatoryCheck() rc=1");
522       return 1;
523     }
524     if (area->mandatory){
525       w_log(LL_FUNC, __FILE__ "::mandatoryCheck() rc=1");
526       return 1;
527     }
528     if ((i=isAreaLink(link->hisAka, area))!=-1){
529       w_log(LL_FUNC, __FILE__ "::mandatoryCheck() rc=%d", area->downlinks[i]->mandatory);
530       return area->downlinks[i]->mandatory;
531     }
532       w_log(LL_FUNC, __FILE__ "::mandatoryCheck() rc=0");
533     return 0;
534 }
535 
536 /* test area-link pair to manual */
manualCheck(s_area * area,s_link * link)537 int manualCheck(s_area *area, s_link *link)
538 {
539     int i;
540 
541     w_log(LL_FUNC, __FILE__ "::manualCheck()");
542 
543     if (grpInArray(area->group,link->optGrp,link->numOptGrp)&&link->manual){
544       w_log(LL_FUNC, __FILE__ "::manualCheck() rc=1");
545       return 1;
546     }
547     if (link->numOptGrp==0 && link->manual){
548       w_log(LL_FUNC, __FILE__ "::manualCheck() rc=1");
549       return 1;
550     }
551     if (area->manual) {
552       w_log(LL_FUNC, __FILE__ "::manualCheck() rc=1");
553       return 1;
554     }
555     if ((i=isAreaLink(link->hisAka, area))!=-1){
556       w_log(LL_FUNC, __FILE__ "::manualCheck() rc=%d", area->downlinks[i]->manual);
557       return area->downlinks[i]->manual;
558     }
559     w_log(LL_FUNC, __FILE__ "::manualCheck() rc=0");
560     return 0;
561 }
562 
subscribeCheck(s_area * area,s_link * link)563 int subscribeCheck(s_area *area, s_link *link)
564 {
565     int found = 0;
566     s_fidoconfig *config = theApp.config;
567 
568     w_log( LL_FUNC, "%s::subscribeCheck() begin", __FILE__ );
569 
570     if (isLinkOfArea(link, area)) return 0;
571 
572     if (area->group) {
573         if (config->numPublicGroup)
574             found = grpInArray(area->group,config->PublicGroup,config->numPublicGroup);
575         if (!found && link->numAccessGrp)
576             found = grpInArray(area->group,link->AccessGrp,link->numAccessGrp);
577     } else found = 1;
578 
579     if ((area->levelwrite > link->level) && (area->levelread > link->level))
580         found = 0;
581 
582     if (!found){
583         w_log( LL_FUNC, "%s::subscribeCheck() end, rc=2", __FILE__ );
584         return 2;
585     }
586     if (area->hide) return 3;
587 
588     w_log( LL_FUNC, "%s::subscribeCheck() end, rc=1", __FILE__ );
589     return 1;
590 }
591 
subscribeAreaCheck(s_area * area,char * areaname,s_link * link)592 int subscribeAreaCheck(s_area *area, char *areaname, s_link *link)
593 {
594     int rc=4;
595 
596     w_log( LL_SRCLINE, "%s::subscribeAreaCheck()", __FILE__ );
597 
598     if( (!areaname)||(!areaname[0]) ){
599       w_log( LL_SRCLINE, "%s::subscribeAreaCheck() Failed (areaname empty) rc=%d", __FILE__, rc );
600       return rc;
601     }
602     if (patimat(area->areaName,areaname)==1) {
603 	rc=subscribeCheck(area, link);
604 	/*  0 - already subscribed / linked */
605 	/*  1 - need subscribe / not linked */
606 	/*  2 - no access */
607 	/*  3 - area is hidden */
608     }
609     /*  else: this is another area */
610     w_log( LL_SRCLINE, "%s::subscribeAreaCheck() end rc=%d", __FILE__, rc );
611     return rc;
612 }
613 
614 /* test link for areas quantity limit exceed
615  * return 0 if not limit exceed
616  * else return not zero
617  */
limitCheck(s_link * link)618 int limitCheck(s_link *link) {
619     register unsigned int i,n;
620     unsigned echoLimit = 0;
621     unsigned areaCount = 0;
622     ps_area  areas = NULL;
623 
624     if      (theApp.module == M_HPT) {
625         echoLimit = link->areafix.echoLimit;
626         areaCount = theApp.config->echoAreaCount;
627         areas     = theApp.config->echoAreas;
628     }
629     else if (theApp.module == M_HTICK) {
630 	echoLimit = link->filefix.echoLimit;
631         areaCount = theApp.config->fileAreaCount;
632         areas     = theApp.config->fileAreas;
633     }
634     w_log(LL_FUNC, __FILE__ "::limitCheck()");
635 
636     if (echoLimit==0) return 0;
637 
638     for (i=n=0; i < areaCount; i++)
639         if (isLinkOfArea(link, &(areas[i])))
640             n++;
641 
642     i = n >= echoLimit ;
643 
644     w_log(LL_FUNC, __FILE__ "::limitCheck() rc=%u", i);
645     return i;
646 }
647