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

传真(Fax)编程

开发平台:

C/C++

  1. /* $Id: faxSendApp.c++,v 1.11 2009/07/13 04:50:41 faxguy Exp $ */
  2. /*
  3.  * Copyright (c) 1994-1996 Sam Leffler
  4.  * Copyright (c) 1994-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 <unistd.h>
  28. #include <string.h>
  29. #include <errno.h>
  30. #include <fcntl.h>
  31. #include <stdlib.h>
  32. #include <sys/file.h>
  33. #include <signal.h>
  34. #include "Sys.h"
  35. #include "Dispatcher.h"
  36. #include "FaxMachineInfo.h"
  37. #include "FaxRecvInfo.h"
  38. #include "FaxSendInfo.h"
  39. #include "FaxAcctInfo.h"
  40. #include "UUCPLock.h"
  41. #include "faxSendApp.h"
  42. #include "config.h"
  43. /*
  44.  * HylaFAX Send Job Agent.
  45.  */
  46. faxSendApp* faxSendApp::_instance = NULL;
  47. faxSendApp::faxSendApp(const fxStr& devName, const fxStr& devID)
  48.     : FaxServer(devName, devID)
  49. {
  50.     ready = false;
  51.     modemLock = NULL;
  52.     setupConfig();
  53.     fxAssert(_instance == NULL, "Cannot create multiple faxSendApp instances");
  54.     _instance = this;
  55. }
  56. faxSendApp::~faxSendApp()
  57. {
  58.     delete modemLock;
  59. }
  60. faxSendApp& faxSendApp::instance() { return *_instance; }
  61. void
  62. faxSendApp::initialize(int argc, char** argv)
  63. {
  64.     FaxServer::initialize(argc, argv);
  65.     faxApp::initialize(argc, argv);
  66.     // NB: must do last to override config file information
  67.     for (GetoptIter iter(argc, argv, getOpts()); iter.notDone(); iter++)
  68. switch (iter.option()) {
  69. case 'l': // do uucp locking
  70.     modemLock = getUUCPLock(getModemDevice());
  71.     break;
  72. case 'c': // set configuration parameter
  73.     readConfigItem(iter.optArg());
  74.     break;
  75. }
  76. }
  77. void
  78. faxSendApp::open()
  79. {
  80.     FaxServer::open();
  81.     faxApp::open();
  82. }
  83. void
  84. faxSendApp::close()
  85. {
  86.     if (isRunning()) {
  87. if (state == FaxServer::SENDING) {
  88.     /*
  89.      * Terminate the active job and let the send
  90.      * operation complete so that the transfer is
  91.      * logged and the appropriate exit status is
  92.      * returned to the caller.
  93.      */
  94.     FaxServer::abortSession();
  95. } else {
  96.     FaxServer::close();
  97.     faxApp::close();
  98. }
  99.     }
  100. }
  101. # define BATCH_FIRST 1
  102. # define BATCH_LAST  2
  103. FaxSendStatus
  104. faxSendApp::send(const char** filenames, int num)
  105. {
  106.     u_int batched = BATCH_FIRST;
  107.     FaxSendStatus status = send_done;
  108.     fxStr batchcommid, notice, errorcode;
  109.     time_t retrybatchtts = 0;
  110.     for (int i = 0; i < num; i++)
  111.     {
  112. if (i+1 == num)
  113.     batched |= BATCH_LAST;
  114. int fd = Sys::open(filenames[i], O_RDWR);
  115. if (fd >= 0) {
  116.     if (flock(fd, LOCK_EX) >= 0) {
  117. FaxRequest* req = new FaxRequest(filenames[i], fd);
  118. /*
  119.  * Handle any session parameters that might be passed
  120.  * down from faxq (or possibly stuck in the modem config
  121.  * file).  Except for DesiredBR/EC/ST, these are applied 
  122.  * before reading the queue file so that any user-specified 
  123.  * values override.  We don't handle DesiredBR/EC/ST this
  124.  * way because it would break their usage in JobControls
  125.  * since there's currently no way for us to determine if
  126.  * the setting in the queue file is explicitly specified.
  127.  */
  128. bool reject;
  129. if (req->readQFile(reject) && !reject) {
  130.     if (status == send_done) { // only if the previous job in batch succeeded
  131. FaxMachineInfo info;
  132. info.updateConfig(canonicalizePhoneNumber(req->number));
  133. FaxAcctInfo ai;
  134. ai.start = Sys::now();
  135. /*
  136.  * Force any DesiredDF/BR/EC/ST options in the configuration
  137.  * files (i.e. JobControls) to take precedence over
  138.  * any user-specified settings.  This shouldn't cause
  139.  * too many problems, hopefully, since their usage should
  140.  * be fairly rare either by configuration settings or by
  141.  * user-specification.
  142.  */
  143. bool usedf = false; // to limit RTFCC application
  144. if (desiredDF != (u_int) -1) {
  145.     req->desireddf = desiredDF;
  146.     usedf = true;
  147. }
  148. if (desiredBR != (u_int) -1)
  149.     req->desiredbr = desiredBR;
  150. if (desiredEC != (u_int) -1)
  151.     req->desiredec = desiredEC;
  152. if (desiredST != (u_int) -1)
  153.     req->desiredst = desiredST;
  154. req->commid = batchcommid; // pass commid on...
  155. if (useJobTSI && req->tsi != "")
  156.     FaxServer::setLocalIdentifier(req->tsi);
  157. FaxServer::sendFax(*req, info, ai, batched, usedf);
  158. batchcommid = req->commid; // ... to all batched jobs
  159. ai.duration = Sys::now() - ai.start;
  160. ai.conntime = getConnectTime();
  161. ai.commid = req->commid;
  162. ai.device = getModemDeviceID();
  163. ai.dest = req->external;
  164. ai.jobid = req->jobid;
  165. ai.jobtag = req->jobtag;
  166. ai.user = req->mailaddr;
  167. ai.csi = info.getCSI();
  168. CallID empty_callid;
  169. ai.callid = empty_callid;
  170. ai.owner = req->owner;
  171. if (req->status == send_done)
  172.     ai.status = "";
  173. else {
  174.     errorcode = req->errorcode;
  175.     notice = req->notice;
  176.     ai.status = req->notice;
  177.     retrybatchtts = req->tts;
  178. }
  179. ai.jobinfo = fxStr::format("%u/%u/%u/%u/%u/%u/%u", 
  180.     req->totpages, req->ntries, req->ndials, req->totdials, req->maxdials, req->tottries, req->maxtries);
  181. if (!ai.record("SEND"))
  182.     logError("Error writing SEND accounting record, dest=%s",
  183. (const char*) ai.dest);
  184. status = req->status;
  185.     } else {
  186. /*
  187.  * This job cannot get sent right now due to an error in a previous
  188.  * job in the batch.
  189.  *
  190.  * In the event that the previous error was not an in-job error (e.g.
  191.  * a busy signal) then we treat that error as if it applies to all jobs.
  192.  * We "keep the batch together" by synchronizing their tts.
  193.  *
  194.  * In the event that the previous error was an in-job error, then the
  195.  * previous job processing is essentially blocking this job from 
  196.  * processing, and so we set the notice accordingly and don't increase
  197.  * the dial-count.  In case batching itself triggered the problem, we 
  198.  * don't set the tts, allowing faxq to reschedule the job, expecting that
  199.  * to disassemble and "shuffle" the entire batch.
  200.  */
  201. if (errorcode == "E001" || 
  202.     errorcode == "E002" || 
  203.     errorcode == "E003") {
  204.     /* busy, no carrier, no answer */
  205.     req->notice = notice;
  206.     req->status = send_retry;
  207.     req->tts = retrybatchtts;
  208.     req->totdials++;
  209. } else {
  210.     req->notice = "Blocked by another job";
  211.     req->status = send_retry;
  212. }
  213.     }
  214.     req->writeQFile(); // update on-disk copy
  215.     delete req;
  216. } else {
  217.     delete req;
  218.     logError("Could not read request file");
  219.     status = send_failed;
  220. }
  221.     } else {
  222. logError("Could not lock request file: %m");
  223. Sys::close(fd);
  224. status = send_failed;
  225.     }
  226. } else {
  227.     logError("Could not open request file "%s": %m", filenames[i]);
  228.     status = send_failed;
  229. }
  230. batched = 0;            // disable BATCH_FIRST and BATCH_LAST routines
  231.     }
  232.     return (status); // return status for exit
  233. }
  234. /*
  235.  * Modem locking support.
  236.  */
  237. bool faxSendApp::canLockModem()
  238. {
  239.     return (modemLock ? modemLock->check() : true);
  240. }
  241. bool
  242. faxSendApp::lockModem()
  243. {
  244.     return (modemLock ? modemLock->lock() : true);
  245. }
  246. void
  247. faxSendApp::unlockModem()
  248. {
  249.     if (modemLock)
  250. modemLock->unlock();
  251. }
  252. /*
  253.  * Notification handlers.
  254.  */
  255. /*
  256.  * Handle notification that the modem device has become
  257.  * available again after a period of being unavailable.
  258.  */
  259. void
  260. faxSendApp::notifyModemReady()
  261. {
  262.     ready = true;
  263. }
  264. /*
  265.  * Handle notification that the modem device looks to
  266.  * be in a state that requires operator intervention.
  267.  */
  268. void
  269. faxSendApp::notifyModemWedged()
  270. {
  271.     if (!sendModemStatus(getModemDeviceID(), "W"))
  272. logError("MODEM %s appears to be wedged",
  273.     (const char*) getModemDevice());
  274.     close();
  275. }
  276. void
  277. faxSendApp::notifyCallPlaced(const FaxRequest& req)
  278. {
  279.     sendJobStatus(req.jobid, "c");
  280.     FaxServer::notifyCallPlaced(req);
  281. }
  282. void
  283. faxSendApp::notifyConnected(const FaxRequest& req)
  284. {
  285.     sendJobStatus(req.jobid, "C");
  286.     FaxServer::notifyConnected(req);
  287. }
  288. void
  289. faxSendApp::notifyPageSent(FaxRequest& req, const char* filename)
  290. {
  291.     FaxSendInfo si(filename, req.commid, req.npages+1,
  292. getPageTransferTime(), getClientParams());
  293.     /*
  294.      * If the system is busy then sendJobStatus may not return
  295.      * quickly.  Thus we run it in a child process and move on.
  296.      */
  297.     pid_t pid = fork();
  298.     switch (pid) {
  299. case 0:
  300.     sendJobStatus(req.jobid, "d%s", (const char*) si.encode());
  301.     sleep(1); // XXX give parent time
  302.     _exit(0);
  303. case -1:
  304.     logError("Can not fork for non-priority logging.");
  305.     sendJobStatus(req.jobid, "d%s", (const char*) si.encode());
  306.     break;
  307. default:
  308.     Dispatcher::instance().startChild(pid, this);
  309.     break;
  310.     }
  311.     FaxServer::notifyPageSent(req, filename);
  312. }
  313. /*
  314.  * Handle notification that a document has been successfully
  315.  * transmitted.  We remove the file from the request array so
  316.  * that it's not resent if the job is requeued.
  317.  */
  318. void
  319. faxSendApp::notifyDocumentSent(FaxRequest& req, u_int fi)
  320. {
  321.     FaxSendInfo si(req.items[fi].item, req.commid, req.npages,
  322. getFileTransferTime(), getClientParams());
  323.     FaxServer::notifyDocumentSent(req, fi);
  324.     // NB: there is a racing with the scheduler and we should delay
  325.     //     the FIFO message to scheduler until we renamed the document
  326.     sendJobStatus(req.jobid, "D%s", (const char*) si.encode());
  327. }
  328. /*
  329.  * Handle notification of a document received as a
  330.  * result of a poll request.
  331.  */
  332. void
  333. faxSendApp::notifyPollRecvd(FaxRequest& req, FaxRecvInfo& ri)
  334. {
  335.     (void) sendJobStatus(req.jobid, "p%s", (const char*) ri.encode());
  336.     FaxServer::notifyPollRecvd(req, ri);
  337.     FaxAcctInfo ai;
  338.     ai.user = req.mailaddr;
  339.     ai.commid = getCommID();
  340.     ai.duration = (time_t) ri.time;
  341.     ai.start = Sys::now() - ai.duration;
  342.     ai.conntime = ai.duration;
  343.     ai.device = getModemDeviceID();
  344.     ai.dest = req.external;
  345.     ai.csi = ri.sender;
  346.     ai.npages = ri.npages;
  347.     ai.params = ri.params.encode();
  348.     ai.status = ri.reason;
  349.     ai.jobid = req.jobid;
  350.     ai.jobtag = req.jobtag;
  351.     CallID empty_callid;
  352.     ai.callid = empty_callid;
  353.     ri.params.asciiEncode(ai.faxdcs);
  354.     ai.jobinfo = fxStr::format("%u/%u/%u/%u/%u/%u/%u", 
  355. req.totpages, req.ntries, req.ndials, req.totdials, req.maxdials, req.tottries, req.maxtries);
  356.     if (!ai.record("POLL"))
  357. logError("Error writing POLL accounting record, dest=%s",
  358.     (const char*) ai.dest);
  359.     // hand to delivery/notification command
  360.     fxStr cmd(pollRcvdCmd
  361.  | quote |       quoted(req.mailaddr) | enquote
  362.  | quote |           quoted(ri.qfile) | enquote
  363.  | quote | quoted(getModemDeviceID()) | enquote
  364.  | quote |          quoted(ai.commid) | enquote
  365.  | quote |          quoted(ri.reason) | enquote
  366.      );
  367.     traceServer("RECV POLL: %s", (const char*) cmd);
  368.     setProcessPriority(BASE); // lower priority
  369.     runCmd(cmd, true, this);
  370.     setProcessPriority(state); // restore previous priority
  371. }
  372. /*
  373.  * Handle notification that a poll operation has been
  374.  * successfully completed.  Note that any received
  375.  * documents have already been passed to notifyPollRecvd.
  376.  */
  377. void
  378. faxSendApp::notifyPollDone(FaxRequest& req, u_int pi)
  379. {
  380.     FaxSendInfo si(req.items[pi].item, req.commid, req.npages,
  381. getFileTransferTime(), getClientParams());
  382.     sendJobStatus(req.jobid, "P%s", (const char*) si.encode());
  383.     FaxServer::notifyPollDone(req, pi);
  384. }
  385. /*
  386.  * Configuration support.
  387.  */
  388. void
  389. faxSendApp::resetConfig()
  390. {
  391.     FaxServer::resetConfig();
  392.     setupConfig();
  393. }
  394. #define N(a) (sizeof (a) / sizeof (a[0]))
  395. faxSendApp::stringtag faxSendApp::strings[] = {
  396. { "pollrcvdcmd", &faxSendApp::pollRcvdCmd, FAX_POLLRCVDCMD },
  397. };
  398. faxSendApp::numbertag faxSendApp::numbers[] = {
  399. { "desireddf", &faxSendApp::desiredDF, (u_int) -1 },
  400. { "desiredbr", &faxSendApp::desiredBR, (u_int) -1 },
  401. { "desiredst", &faxSendApp::desiredST, (u_int) -1 },
  402. { "desiredec", &faxSendApp::desiredEC, (u_int) -1 },
  403. };
  404. faxSendApp::booltag faxSendApp::booleans[] = {
  405. { "usejobtsi", &faxSendApp::useJobTSI, false },
  406. };
  407. void
  408. faxSendApp::setupConfig()
  409. {
  410.     int i;
  411.     for (i = N(strings)-1; i >= 0; i--)
  412. (*this).*strings[i].p = (strings[i].def ? strings[i].def : "");
  413.     for (i = N(numbers)-1; i >= 0; i--)
  414. (*this).*numbers[i].p = numbers[i].def;
  415.     for (i = N(booleans)-1; i >= 0; i--)
  416. (*this).*booleans[i].p = booleans[i].def;
  417. }
  418. bool
  419. faxSendApp::setConfigItem(const char* tag, const char* value)
  420. {
  421.     u_int ix;
  422.     if (findTag(tag, (const tags*) strings, N(strings), ix)) {
  423. (*this).*strings[ix].p = value;
  424.     } else if (findTag(tag, (const tags*) numbers, N(numbers), ix)) {
  425. (*this).*numbers[ix].p = getNumber(value);
  426.     } else if (findTag(tag, (const tags*) booleans, N(booleans), ix)) {
  427. (*this).*booleans[ix].p = getBoolean(value);
  428.     } else
  429. return (FaxServer::setConfigItem(tag, value));
  430.     return (true);
  431. }
  432. #undef N
  433. /*
  434.  * Miscellaneous stuff.
  435.  */
  436. static void
  437. usage(const char* appName)
  438. {
  439.     faxApp::fatal("usage: %s -m deviceID [-t tracelevel] [-l] qfile", appName);
  440. }
  441. static void
  442. sigCleanup(int s)
  443. {
  444.     int old_errno = errno;
  445.     signal(s, fxSIGHANDLER(sigCleanup));
  446.     logError("CAUGHT SIGNAL %d", s);
  447.     faxSendApp::instance().close();
  448.     if (!faxSendApp::instance().isRunning())
  449. _exit(send_failed);
  450.     errno = old_errno;
  451. }
  452. int
  453. main(int argc, char** argv)
  454. {
  455.     faxApp::setupLogging("FaxSend");
  456.     fxStr appName = argv[0];
  457.     u_int l = appName.length();
  458.     appName = appName.tokenR(l, '/');
  459.     faxApp::setOpts("c:m:lpx"); // p+x are for FaxServer
  460.     fxStr devID;
  461.     for (GetoptIter iter(argc, argv, faxApp::getOpts()); iter.notDone(); iter++)
  462. switch (iter.option()) {
  463. case 'm': devID = iter.optArg(); break;
  464. case '?': usage(appName);
  465. }
  466.     if (devID == "")
  467. usage(appName);
  468.     faxSendApp* app = new faxSendApp(faxApp::idToDev(devID), devID);
  469.     signal(SIGTERM, fxSIGHANDLER(sigCleanup));
  470.     signal(SIGINT, fxSIGHANDLER(sigCleanup));
  471.     app->initialize(argc, argv);
  472.     app->open();
  473.     while (app->isRunning() && !app->isReady())
  474. Dispatcher::instance().dispatch();
  475.     FaxSendStatus status;
  476.     if (app->isReady())
  477. status = app->send((const char**)&argv[optind], argc-optind);
  478.     else
  479. status = send_retry;
  480.     app->close();
  481.     return (status);
  482. }