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