faxGettyApp.c++
上传用户:weiyuanprp
上传日期:2020-05-20
资源大小:1169k
文件大小:36k
源码类别:

传真(Fax)编程

开发平台:

C/C++

  1. /* $Id: faxGettyApp.c++,v 1.24 2008/04/26 22:34:28 faxguy Exp $ */
  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 <sys/types.h>
  27. #include <ctype.h>
  28. #include <errno.h>
  29. #include <pwd.h>
  30. #include <math.h>
  31. #include <limits.h>
  32. #include <sys/file.h>
  33. #include <grp.h>
  34. #include <unistd.h>
  35. #include "Sys.h"
  36. #include "Dispatcher.h"
  37. #include "FaxRecvInfo.h"
  38. #include "FaxMachineInfo.h"
  39. #include "FaxAcctInfo.h"
  40. #include "faxGettyApp.h"
  41. #include "UUCPLock.h"
  42. #include "Getty.h"
  43. #include "REArray.h"
  44. #include "BoolArray.h"
  45. #include "config.h"
  46. /*
  47.  * HylaFAX Spooling and Command Agent.
  48.  */
  49. AnswerTimeoutHandler::AnswerTimeoutHandler() {}
  50. AnswerTimeoutHandler::~AnswerTimeoutHandler() {}
  51. void AnswerTimeoutHandler::timerExpired(long, long)
  52.     { faxGettyApp::instance().answerCleanup(); }
  53. const fxStr faxGettyApp::recvDir = FAX_RECVDIR;
  54. faxGettyApp* faxGettyApp::_instance = NULL;
  55. faxGettyApp::faxGettyApp(const fxStr& devName, const fxStr& devID)
  56.     : FaxServer(devName, devID)
  57. {
  58.     devfifo = -1;
  59.     modemLock = NULL;
  60.     setupConfig();
  61.     fxAssert(_instance == NULL, "Cannot create multiple faxGettyApp instances");
  62.     _instance = this;
  63. }
  64. faxGettyApp::~faxGettyApp()
  65. {
  66.     delete modemLock;
  67. }
  68. faxGettyApp& faxGettyApp::instance() { return *_instance; }
  69. void
  70. faxGettyApp::initialize(int argc, char** argv)
  71. {
  72.     FaxServer::initialize(argc, argv);
  73.     faxApp::initialize(argc, argv);
  74.     for (GetoptIter iter(argc, argv, getOpts()); iter.notDone(); iter++)
  75. switch (iter.option()) {
  76. case 'c': // set configuration parameter
  77.     readConfigItem(iter.optArg());
  78.     break;
  79. case 'D': // detach process from tty
  80.     detachFromTTY();
  81.     break;
  82. }
  83.     modemLock = getUUCPLock(getModemDevice());
  84.     /*
  85.      * Notify queuer that modem exists and what the
  86.      * phone number is.  This is used by the queuer
  87.      * when processing other messages we sent
  88.      * later--such as when a document is received.
  89.      */
  90.     sendModemStatus("N" | getModemNumber());
  91. }
  92. void
  93. faxGettyApp::open()
  94. {
  95.     traceServer("OPEN %s  %s"
  96.         , (const char*) getModemDevice()
  97.         , HYLAFAX_VERSION
  98.     );
  99.     faxApp::open();
  100.     FaxServer::open();
  101. }
  102. void
  103. faxGettyApp::close()
  104. {
  105.     if (isRunning()) {
  106. sendModemStatus("D");
  107. traceServer("CLOSE " | getModemDevice());
  108. faxApp::close();
  109. FaxServer::close();
  110.     }
  111. }
  112. bool faxGettyApp::lockModem() { return modemLock->lock(); }
  113. void faxGettyApp::unlockModem() { modemLock->unlock(); }
  114. bool faxGettyApp::canLockModem() { return modemLock->check(); }
  115. bool faxGettyApp::isModemLocked() { return modemLock->isLocked(); }
  116. bool
  117. faxGettyApp::setupModem(bool isSend)
  118. {
  119.     /*
  120.      * Reread the configuration file if it has been
  121.      * changed.  We do this before each setup operation
  122.      * since we are a long-running process and it should
  123.      * not be necessary to restart the process to have
  124.      * config file changes take effect.
  125.      */
  126.     if (updateConfig(getConfigFile()))
  127. sendModemStatus("N" | getModemNumber());
  128.     if (FaxServer::setupModem(false) && ModemServer::readyModem()) {
  129. /*
  130.  * Setup modem for receiving.
  131.  */
  132. FaxModem* modem = (FaxModem*) ModemServer::getModem();
  133. modem->setupReceive();
  134. /*
  135.  * If the server is configured, listen for incoming calls.
  136.  */
  137. setRingsBeforeAnswer(ringsBeforeAnswer);
  138. return (true);
  139.     } else
  140. return (false);
  141. }
  142. /*
  143.  * Discard any handle on the modem.
  144.  */
  145. void
  146. faxGettyApp::discardModem(bool dropDTR)
  147. {
  148.     int fd = getModemFd();
  149.     if (fd >= 0) {
  150. if (Dispatcher::instance().handler(fd, Dispatcher::ReadMask))
  151.     Dispatcher::instance().unlink(fd);
  152.     }
  153.     FaxServer::discardModem(dropDTR);
  154. }
  155. /*
  156.  * Begin listening for ring status messages from the modem.
  157.  * The modem is locked and we mark it as busy to the queuer
  158.  * (just as if we were answering a call).  We listen for one
  159.  * ring status message and, if successful, keep the modem
  160.  * locked and change state so that further input will be
  161.  * consumed.
  162.  */
  163. void
  164. faxGettyApp::listenBegin()
  165. {
  166.     if (lockModem()) {
  167. changeState(LISTENING);
  168. sendModemStatus("B");
  169. ringsHeard = 0;
  170. listenForRing();
  171.     } else if (state != SENDING && state != ANSWERING && state != RECEIVING) {
  172. /*
  173.  * The modem is in use to call out, or some other process
  174.  * has grabbed the lock to handle the incoming call. 
  175.  * Discard our handle on the modem and change to LOCKWAIT
  176.  * state where we wait for the modem to come available again.
  177.  */
  178. traceServer("ANSWER: Can not lock modem device");
  179. discardModem(false);
  180. changeState(LOCKWAIT, pollLockWait);
  181. sendModemStatus("U");
  182.     }
  183. }
  184. /*
  185.  * Consume a ring status message from the modem.  We assume
  186.  * the modem is locked and that input is present.  If we don't
  187.  * see a ring then we exit LISTENING state and reset our
  188.  * handle on the modem.  If the number of rings received
  189.  * matches ringsBeforeAnswer then we answer the phone.
  190.  */
  191. void
  192. faxGettyApp::listenForRing()
  193. {
  194.     Dispatcher::instance().stopTimer(&answerHandler);
  195.     bool again;
  196.     do {
  197.         CallType ctype = ClassModem::CALLTYPE_UNKNOWN;
  198. CallID callid(idConfig.length());
  199.         again = false;
  200.         if (modemWaitForRings(ringsHeard, ctype, callid)) {
  201.     if (! callid.isEmpty()) {
  202. received_callid = callid; // CNID is only sent once.  Store it
  203. // for answering after later RINGs.
  204. fxStr send;
  205. for (u_int i = 0; i < received_callid.size(); i++) {
  206.     if (i) send.append(',');
  207.     send.append(""" | received_callid[i] | """);
  208.     traceServer("ANSWER: Call ID %d "%s"", i+1, received_callid.id(i));
  209. }
  210. faxApp::sendModemStatus(getModemDeviceID(), "C%s", (const char*) send);
  211.     }
  212.     ++ringsHeard;
  213.             if (dringOn.length() && ctype == ClassModem::CALLTYPE_UNKNOWN) --ringsHeard;
  214.              // distinctive ring failed to identify - get another ring
  215.             
  216.     /* DID modems may only signal a call with DID data - no RING */
  217.     bool done = false;
  218.     for (u_int i = 0; i < callid.size(); i++) {
  219. if ((u_int) idConfig[i].answerlength > 0 && 
  220.     callid[i].length() >= (u_int) idConfig[i].answerlength) {
  221.     done = true;
  222.     break;
  223. }
  224.     }
  225.     if (done || (ringsBeforeAnswer && (ringsHeard >= ringsBeforeAnswer)))
  226. answerPhone(ClassModem::ANSTYPE_ANY, ctype, received_callid);
  227.     else if (isModemInput())
  228.         again = true;
  229.     else
  230.         // NB: 10 second timeout should be plenty
  231.         Dispatcher::instance().startTimer(10, 0, &answerHandler);
  232.         } else
  233.     answerCleanup();
  234.     } while (again);
  235. }
  236. /*
  237.  * Handle a user request to answer the phone.
  238.  * Note that this can come in when we are in
  239.  * many different states.
  240.  */
  241. void
  242. faxGettyApp::answerPhoneCmd(AnswerType atype, const char* dialnumber)
  243. {
  244.     CallType ctype = ClassModem::CALLTYPE_UNKNOWN;
  245.     if (state == LISTENING) {
  246. /*
  247.  * We were listening to rings when an answer command
  248.  * was received.  The modem is already locked so just
  249.  * cancel any timeout and answer the call.
  250.  */
  251. Dispatcher::instance().stopTimer(&answerHandler);
  252. answerPhone(atype, ctype, received_callid, dialnumber);
  253.     } else if (lockModem()) {
  254. /*
  255.  * The modem is ours, notifier the queuer and answer.
  256.  */
  257. sendModemStatus("B");
  258. answerPhone(atype, ctype, received_callid, dialnumber);
  259.     } else if (state != ANSWERING && state != RECEIVING) {
  260. /*
  261.  * The modem is in use to call out, or some other process
  262.  * has grabbed the lock to handle the incoming call. 
  263.  * Discard our handle on the modem and change to LOCKWAIT
  264.  * state where we wait for the modem to come available again.
  265.  */
  266. discardModem(false);
  267. changeState(LOCKWAIT, pollLockWait);
  268. sendModemStatus("U");
  269.     }
  270. }
  271. void
  272. faxGettyApp::scriptedConfig(fxStr& cmd, fxStr& emsg)
  273. {
  274.     int pipefd[2], status;
  275.     char line[1024];
  276.     if (pipe(pipefd) == 0) {
  277. pid_t pid = fork();
  278. switch (pid) {
  279.     case -1:
  280. emsg = "Could not fork for scripted configuration. {E305}";
  281. logError("%s", (const char*)emsg);
  282. Sys::close(pipefd[0]);
  283. Sys::close(pipefd[1]);
  284. break;
  285.     case  0:
  286. dup2(pipefd[1], STDOUT_FILENO);
  287. Sys::close(pipefd[0]);
  288. Sys::close(pipefd[1]);
  289. execl("/bin/sh", "sh", "-c", (const char*) cmd, (char*) NULL);
  290. sleep(1);
  291. _exit(1);
  292.     default:
  293. Sys::close(pipefd[1]);
  294. {
  295.     FILE* fd = fdopen(pipefd[0], "r");
  296.     while (fgets(line, sizeof (line)-1, fd)) {
  297. line[strlen(line)-1]=''; // Nuke n at end of line
  298. (void) readConfigItem(line);
  299.     }
  300.     Sys::waitpid(pid, status);
  301.     if (status != 0) {
  302. emsg = fxStr::format("Bad exit status %#o for '%s' {E306}", status, (const char*) cmd);
  303. logError("%s", (const char*)emsg);
  304.     }
  305.     // modem settings may have changed...
  306.     FaxModem* modem = (FaxModem*) ModemServer::getModem();
  307.     modem->pokeConfig(false);
  308. }
  309. Sys::close(pipefd[0]);
  310. break;
  311. }
  312.     } else {
  313. emsg = "Could not open a pipe for scripted configuration. {E307}";
  314. logError("%s", (const char*) emsg);
  315.     }
  316. }
  317. /*
  318.  * Answer the telephone in response to data from the modem
  319.  * (e.g. a "RING" message) or an explicit command from the
  320.  * user (sending an "ANSWER" command through the FIFO).
  321.  */
  322. void
  323. faxGettyApp::answerPhone(AnswerType atype, CallType ctype, CallID& callid, const char* dialnumber)
  324. {
  325.     FaxModem* modem = (FaxModem*) ModemServer::getModem();
  326.     fxStr callingnumber = "";
  327.     fxStr statusmsg = "Answering the phone";
  328.     for (u_int i = 0; i < callid.size(); i++) {
  329. if (modem->getCallIDType(i) == "calling-number") {
  330.     callingnumber = callid[i];
  331. }
  332. if (callid[i].length() && modem->doCallIDDisplay(i)) {
  333.     statusmsg.append(", ");
  334.     statusmsg.append(modem->getCallIDLabel(i));
  335.     statusmsg.append(":");
  336.     statusmsg.append(callid[i]);
  337. }
  338.     }
  339.     changeState(ANSWERING, 0, (const char*) statusmsg);
  340.     beginSession(FAXNumber);
  341.     sendModemStatus("I" | getCommID());
  342.     FaxAcctInfo ai;
  343.     ai.user = "fax";
  344.     ai.commid = getCommID();
  345.     ai.start = Sys::now();
  346.     ai.device = getModemDeviceID();
  347.     ai.dest = getModemNumber();
  348.     ai.callid = callid;
  349.     ai.npages = 0;
  350.     ai.params = 0;
  351.     ai.csi = "";
  352.     ai.jobid = "";
  353.     ai.jobtag = "";
  354.     ai.owner = "";
  355.     FaxMachineInfo info;
  356.     if (callingnumber.length()) {
  357. info.updateConfig(canonicalizePhoneNumber(callingnumber));
  358. // if updateConfig returns false it's probably because the info file doesn't exist, no need to raise alarms
  359.     }    
  360.     /*
  361.      * Answer the phone according to atype.  If this is
  362.      * ``any'', then pick a more specific type.  If
  363.      * adaptive-answer is enabled and ``any'' is requested,
  364.      * then rotate through the set of possibilities.
  365.      */
  366.     bool callResolved;
  367.     bool advanceRotary = true;
  368.     fxStr emsg;
  369.     fxStr callid_formatted = "";
  370.     /*
  371.      * We default to accepting all calls.
  372.      * If the DynamicConfig set's RejectCall to true, then we
  373.      * will reject it.
  374.      */
  375.     rejectCall = false;
  376.     for (u_int i = 0; i < callid.size(); i++)
  377. callid_formatted.append(quote | quoted(callid.id(i)) | enquote);
  378.     if (callid_formatted.length())
  379.      traceProtocol("CallID:%s", (const char*) callid_formatted);
  380.     if (dynamicConfig.length()) {
  381. fxStr cmd(dynamicConfig | quote | quoted(getModemDevice()) | enquote | callid_formatted);
  382. traceServer("DynamicConfig: %s", (const char*)cmd);
  383. scriptedConfig(cmd, emsg);
  384.     }
  385.     if (rejectCall)
  386.     {
  387. /*
  388.  * Call was rejected based on Caller ID information.
  389.  */
  390. emsg = "ANSWER: CALL REJECTED {E308}";
  391. traceServer("%s", (const char*) emsg);
  392. callResolved = false;
  393. advanceRotary = false;
  394.     } else {
  395. if (ctype != ClassModem::CALLTYPE_UNKNOWN) {
  396.     /*
  397.      * Distinctive ring or other means has already identified
  398.      * the type of call.  If we're to answer the call in a
  399.      * different way, then treat this as an error and don't
  400.      * answer the phone.  Otherwise answer according to the
  401.      * deduced call type.
  402.      */
  403.     if (atype != ClassModem::ANSTYPE_ANY && ctype != atype) {
  404.         emsg = fxStr::format("ANSWER: Call deduced as %s,"
  405.      " but told to answer as %s; call ignored {E309}",
  406.      ClassModem::callTypes[ctype],
  407.      ClassModem::answerTypes[atype]);
  408. traceServer("%s", (const char*)emsg);
  409. callResolved = false;
  410. advanceRotary = false;
  411.     } else {
  412. // NB: answer based on ctype, not atype
  413. if (!(noAnswerVoice && ctype == ClassModem::CALLTYPE_VOICE)) 
  414.     ctype = modemAnswerCall(ctype, emsg, dialnumber);
  415. callResolved = processCall(ctype, info, emsg, callid);
  416.     }
  417. } else if (atype == ClassModem::ANSTYPE_ANY) {
  418.     /*
  419.      * Normal operation; answer according to the settings
  420.      * for the rotary and adaptive answer capabilities.
  421.      */
  422.     int r = answerRotor;
  423.     do {
  424. callResolved = answerCall(answerRotary[r], ctype, info, emsg, callid, dialnumber);
  425. r = (r+1) % answerRotorSize;
  426.     } while (!callResolved && adaptiveAnswer && r != answerRotor);
  427. } else {
  428.     /*
  429.      * Answer for a specific type of call but w/o
  430.      * any existing call type information such as
  431.      * distinctive ring.
  432.      */
  433.     callResolved = answerCall(atype, ctype, info, emsg, callid, dialnumber);
  434. }
  435.     }
  436.     /*
  437.      * Call resolved.  If we were able to recognize the call
  438.      * type and setup a session, then reset the answer rotary
  439.      * state if there is a bias toward a specific answer type.
  440.      * Otherwise, if the call failed, advance the rotor to
  441.      * the next answer type in preparation for the next call.
  442.      */
  443.     if (callResolved) {
  444. if (answerBias != (u_int) -1)
  445.     answerRotor = answerBias;
  446.     } else {
  447. if (emsg.length()) traceProtocol((const char*) emsg);
  448. if (advanceRotary) {
  449.     if (adaptiveAnswer)
  450. answerRotor = 0;
  451.     else
  452. answerRotor = (answerRotor+1) % answerRotorSize;
  453. }
  454.     }
  455.     sendModemStatus("I");
  456.     endSession();
  457.     ai.status = emsg;
  458.     ai.duration = Sys::now() - ai.start;
  459.     ai.conntime = ai.duration;
  460.     ai.jobinfo = "";
  461.     if (logCalls && !ai.record("CALL"))
  462. logError("Error writing CALL accounting record, dest=%s",
  463.     (const char*) ai.dest);
  464.     answerCleanup();
  465. }
  466. /*
  467.  * Cleanup after answering a call or listening for ring status
  468.  * messages from the modem (when ringsBeforeAnswer is zero).
  469.  *
  470.  * If we still have a handle on the modem, then force a hangup
  471.  * and discard the handle.  We do this explicitly because some
  472.  * modems are impossible to safely hangup in the event of a
  473.  * problem.  Forcing a close on the device so that the modem
  474.  * will see DTR drop (hopefully) should clean up any bad state
  475.  * its in.  We then immediately try to setup the modem again
  476.  * so that we can be ready to answer incoming calls again.
  477.  *
  478.  * NB: the modem may have been discarded if a child process
  479.  *     was invoked to handle the inbound call.
  480.  */
  481. void
  482. faxGettyApp::answerCleanup()
  483. {
  484.     if (modemReady()) {
  485. modemHangup();
  486. discardModem(true);
  487.     }
  488.     for (u_int i = 0; i < received_callid.size(); i++)
  489. received_callid[i].resize(0);
  490.     bool isSetup;
  491.     if (isModemLocked() || lockModem()) {
  492. isSetup = setupModem(false);
  493. unlockModem();
  494.     } else
  495. isSetup = false;
  496.     if (isSetup)
  497. changeState(RUNNING, pollLockWait);
  498.     else
  499. changeState(MODEMWAIT, pollModemWait);
  500. }
  501. /*
  502.  * Answer a call according to the specified answer type
  503.  * and return the deduced call type.  If we are to use an
  504.  * external application to deduce and possibly handle the
  505.  * call then we do it here and return the call type it
  506.  * comes up with.  Otherwise we use whatever deduction
  507.  * the modem layer arrives at as the call type.
  508.  */
  509. bool
  510. faxGettyApp::answerCall(AnswerType atype, CallType& ctype, FaxMachineInfo info, fxStr& emsg, CallID& callid, const char* dialnumber)
  511. {
  512.     bool callResolved;
  513.     if (atype == ClassModem::ANSTYPE_EXTERN) {
  514. if (egettyArgs != "") {
  515.     /*
  516.      * Use an external application to deduce and possibly
  517.      * handle the call.  We invoke the egetty application
  518.      * and interpret the exit status as the deduced call type.
  519.      * If the call has not been handled in the subprocess
  520.      * then we take action based on the returned call type.
  521.      */
  522.     int status;
  523.     ctype = runGetty("EXTERN GETTY", OSnewEGetty,
  524. egettyArgs, emsg, status, lockExternCalls, callid, true);
  525.     if (ctype == ClassModem::CALLTYPE_DONE) // NB: call completed
  526. return (true);
  527.     if (ctype != ClassModem::CALLTYPE_ERROR) {
  528. status = (status >> 8) & 0xff;
  529. if (status >= 11 && status <= 13) {
  530.     /* status 11-13 signals that egetty did not determine the call type */
  531.     ctype = modemAnswerCall(ctype, emsg, dialnumber);
  532. } else
  533.     modemAnswerCallCmd(ctype);
  534.     }
  535. } else
  536.     emsg = "External getty use is not permitted {E310}";
  537.     } else
  538. ctype = modemAnswerCall(atype, emsg, dialnumber);
  539.     callResolved = processCall(ctype, info, emsg, callid);
  540.     return (callResolved);
  541. }
  542. /*
  543.  * Process an inbound call after the phone's been answered.
  544.  * Calls may either be handled within the process or through
  545.  * an external application.  Fax calls are handled internally.
  546.  * Other types of calls are handled with external apps.  The
  547.  * modem is conditioned for service, the process is started
  548.  * with the open file descriptor passed on stdin+stdout+stderr,
  549.  * and the local handle on the modem is discarded so that SIGHUP
  550.  * is delivered to the subprocess (group) on last close.  This
  551.  * process waits for the subprocess to terminate, at which time
  552.  * it removes the modem lock file and let's faxgetty continue on
  553.  * to recondition the modem for incoming calls (if configured).
  554.  */
  555. bool
  556. faxGettyApp::processCall(CallType ctype, FaxMachineInfo info, fxStr& emsg, CallID& callid)
  557. {
  558.     bool callHandled = false;
  559.     /*
  560.      * First of - turn of Dispatcher
  561.      */
  562.     int fd = getModemFd();
  563.     if (fd >= 0) {
  564. if (Dispatcher::instance().handler(fd, Dispatcher::ReadMask))
  565.     Dispatcher::instance().unlink(fd);
  566.     }
  567.     switch (ctype) {
  568.     case ClassModem::CALLTYPE_FAX:
  569. {
  570.     traceServer("ANSWER: FAX CONNECTION  DEVICE '%s'"
  571. , (const char*) getModemDevice());
  572.     FaxModem* modem = (FaxModem*) ModemServer::getModem();
  573.     fxStr statusmsg = "Receiving facsimile";
  574.     for (u_int i = 0; i < callid.size(); i++) {
  575. if (callid[i].length() && modem->doCallIDDisplay(i)) {
  576.     statusmsg.append(", ");
  577.     statusmsg.append(modem->getCallIDLabel(i));
  578.     statusmsg.append(":");
  579.     statusmsg.append(callid[i]);
  580. }
  581.     }
  582.     changeState(RECEIVING, 0, (const char*) statusmsg);
  583.     sendRecvStatus(getModemDeviceID(), "B");
  584.     callHandled = recvFax(callid, info, emsg);
  585.     sendRecvStatus(getModemDeviceID(), "E");
  586. }
  587. break;
  588.     case ClassModem::CALLTYPE_DATA:
  589. traceServer("ANSWER: DATA CONNECTION");
  590. if (gettyArgs != "") {
  591.     sendModemStatus("d");
  592.     int status;
  593.     runGetty("GETTY", OSnewGetty, gettyArgs, emsg, status, lockDataCalls, callid);
  594.     sendModemStatus("e");
  595. } else
  596.     traceServer("ANSWER: Data connections are not permitted");
  597. callHandled = true;
  598. break;
  599.     case ClassModem::CALLTYPE_VOICE:
  600. traceServer("ANSWER: VOICE CONNECTION");
  601. if (vgettyArgs != "") {
  602.     sendModemStatus("v");
  603.     int status;
  604.     runGetty("VGETTY", OSnewVGetty, vgettyArgs, emsg, status, lockVoiceCalls, callid);
  605.     sendModemStatus("w");
  606. } else
  607.     traceServer("ANSWER: Voice connections are not permitted");
  608. callHandled = true;
  609. break;
  610.     case ClassModem::CALLTYPE_ERROR:
  611. traceServer("ANSWER: %s", (const char*) emsg);
  612. break;
  613.     }
  614.     return (callHandled);
  615. }
  616. /*
  617.  * Run a getty subprocess and wait for it to terminate.
  618.  * The speed parameter is passed to use in establishing
  619.  * a login session.
  620.  */
  621. CallType
  622. faxGettyApp::runGetty(
  623.     const char* what,
  624.     Getty* (*newgetty)(const fxStr&, const fxStr&),
  625.     const char* args,
  626.     fxStr& emsg,
  627.     int& status,
  628.     bool keepLock,
  629.     const CallID& callid,
  630.     bool keepModem
  631. )
  632. {
  633.     fxStr prefix(_PATH_DEV);
  634.     fxStr dev(getModemDevice());
  635.     if (dev.head(prefix.length()) == prefix)
  636. dev.remove(0, prefix.length());
  637.     Getty* getty = (*newgetty)(dev, fxStr::format("%u", getModemRate()));
  638.     if (getty == NULL) {
  639. emsg = fxStr::format("%s: could not create {E311}", what);
  640. return (ClassModem::CALLTYPE_ERROR);
  641.     }
  642.     getty->setupArgv(args, callid);
  643.     /*
  644.      * The getty process should not inherit the lock file.
  645.      * Remove it here before the fork so that our state is
  646.      * correct (so further unlock calls will do nothing).
  647.      * Note that we remove the lock here because apps such
  648.      * as ppp and slip that install their own lock cannot
  649.      * cope with finding a lock in place (even if it has
  650.      * their pid in it).  This creates a potential window
  651.      * during which outbound jobs might grab the modem
  652.      * since they won't find a lock file in place.
  653.      */
  654.     if (!keepLock)
  655. unlockModem();
  656.     bool parentIsInit = (getppid() == 1);
  657.     pid_t pid = fork();
  658.     if (pid == -1) {
  659. emsg = fxStr::format("%s: can not fork: %s {E312}", what, strerror(errno));
  660. delete getty;
  661. return (ClassModem::CALLTYPE_ERROR);
  662.     }
  663.     if (pid == 0) { // child, start getty session
  664. setProcessPriority(BASE); // remove any high priority
  665. if (keepLock)
  666.     /*
  667.      * The getty process should inherit the lock file.
  668.      * Force the UUCP lock owner so that apps find their
  669.      * own pid in the lock file.  Otherwise they abort
  670.      * thinking some other process already has control
  671.      * of the modem.  Note that doing this creates a
  672.      * potential window for stale lock removal between
  673.      * the time the login process terminates and the
  674.      * parent retakes ownership of the lock file (see below).
  675.      */
  676.     modemLock->setOwner(getpid());
  677. {
  678.   setpwent();
  679.   uid_t uid = getuid();
  680.   gid_t gid = getgid();
  681.           uid_t euid = geteuid();
  682.           gid_t egid = getegid();
  683.   if (setegid(gid) < 0)
  684.       traceServer("runGetty::setegid: %m");
  685.   if (seteuid(uid) < 0)
  686.       traceServer("runGetty::seteuid (child): %m");
  687.           const struct passwd *pwd = getpwuid(euid);
  688.           if (initgroups(pwd->pw_name, egid) != 0)
  689.               traceServer("runGetty::initgroups: %m");
  690.   endpwent();
  691. }
  692. getty->run(getModemFd(), parentIsInit);
  693. _exit(127);
  694. /*NOTREACHED*/
  695.     }
  696.     traceServer("%s: START "%s", pid %lu", what,
  697. (const char*) getty->getCmdLine(), (u_long) pid);
  698.     getty->setPID(pid);
  699.     /*
  700.      * Purge existing modem state because the getty+login
  701.      * processe will change everything and because we must
  702.      * close the descriptor so that the getty will get
  703.      * SIGHUP on last close.
  704.      */
  705.     if (!keepModem)
  706. discardModem(false);
  707.     changeState(GETTYWAIT);
  708.     getty->wait(status, true); // wait for getty/login work to complete
  709.     if (status > 1280 && (status < 2816 || status > 3328)) { // these codes are undefined and must be an error    
  710.         emsg = fxStr::format("ERROR: Unknown status %#o {E313}", status);
  711.         status = 1024;
  712.     }
  713.     /*
  714.      * Retake ownership of the modem.  Note that there's
  715.      * a race in here (another process could come along
  716.      * and purge the lock file thinking it was stale because
  717.      * the pid is for the process that just terminated);
  718.      * the only way to avoid it is to use a real locking
  719.      * mechanism (e.g. flock on the lock file).
  720.      */
  721.     if (keepLock)
  722. modemLock->setOwner(0); // NB: 0 =>'s use setup pid
  723.     uid_t euid = geteuid();
  724.     if (seteuid(getuid()) < 0) // Getty::hangup assumes euid is root
  725.  traceServer("runGetty: seteuid (parent): %m");
  726.     getty->hangup(); // cleanup getty-related stuff
  727.     seteuid(euid);
  728.     traceServer("%s: exit status %#o", what, status);
  729.     delete getty;
  730.     int s = (status >> 8) & 0xff;
  731.     if (s >= 11 && s <= 13) s -= 10; // status 11-13 have special meaning
  732.     return (CallType)(s);
  733. }
  734. /*
  735.  * Set the number of rings to wait before answering
  736.  * the telephone.  If there is a modem setup, then
  737.  * configure the dispatcher to reflect whether or not
  738.  * we need to listen for data from the modem (the RING
  739.  * status messages).
  740.  */
  741. void
  742. faxGettyApp::setRingsBeforeAnswer(int rings)
  743. {
  744.     ringsBeforeAnswer = rings;
  745.     if (modemReady()) {
  746. int fd = getModemFd();
  747. Dispatcher::instance().link(fd, Dispatcher::ReadMask, this);
  748. /*
  749.  * Systems that have a tty driver based on SVR4 streams
  750.  * frequently don't implement select properly in that if
  751.  * output is collected by another process (e.g. cu, tip,
  752.  * kermit) quickly we do not reliably get woken up.  On
  753.  * these systems however the close on the tty usually
  754.  * causes us to wakeup from select for an exceptional
  755.  * condition on the descriptor--and this is good enough
  756.  * for us to do the work we need.
  757.  */
  758. Dispatcher::instance().link(fd, Dispatcher::ExceptMask, this);
  759.     }
  760. }
  761. /*
  762.  * Notification handlers.
  763.  */
  764. /*
  765.  * Handle notification that the modem device has become
  766.  * available again after a period of being unavailable.
  767.  */
  768. void
  769. faxGettyApp::notifyModemReady()
  770. {
  771.     (void) faxApp::sendModemStatus(getModemDeviceID(),
  772. readyState | getModemCapabilities() | ":%02x", modemPriority);
  773. }
  774. /*
  775.  * Handle notification that the modem device looks to
  776.  * be in a state that requires operator intervention.
  777.  */
  778. void
  779. faxGettyApp::notifyModemWedged()
  780. {
  781.     if (!sendModemStatus("W"))
  782. logError("MODEM %s appears to be wedged",
  783.     (const char*) getModemDevice());
  784.     close();
  785. }
  786. void
  787. faxGettyApp::notifyRecvBegun(FaxRecvInfo& ri)
  788. {
  789.     (void) sendRecvStatus(getModemDeviceID(), "S%s", (const char*) ri.encode());
  790.     FaxServer::notifyRecvBegun(ri);
  791. }
  792. /*
  793.  * Handle notification that a page has been received.
  794.  */
  795. void
  796. faxGettyApp::notifyPageRecvd(TIFF* tif, FaxRecvInfo& ri, int ppm)
  797. {
  798.     (void) sendRecvStatus(getModemDeviceID(), "P%s", (const char*) ri.encode());
  799.     FaxServer::notifyPageRecvd(tif, ri, ppm);
  800.     // XXX fill in
  801. }
  802. bool
  803. faxGettyApp::processTSIRecvdCmd(FaxRecvInfo& ri, fxStr& emsg)
  804. {
  805.     rejectCall = false;
  806.     if (tsiRecvdCmd.length()) {
  807. fxStr callid_formatted;
  808. for (u_int i = 0; i < ri.callid.size(); i++) {
  809.     callid_formatted.append(quote | quoted(ri.callid.id(i)) | enquote);
  810. }
  811. fxStr cmd(tsiRecvdCmd
  812. | quote |           quoted(ri.qfile) | enquote
  813. | quote | quoted(getModemDeviceID()) | enquote
  814. | quote |          quoted(ri.commid) | enquote
  815. | quote |          quoted(ri.sender) | enquote
  816. | quote |         quoted(ri.subaddr) | enquote
  817. | quote |          quoted(ri.passwd) | enquote
  818. | callid_formatted);
  819. traceServer("RECV FAX: %s", (const char*) cmd);
  820. scriptedConfig(cmd, emsg);
  821. if (rejectCall) emsg = "Permission denied (TSIRecvdCmd rejected call)";
  822.     }
  823.     return (!rejectCall);
  824. }
  825. /*
  826.  * Handle notification that a document has been received.
  827.  */
  828. void
  829. faxGettyApp::notifyDocumentRecvd(FaxRecvInfo& ri)
  830. {
  831.     (void) sendRecvStatus(getModemDeviceID(), "D%s", (const char*) ri.encode());
  832.     FaxServer::notifyDocumentRecvd(ri);
  833.     FaxAcctInfo ai;
  834.     ai.user = "fax";
  835.     ai.commid = getCommID();
  836.     ai.duration = (time_t) ri.time;
  837.     ai.start = Sys::now() - ai.duration;
  838.     ai.conntime = ai.duration;
  839.     ai.device = getModemDeviceID();
  840.     ai.dest = getModemNumber();
  841.     ai.csi = ri.sender;
  842.     ai.npages = ri.npages;
  843.     ai.params = ri.params.encode();
  844.     ai.status = ri.reason;
  845.     ai.jobid = ri.qfile;
  846.     ai.jobtag = "";
  847.     ai.callid = ri.callid;
  848.     ai.owner = "";
  849.     ai.jobinfo = "";
  850.     ri.params.asciiEncode(ai.faxdcs);
  851.     if (!ai.record("RECV"))
  852. logError("Error writing RECV accounting record, dest=%s",
  853.     (const char*) ai.dest);
  854. }
  855. /*
  856.  * Handle notification that a document has been received.
  857.  */
  858. void
  859. faxGettyApp::notifyRecvDone(FaxRecvInfo& ri)
  860. {
  861.     FaxServer::notifyRecvDone(ri);
  862.     fxStr callid_formatted;
  863.     for (u_int i = 0; i < ri.callid.size(); i++) {
  864. callid_formatted.append(quote | quoted(ri.callid.id(i)) | enquote);
  865.     }
  866.     // hand to delivery/notification command
  867.     fxStr cmd(faxRcvdCmd
  868. | quote |           quoted(ri.qfile) | enquote
  869. | quote | quoted(getModemDeviceID()) | enquote
  870. | quote |          quoted(ri.commid) | enquote
  871. | quote |          quoted(ri.reason) | enquote
  872. | callid_formatted);
  873.     traceServer("RECV FAX: %s", (const char*) cmd);
  874.     setProcessPriority(BASE); // lower priority
  875.     runCmd(cmd, true, this);
  876.     setProcessPriority(state); // restore priority
  877. }
  878. /*
  879.  * Send a modem status message to the central queuer process.
  880.  */
  881. bool
  882. faxGettyApp::sendModemStatus(const char* msg)
  883. {
  884.     return faxApp::sendModemStatus(getModemDeviceID(), msg);
  885. }
  886. /*
  887.  * FIFO-related support.
  888.  */
  889. /*
  890.  * Open the requisite FIFO special files.
  891.  */
  892. void
  893. faxGettyApp::openFIFOs()
  894. {
  895.     devfifo = openFIFO(fifoName | "." | getModemDeviceID(), 0600, true);
  896.     Dispatcher::instance().link(devfifo, Dispatcher::ReadMask, this);
  897. }
  898. void
  899. faxGettyApp::closeFIFOs()
  900. {
  901.     Sys::close(devfifo), devfifo = -1;
  902. }
  903. /*
  904.  * Respond to input on the specified file descriptor.
  905.  */
  906. int
  907. faxGettyApp::inputReady(int fd)
  908. {
  909.     if (fd == devfifo)
  910. return FIFOInput(fd);
  911.     else {
  912. if (state == LISTENING)
  913.     listenForRing();
  914. else
  915.     listenBegin();
  916. return (0);
  917.     }
  918. }
  919. /*
  920.  * Process a message received through a FIFO.
  921.  */
  922. void
  923. faxGettyApp::FIFOMessage(const char* cp)
  924. {
  925.     switch (cp[0]) {
  926.     case 'A': // answer the phone
  927. traceServer("ANSWER %s", cp[1] != '' ? cp+1 : "any");
  928. if (cp[1] != '') {
  929.     if (streq(cp+1, "fax"))
  930. answerPhoneCmd(ClassModem::ANSTYPE_FAX);
  931.     else if (streq(cp+1, "data"))
  932. answerPhoneCmd(ClassModem::ANSTYPE_DATA);
  933.     else if (streq(cp+1, "voice"))
  934. answerPhoneCmd(ClassModem::ANSTYPE_VOICE);
  935.     else if (streq(cp+1, "extern"))
  936. answerPhoneCmd(ClassModem::ANSTYPE_EXTERN);
  937.     else if (strncmp(cp+1, "dial", 4) == 0) {
  938. /*   
  939.  * In order to accomodate some so-called "polling" servers which
  940.  * do not follow spec in that we have to dial in, but begin the
  941.  * poll with ATA and follow fax reception protocol rather than
  942.  * polling protocol.  (Which essentially requires us to not
  943.  * produce typical calling CNG beeps, but rather a CED squelch.)
  944.  * We must terminate the dialstring with a semicolon, which
  945.  * should instruct the modem to return "OK" after dialing and not
  946.  * produce CNG, at which time we would follow with ATA.
  947.  */
  948. const char* dialnumber = cp+5; // usually must end with ";"
  949. answerPhoneCmd(ClassModem::ANSTYPE_DIAL, dialnumber);
  950.     }
  951. } else
  952.     answerPhoneCmd(answerRotary[0]);
  953. break;
  954.     case 'C': // configuration control
  955. traceServer("CONFIG "%s"", cp+1);
  956. readConfigItem(cp+1);
  957. break;
  958.     case 'H': // HELLO from queuer
  959. traceServer("HELLO");
  960. sendModemStatus("N" | getModemNumber());
  961. if (state == FaxServer::RUNNING)
  962.     notifyModemReady(); // sends capabilities also
  963. break;
  964.     case 'Q': // quit
  965. traceServer("QUIT");
  966. close();
  967. break;
  968.     case 'S': // set modem ready state
  969. traceServer("STATE "%s"", cp+1);
  970. setConfigItem("modemreadystate", cp+1);
  971. break;
  972.     case 'L': // set modem ready state
  973. traceServer("LOCKWAIT");
  974. discardModem(false);
  975. changeState(LOCKWAIT, pollLockWait);
  976. break;
  977.     case 'Z': // abort send/receive
  978. FaxServer::abortSession();
  979. break;
  980.     default:
  981. faxApp::FIFOMessage(cp);
  982. break;
  983.     }
  984. }
  985. /*
  986.  * Miscellaneous stuff.
  987.  */
  988. /*
  989.  * Configuration support.
  990.  */
  991. void
  992. faxGettyApp::resetConfig()
  993. {
  994.     FaxServer::resetConfig();
  995.     setupConfig();
  996. }
  997. #define N(a) (sizeof (a) / sizeof (a[0]))
  998. faxGettyApp::stringtag faxGettyApp::strings[] = {
  999. { "gettyargs", &faxGettyApp::gettyArgs },
  1000. { "vgettyargs", &faxGettyApp::vgettyArgs },
  1001. { "egettyargs", &faxGettyApp::egettyArgs },
  1002. { "faxrcvdcmd", &faxGettyApp::faxRcvdCmd, FAX_FAXRCVDCMD },
  1003. { "tsirecvdcmd", &faxGettyApp::tsiRecvdCmd },
  1004. { "dynamicconfig", &faxGettyApp::dynamicConfig },
  1005. };
  1006. faxGettyApp::numbertag faxGettyApp::numbers[] = {
  1007. { "answerbias", &faxGettyApp::answerBias, (u_int) -1 },
  1008. };
  1009. faxGettyApp::booltag faxGettyApp::booleans[] = {
  1010. { "adaptiveanswer", &faxGettyApp::adaptiveAnswer, false },
  1011. { "lockdatacalls", &faxGettyApp::lockDataCalls, true },
  1012. { "lockvoicecalls", &faxGettyApp::lockVoiceCalls, true },
  1013. { "lockexterncalls", &faxGettyApp::lockExternCalls, true },
  1014. { "logcalls", &faxGettyApp::logCalls, true },
  1015. { "rejectcall", &faxGettyApp::rejectCall, false },
  1016. };
  1017. void
  1018. faxGettyApp::setupConfig()
  1019. {
  1020.     int i;
  1021.     for (i = N(strings)-1; i >= 0; i--)
  1022. (*this).*strings[i].p = (strings[i].def ? strings[i].def : "");
  1023.     for (i = N(numbers)-1; i >= 0; i--)
  1024. (*this).*numbers[i].p = numbers[i].def;
  1025.     for (i = N(booleans)-1; i >= 0; i--)
  1026. (*this).*booleans[i].p = booleans[i].def;
  1027.     readyState = "R"; // default is really ready
  1028.     modemPriority = 255; // default is lowest priority
  1029.     ringsBeforeAnswer = 0; // default is not to answer phone
  1030.     setAnswerRotary("any"); // answer calls as ``any''
  1031. }
  1032. bool
  1033. faxGettyApp::setConfigItem(const char* tag, const char* value)
  1034. {
  1035.     u_int ix;
  1036.     if (findTag(tag, (const tags*) strings, N(strings), ix)) {
  1037. (*this).*strings[ix].p = value;
  1038.     } else if (findTag(tag, (const tags*)numbers, N(numbers), ix)) {
  1039. (*this).*numbers[ix].p = getNumber(value);
  1040. switch (ix) {
  1041. case 0: answerBias = fxmin(answerBias, (u_int) 2); break;
  1042. }
  1043.     } else if (findTag(tag, (const tags*)booleans, N(booleans), ix)) {
  1044. (*this).*booleans[ix].p = getBoolean(value);
  1045.     } else if (streq(tag, "ringsbeforeanswer")) {
  1046. setRingsBeforeAnswer(getNumber(value));
  1047.     } else if (streq(tag, "answerrotary")) {
  1048. setAnswerRotary(value);
  1049.     } else if (streq(tag, "modempriority")) {
  1050. u_int p = getNumber(value);
  1051. if (p != modemPriority) {
  1052.     modemPriority = p;
  1053.     if (state == FaxServer::RUNNING)
  1054. notifyModemReady();
  1055. }
  1056.     } else if (streq(tag, "modemreadystate")) {
  1057. if (readyState != value) {
  1058.     readyState = value;
  1059.     if (state == FaxServer::RUNNING)
  1060. notifyModemReady();
  1061. }
  1062. if (readyState == "B")
  1063.     ModemServer::readyStateMsg = " (busy)";
  1064. else if (readyState == "D")
  1065.     ModemServer::readyStateMsg = " (down)";
  1066. else if (readyState == "E")
  1067.     ModemServer::readyStateMsg = " (exempt)";
  1068. else
  1069.     ModemServer::readyStateMsg = "";
  1070.     } else
  1071. return (FaxServer::setConfigItem(tag, value));
  1072.     return (true);
  1073. }
  1074. #undef N
  1075. /*
  1076.  * Process an answer rotary spec string.
  1077.  */
  1078. void
  1079. faxGettyApp::setAnswerRotary(const fxStr& value)
  1080. {
  1081.     u_int i;
  1082.     u_int l = 0;
  1083.     for (i = 0; i < 3 && l < value.length(); i++) {
  1084. fxStr type(value.token(l, " t"));
  1085. type.raisecase();
  1086. if (type == "FAX")
  1087.     answerRotary[i] = ClassModem::ANSTYPE_FAX;
  1088. else if (type == "DATA")
  1089.     answerRotary[i] = ClassModem::ANSTYPE_DATA;
  1090. else if (type == "VOICE")
  1091.     answerRotary[i] = ClassModem::ANSTYPE_VOICE;
  1092. else if (type == "EXTERN")
  1093.     answerRotary[i] = ClassModem::ANSTYPE_EXTERN;
  1094. else {
  1095.     if (type != "ANY")
  1096. configError("Unknown answer type "%s"", (const char*) type);
  1097.     answerRotary[i] = ClassModem::ANSTYPE_ANY;
  1098. }
  1099.     }
  1100.     if (i == 0) // void string
  1101. answerRotary[i++] = ClassModem::ANSTYPE_ANY;
  1102.     answerRotor = 0;
  1103.     answerRotorSize = i;
  1104. }
  1105. static void
  1106. usage(const char* appName)
  1107. {
  1108.     faxApp::fatal("usage: %s [-c config-item] [-q queue-dir] [-Dpx] modem-device", appName);
  1109. }
  1110. static void
  1111. sigCleanup(int s)
  1112. {
  1113.     logError("CAUGHT SIGNAL %d", s);
  1114.     faxGettyApp::instance().close();
  1115.     _exit(-1);
  1116. }
  1117. int
  1118. main(int argc, char** argv)
  1119. {
  1120.     faxApp::setupLogging("FaxGetty");
  1121.     fxStr appName = argv[0];
  1122.     u_int l = appName.length();
  1123.     appName = appName.tokenR(l, '/');
  1124.     faxGettyApp::setupPermissions();
  1125.     faxApp::setOpts("c:q:Ddpx"); // p+x are for ModemServer
  1126.     fxStr queueDir(FAX_SPOOLDIR);
  1127.     fxStr device;
  1128.     GetoptIter iter(argc, argv, faxApp::getOpts());
  1129.     for (; iter.notDone(); iter++)
  1130. switch (iter.option()) {
  1131. case 'q': queueDir = iter.optArg(); break;
  1132. case '?': usage(appName);
  1133. }
  1134.     if (device == "") {
  1135. device = iter.getArg();
  1136. if (device == "")
  1137.     usage(appName);
  1138.     }
  1139.     if (device[0] != '/') // for getty
  1140. device.insert(_PATH_DEV);
  1141.     if (Sys::chdir(queueDir) < 0)
  1142. faxApp::fatal(queueDir | ": Can not change directory");
  1143.     faxGettyApp* app = new faxGettyApp(device, faxApp::devToID(device));
  1144.     signal(SIGTERM, fxSIGHANDLER(sigCleanup));
  1145.     signal(SIGINT, fxSIGHANDLER(sigCleanup));
  1146.     app->initialize(argc, argv);
  1147.     app->open();
  1148.     while (app->isRunning())
  1149. Dispatcher::instance().dispatch();
  1150.     app->close();
  1151.     delete app;
  1152.     return 0;
  1153. }