1 /*	$Id$ */
2 /*
3  * Copyright (c) 1990-1996 Sam Leffler
4  * Copyright (c) 1991-1996 Silicon Graphics, Inc.
5  * HylaFAX is a trademark of Silicon Graphics
6  *
7  * Permission to use, copy, modify, distribute, and sell this software and
8  * its documentation for any purpose is hereby granted without fee, provided
9  * that (i) the above copyright notices and this permission notice appear in
10  * all copies of the software and related documentation, and (ii) the names of
11  * Sam Leffler and Silicon Graphics may not be used in any advertising or
12  * publicity relating to the software without the specific, prior written
13  * permission of Sam Leffler and Silicon Graphics.
14  *
15  * THE SOFTWARE IS PROVIDED "AS-IS" AND WITHOUT WARRANTY OF ANY KIND,
16  * EXPRESS, IMPLIED OR OTHERWISE, INCLUDING WITHOUT LIMITATION, ANY
17  * WARRANTY OF MERCHANTABILITY OR FITNESS FOR A PARTICULAR PURPOSE.
18  *
19  * IN NO EVENT SHALL SAM LEFFLER OR SILICON GRAPHICS BE LIABLE FOR
20  * ANY SPECIAL, INCIDENTAL, INDIRECT OR CONSEQUENTIAL DAMAGES OF ANY KIND,
21  * OR ANY DAMAGES WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS,
22  * WHETHER OR NOT ADVISED OF THE POSSIBILITY OF DAMAGE, AND ON ANY THEORY OF
23  * LIABILITY, ARISING OUT OF OR IN CONNECTION WITH THE USE OR PERFORMANCE
24  * OF THIS SOFTWARE.
25  */
26 #include "ServerConfig.h"
27 #include "FaxTrace.h"
28 #include "DialRules.h"
29 #include "UUCPLock.h"
30 #include "REArray.h"
31 #include "BoolArray.h"
32 #include "faxApp.h"
33 
34 #include <ctype.h>
35 #include "Sys.h"
36 
37 #include "config.h"
38 
ServerConfig()39 ServerConfig::ServerConfig()
40 {
41     lastTSIModTime = 0;
42     lastPWDModTime = 0;
43     tsiPats = NULL;
44     pwdPats = NULL;
45     acceptTSI = NULL;
46     acceptPWD = NULL;
47     dialRules = NULL;
48     setupConfig();
49 }
50 
~ServerConfig()51 ServerConfig::~ServerConfig()
52 {
53     delete dialRules;
54     delete acceptTSI;
55     delete acceptPWD;
56     delete tsiPats;
57     delete pwdPats;
58 }
59 
60 void
configError(const char * fmt,...)61 ServerConfig::configError(const char* fmt, ...)
62 {
63     va_list ap;
64     va_start(ap, fmt);
65     vconfigError(fmt, ap);
66     va_end(ap);
67 }
68 
69 void
configTrace(const char * fmt,...)70 ServerConfig::configTrace(const char* fmt, ...)
71 {
72     va_list ap;
73     va_start(ap, fmt);
74     vconfigTrace(fmt, ap);
75     va_end(ap);
76 }
77 
78 #define	N(a)	(sizeof (a) / sizeof (a[0]))
79 
80 ServerConfig::S_booltag ServerConfig::bools[] = {
81 { "batchlogs",		&ServerConfig::batchLogs,	true },
82 { "polllockpokemodem",	&ServerConfig::pollLockPokeModem, false },
83 };
84 
85 ServerConfig::S_stringtag ServerConfig::strings[] = {
86 { "logfacility",	&ServerConfig::logFacility,	LOG_FAX },
87 { "faxnumber",		&ServerConfig::FAXNumber },
88 { "areacode",		&ServerConfig::areaCode	},
89 { "countrycode",	&ServerConfig::countryCode },
90 { "longdistanceprefix",	&ServerConfig::longDistancePrefix },
91 { "internationalprefix",&ServerConfig::internationalPrefix },
92 { "qualifytsi",		&ServerConfig::qualifyTSI },
93 { "qualifypwd",		&ServerConfig::qualifyPWD },
94 { "uucplockdir",	&ServerConfig::uucpLockDir,	UUCP_LOCKDIR },
95 { "uucplocktype",	&ServerConfig::uucpLockType,	UUCP_LOCKTYPE },
96 };
97 ServerConfig::S_numbertag ServerConfig::numbers[] = {
98 { "tracingmask",	&ServerConfig::tracingMask,	// NB: must be first
99    FAXTRACE_MODEMIO|FAXTRACE_TIMEOUTS },
100 { "sessiontracing",	&ServerConfig::logTracingLevel,	FAXTRACE_SERVER },
101 { "servertracing",	&ServerConfig::tracingLevel,	FAXTRACE_SERVER },
102 { "uucplocktimeout",	&ServerConfig::uucpLockTimeout,	0 },
103 { "jobreqproto",	&ServerConfig::requeueProto,	FAX_REQPROTO },
104 { "jobreqother",	&ServerConfig::requeueOther,	FAX_REQUEUE },
105 { "jobretryother",	&ServerConfig::retryOther,	FAX_RETRY },
106 { "pollmodemwait",	&ServerConfig::pollModemWait,	30 },
107 { "polllockwait",	&ServerConfig::pollLockWait,	30 },
108 { "maxrecvpages",	&ServerConfig::maxRecvPages,	(u_int) -1 },
109 { "maxbadcalls",	&ServerConfig::maxConsecutiveBadCalls, 25 },
110 { "maxsetupattempts",	&ServerConfig::maxSetupAttempts, 2 },
111 };
112 ServerConfig::S_filemodetag ServerConfig::filemodes[] = {
113 { "recvfilemode",	&ServerConfig::recvFileMode,	0600 },
114 { "devicemode",		&ServerConfig::deviceMode,	0600 },
115 { "logfilemode",	&ServerConfig::logMode,		0600 },
116 { "uucplockmode",	&ServerConfig::uucpLockMode,	UUCP_LOCKMODE },
117 };
118 
119 void
setupConfig()120 ServerConfig::setupConfig()
121 {
122     int i;
123 
124     for (i = N(bools)-1; i >= 0; i--)
125 	(*this).*bools[i].p = bools[i].def;
126     for (i = N(strings)-1; i >= 0; i--)
127 	(*this).*strings[i].p = (strings[i].def ? strings[i].def : "");
128     for (i = N(filemodes)-1; i >= 0; i--)
129 	(*this).*filemodes[i].p = (mode_t) filemodes[i].def;
130     for (i = N(numbers)-1; i >= 0; i--)
131 	(*this).*numbers[i].p = numbers[i].def;
132 
133     speakerVolume = ClassModem::QUIET;	// default speaker volume
134 #ifdef sgi
135     clocalAsRoot = true;		// under IRIX must be root to set CLOCAL
136 #else
137     clocalAsRoot = false;		// everywhere else anyone can do it
138 #endif
139 
140 #if HAS_SCHEDCTL || HAS_PRIOCNTL || HAS_RTPRIO
141     priorityScheduling = true;		// maintain historic behavior
142 #else
143     priorityScheduling = false;		// for new mechanisms anyone can do it
144 #endif
145 
146     requeueTTS[ClassModem::OK]		= 0;
147     requeueTTS[ClassModem::BUSY]	= FAX_REQBUSY;
148     requeueTTS[ClassModem::NOCARRIER]	= FAX_REQUEUE;
149     requeueTTS[ClassModem::NOANSWER]	= FAX_REQUEUE;
150     requeueTTS[ClassModem::NODIALTONE]	= FAX_REQUEUE;
151     requeueTTS[ClassModem::ERROR]	= FAX_REQUEUE;
152     requeueTTS[ClassModem::FAILURE]	= FAX_REQUEUE;
153     requeueTTS[ClassModem::NOFCON]	= FAX_REQUEUE;
154     requeueTTS[ClassModem::DATACONN]	= FAX_REQUEUE;
155 
156 
157     retryMAX[ClassModem::OK]		= 0;
158     retryMAX[ClassModem::BUSY]		= FAX_RETBUSY;
159     retryMAX[ClassModem::NOCARRIER]	= 1;		// Old NoCarrierRetry Default
160     retryMAX[ClassModem::NOANSWER]	= FAX_RETRY;
161     retryMAX[ClassModem::NODIALTONE]	= FAX_RETRY;
162     retryMAX[ClassModem::ERROR]		= FAX_RETRY;
163     retryMAX[ClassModem::FAILURE]	= FAX_RETRY;
164     retryMAX[ClassModem::NOFCON]	= FAX_RETRY;
165     retryMAX[ClassModem::DATACONN]	= FAX_RETRY;
166 
167     localIdentifier = "";
168     delete dialRules, dialRules = NULL;
169 }
170 
171 void
resetConfig()172 ServerConfig::resetConfig()
173 {
174     ModemConfig::resetConfig();
175     setupConfig();
176 }
177 
178 #define	valeq(a,b)	(strcasecmp(a,b)==0)
179 
180 SpeakerVolume
getVolume(const char * cp)181 ServerConfig::getVolume(const char* cp)
182 {
183     if (valeq(cp, "off"))
184 	return ClassModem::OFF;
185     else if (valeq(cp, "quiet"))
186 	return ClassModem::QUIET;
187     else if (valeq(cp, "low"))
188 	return ClassModem::LOW;
189     else if (valeq(cp, "medium"))
190 	return ClassModem::MEDIUM;
191     else if (valeq(cp, "high"))
192 	return ClassModem::HIGH;
193     else {
194 	configError("Unknown speaker volume \"%s\"; using \"quiet\"", cp);
195 	return ClassModem::QUIET;
196     }
197 }
198 
199 void
setLocalIdentifier(const fxStr & lid)200 ServerConfig::setLocalIdentifier(const fxStr& lid)
201 {
202     localIdentifier = lid;
203 }
204 
205 void
setModemSpeakerVolume(SpeakerVolume level)206 ServerConfig::setModemSpeakerVolume(SpeakerVolume level)
207 {
208     speakerVolume = level;
209 }
210 
211 /*
212  * Subclass DialStringRules so that we can redirect the
213  * diagnostic and tracing interfaces through the server.
214  */
215 class ServerConfigDialStringRules : public DialStringRules {
216 private:
217     ServerConfig& config;	// XXX should be const, but requires other mods
218 
219     virtual void parseError(const char* fmt ...);
220     virtual void traceParse(const char* fmt ...);
221     virtual void traceRules(const char* fmt ...);
222 public:
223     ServerConfigDialStringRules(ServerConfig& config, const char* filename);
224     ~ServerConfigDialStringRules();
225 };
ServerConfigDialStringRules(ServerConfig & c,const char * f)226 ServerConfigDialStringRules::ServerConfigDialStringRules(ServerConfig& c, const char* f)
227     : DialStringRules(f), config(c)
228 {}
~ServerConfigDialStringRules()229 ServerConfigDialStringRules::~ServerConfigDialStringRules() {}
230 
231 void
parseError(const char * fmt...)232 ServerConfigDialStringRules::parseError(const char* fmt ...)
233 {
234     va_list ap;
235     va_start(ap, fmt);
236     config.vconfigError(fmt, ap);
237     va_end(ap);
238 }
239 void
traceParse(const char * fmt...)240 ServerConfigDialStringRules::traceParse(const char* fmt ...)
241 {
242     va_list ap;
243     va_start(ap, fmt);
244     config.vdialrulesTrace(fmt, ap);
245     va_end(ap);
246 }
247 void
traceRules(const char * fmt...)248 ServerConfigDialStringRules::traceRules(const char* fmt ...)
249 {
250     va_list ap;
251     va_start(ap, fmt);
252     config.vdialrulesTrace(fmt, ap);
253     va_end(ap);
254 }
255 
256 void
setDialRules(const char * name)257 ServerConfig::setDialRules(const char* name)
258 {
259     delete dialRules;
260     dialRules = new ServerConfigDialStringRules(*this, name);
261     dialRules->setVerbose(
262 	((tracingLevel|logTracingLevel) & FAXTRACE_DIALRULES) != 0);
263     /*
264      * Setup configuration environment.
265      */
266     dialRules->def("AreaCode", areaCode);
267     dialRules->def("CountryCode", countryCode);
268     dialRules->def("LongDistancePrefix", longDistancePrefix);
269     dialRules->def("InternationalPrefix", internationalPrefix);
270     if (!dialRules->parse()) {
271 	configError("Parse error in dial string rules \"%s\"", name);
272 	delete dialRules, dialRules = NULL;
273     }
274 }
275 
276 /*
277  * Convert a dialing string to a canonical format.
278  */
279 fxStr
canonicalizePhoneNumber(const fxStr & ds)280 ServerConfig::canonicalizePhoneNumber(const fxStr& ds)
281 {
282     if (dialRules)
283 	return dialRules->canonicalNumber(ds);
284     else
285 	return ds;
286 }
287 
288 /*
289  * Prepare a dialing string for use.
290  */
291 fxStr
prepareDialString(const fxStr & ds)292 ServerConfig::prepareDialString(const fxStr& ds)
293 {
294     if (dialRules)
295 	return dialRules->dialString(ds);
296     else
297 	return ds;
298 }
299 
300 /*
301  * Create an appropriate UUCP lock instance.
302  */
303 UUCPLock*
getUUCPLock(const fxStr & deviceName)304 ServerConfig::getUUCPLock(const fxStr& deviceName)
305 {
306     return UUCPLock::newLock(uucpLockType,
307 	uucpLockDir, deviceName, uucpLockMode);
308 }
309 
310 bool
checkACL(const fxStr & id,REArray * pats,fxBoolArray & accept)311 ServerConfig::checkACL(const fxStr& id, REArray* pats, fxBoolArray& accept)
312 {
313     if (pats != NULL) {
314 	for (u_int i = 0; i < pats->length(); i++)
315 	    if ((*pats)[i]->Find(id))
316 		return (accept[i]);
317     }
318     return (false);			// NB: reject if no patterns!
319 }
320 
321 bool
isTSIOk(const fxStr & tsi)322 ServerConfig::isTSIOk(const fxStr& tsi)
323 {
324     updatePatterns(qualifyTSI, tsiPats, acceptTSI, lastTSIModTime);
325     return (qualifyTSI == "" ? true : checkACL(tsi, tsiPats, *acceptTSI));
326 }
327 
328 bool
isPWDOk(const fxStr & pwd)329 ServerConfig::isPWDOk(const fxStr& pwd)
330 {
331     updatePatterns(qualifyPWD, pwdPats, acceptPWD, lastPWDModTime);
332     return (qualifyPWD == "" ? true : checkACL(pwd, pwdPats, *acceptPWD));
333 }
334 
335 /*
336  * Update pattern arrays if a file patterns has
337  * been changed since the last time we read it.
338  */
339 void
updatePatterns(const fxStr & file,REArray * & pats,fxBoolArray * & accept,time_t & lastModTime)340 ServerConfig::updatePatterns(const fxStr& file,
341     REArray*& pats, fxBoolArray*& accept,
342     time_t& lastModTime)
343 {
344     struct stat sb;
345     if (file != "" && Sys::stat(file, sb) >= 0 && sb.st_mtime >= lastModTime) {
346 	FILE* fp = Sys::fopen(file, "r");
347 	if (fp != NULL) {
348 	    readPatterns(fp, pats, accept);
349 	    lastModTime = sb.st_mtime;
350 	    fclose(fp);
351 	}
352     } else if (pats) {
353 	// file's been removed, delete any existing info
354 	delete pats, pats = NULL;
355 	delete accept, accept = NULL;
356     }
357 }
358 
359 /*
360  * Read the file of patterns into an array.
361  *
362  * The order of patterns is preserved and a leading ``!''
363  * is interpreted to mean ``reject if matched by this
364  * regex''.  Note also that we always allocate an array
365  * of patterns.  The pattern matching logic rejects
366  * things that don't match any patterns.  Thus an empty file
367  * causes all incoming facsimile to be rejected.
368  */
369 void
readPatterns(FILE * fp,REArray * & pats,fxBoolArray * & accept)370 ServerConfig::readPatterns(FILE* fp, REArray*& pats, fxBoolArray*& accept)
371 {
372     if (pats)
373 	pats->resize(0);
374     else
375 	pats = new REArray;
376     if (accept)
377 	accept->resize(0);
378     else
379 	accept = new fxBoolArray;
380 
381     char line[256];
382     while (fgets(line, sizeof (line)-1, fp)) {
383 	char* cp = strchr(line, '#');
384 	if (cp || (cp = strchr(line, '\n')))
385 	    *cp = '\0';
386 	/* trim off trailing white space */
387 	for (cp = strchr(line, '\0'); cp > line; cp--)
388 	    if (!isspace(cp[-1]))
389 		break;
390 	*cp = '\0';
391 	if (line[0] == '\0')
392 	    continue;
393 	RE* re;
394 	if (line[0] == '!') {
395 	    accept->append(false);
396 	    pats->append(re = new RE(line+1));
397 	} else {
398 	    accept->append(true);
399 	    pats->append(re = new RE(line));
400 	}
401 	if (re->getErrorCode() > REG_NOMATCH) {
402 	    fxStr emsg;
403 	    re->getError(emsg);
404 	    configError("Bad TSI/CID pattern: %s: " | emsg, re->pattern());
405 	}
406     }
407 }
408 
409 static void
tiffErrorHandler(const char * module,const char * fmt0,va_list ap)410 tiffErrorHandler(const char* module, const char* fmt0, va_list ap)
411 {
412     fxStr fmt = (module != NULL) ?
413         fxStr::format("%s: Warnings, %s.", module, fmt0)
414         : fxStr::format("Warnings, %s.", fmt0);
415     vlogError(fmt, ap);
416 }
417 
418 static void
tiffWarningHandler(const char * module,const char * fmt0,va_list ap)419 tiffWarningHandler(const char* module, const char* fmt0, va_list ap)
420 {
421     fxStr fmt = (module != NULL) ?
422         fxStr::format("%s: Warnings, %s.", module, fmt0)
423         : fxStr::format("Warnings, %s.", fmt0);
424     vlogWarning(fmt, ap);
425 }
426 
427 bool
setConfigItem(const char * tag,const char * value)428 ServerConfig::setConfigItem(const char* tag, const char* value)
429 {
430     u_int ix;
431     if (findTag(tag, (const tags*)strings, N(strings), ix)) {
432 	(*this).*strings[ix].p = value;
433 	switch (ix) {
434 	case 0:	faxApp::setLogFacility(logFacility); break;
435 	}
436     } else if (findTag(tag, (const tags*)numbers, N(numbers), ix)) {
437 	(*this).*numbers[ix].p = getNumber(value);
438 	switch (ix) {
439 	case 1: tracingLevel &= ~tracingMask;
440 	case 2: logTracingLevel &= ~tracingMask;
441 	    if (dialRules)
442 		dialRules->setVerbose(
443 		    (tracingLevel|logTracingLevel) & FAXTRACE_DIALRULES);
444 	    if ((tracingLevel|logTracingLevel) & FAXTRACE_TIFF) {
445 		TIFFSetErrorHandler(tiffErrorHandler);
446 		TIFFSetWarningHandler(tiffWarningHandler);
447 	    } else {
448 		TIFFSetErrorHandler(NULL);
449 		TIFFSetWarningHandler(NULL);
450 	    }
451 	    break;
452 	case 3: UUCPLock::setLockTimeout(uucpLockTimeout); break;
453 	}
454     } else if (findTag(tag, (const tags*)filemodes, N(filemodes), ix))
455 	(*this).*filemodes[ix].p = strtol(value, 0, 8);
456     else if (findTag(tag, (const tags*)bools, N(bools), ix))
457 	(*this).*bools[ix].p = getBoolean(value);
458 
459     else if (streq(tag, "speakervolume"))
460 	setModemSpeakerVolume(getVolume(value));
461     else if (streq(tag, "localidentifier"))
462 	setLocalIdentifier(value);
463     else if (streq(tag, "dialstringrules"))
464 	setDialRules(value);
465     else if (streq(tag, "clocalasroot"))
466 	clocalAsRoot = getBoolean(value);
467     else if (streq(tag, "priorityscheduling"))
468 	priorityScheduling = getBoolean(value);
469     else if (streq(tag, "jobreqbusy"))
470 	requeueTTS[ClassModem::BUSY] = getNumber(value);
471     else if (streq(tag, "jobreqnocarrier"))
472 	requeueTTS[ClassModem::NOCARRIER] = getNumber(value);
473     else if (streq(tag, "jobreqnoanswer"))
474 	requeueTTS[ClassModem::NOANSWER] = getNumber(value);
475     else if (streq(tag, "jobreqnofcon"))
476 	requeueTTS[ClassModem::NOFCON] = getNumber(value);
477     else if (streq(tag, "jobreqdataconn"))
478 	requeueTTS[ClassModem::DATACONN] = getNumber(value);
479     else if (streq(tag, "jobreqerror"))
480 	requeueTTS[ClassModem::ERROR] = getNumber(value);
481     else if (streq(tag, "nocarrierretrys"))
482 	retryMAX[ClassModem::NOCARRIER] = getNumber(value);
483     else if (streq(tag, "jobretrybusy"))
484 	retryMAX[ClassModem::BUSY] = getNumber(value);
485     else if (streq(tag, "jobretrynocarrier"))
486 	retryMAX[ClassModem::NOCARRIER] = getNumber(value);
487     else if (streq(tag, "jobretrynoanswer"))
488 	retryMAX[ClassModem::NOANSWER] = getNumber(value);
489     else if (streq(tag, "jobretrynofcon"))
490 	retryMAX[ClassModem::NOFCON] = getNumber(value);
491     else if (streq(tag, "jobretrydataconn"))
492 	retryMAX[ClassModem::DATACONN] = getNumber(value);
493     else if (streq(tag, "jobretryerror"))
494 	retryMAX[ClassModem::ERROR] = getNumber(value);
495     else
496 	return ModemConfig::setConfigItem(tag, value);
497     return (true);
498 }
499 #undef N
500