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

传真(Fax)编程

开发平台:

C/C++

  1. /* $Id: FaxRequest.c++,v 1.18 2008/09/16 05:39:07 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.h"
  27. #include "FaxRequest.h"
  28. #include "StackBuffer.h"
  29. #include "class2.h"
  30. #include "config.h"
  31. #include "hash.h" // pre-calculated hash values
  32. #include <ctype.h>
  33. #include <errno.h>
  34. /*
  35.  * HylaFAX job request file handling.
  36.  */
  37. extern void vlogError(const char* fmt, va_list ap);
  38. extern void logError(const char* fmt ...);
  39. FaxRequest::FaxRequest(const fxStr& qf, int f) : qfile(qf)
  40. {
  41.     reset();
  42.     fd = f;
  43. }
  44. void
  45. FaxRequest::reset(void)
  46. {
  47.     tts = 0;
  48.     killtime = 0;
  49.     retrytime = 0;
  50.     state = 0;
  51.     status = send_retry;
  52.     pri = (u_short) -1;
  53.     usrpri = FAX_DEFPRIORITY;
  54.     pagewidth = pagelength = resolution = 0;
  55.     npages = totpages = skippages = skippedpages = nocountcover = 0;
  56.     ntries = ndials = 0;
  57.     minbr = BR_2400;
  58.     desiredbr = BR_33600;
  59.     desiredst = ST_0MS;
  60.     desiredec = EC_ENABLE256;
  61.     desireddf = DF_2DMMR;
  62.     desiredtl = 0;
  63.     totdials = 0, maxdials = (u_short) FAX_REDIALS;
  64.     tottries = 0, maxtries = (u_short) FAX_RETRIES;
  65.     useccover = true;
  66.     usexvres = false;
  67.     serverdocover = false;
  68.     ignoremodembusy = false;
  69.     pagechop = chop_default;
  70.     chopthreshold = -1;
  71.     csi = fxStr::null;
  72.     nsf = fxStr::null;
  73.     notify = no_notice;
  74.     jobtype = "facsimile"; // for compatibility w/ old clients
  75.     writeQFilePid = 0;
  76. }
  77. FaxRequest::~FaxRequest()
  78. {
  79.     if (fd != -1)
  80. Sys::close(fd);
  81. }
  82. #define N(a) (sizeof (a) / sizeof (a[0]))
  83. FaxRequest::stringval FaxRequest::strvals[] = {
  84.     { "canonical", &FaxRequest::canonical },
  85.     { "external", &FaxRequest::external },
  86.     { "number", &FaxRequest::number },
  87.     { "mailaddr", &FaxRequest::mailaddr },
  88.     { "sender", &FaxRequest::sender },
  89.     { "jobid", &FaxRequest::jobid },
  90.     { "jobtag", &FaxRequest::jobtag },
  91.     { "pagehandling", &FaxRequest::pagehandling },
  92.     { "modem", &FaxRequest::modem },
  93.     { "modemused", &FaxRequest::modemused },
  94.     { "faxnumber", &FaxRequest::faxnumber },
  95.     { "faxname", &FaxRequest::faxname },
  96.     { "tsi", &FaxRequest::tsi },
  97.     { "receiver", &FaxRequest::receiver },
  98.     { "company", &FaxRequest::company },
  99.     { "location", &FaxRequest::location },
  100.     { "voice", &FaxRequest::voice },
  101.     { "fromcompany", &FaxRequest::fromcompany },
  102.     { "fromlocation", &FaxRequest::fromlocation },
  103.     { "fromvoice", &FaxRequest::fromvoice },
  104.     { "regarding", &FaxRequest::regarding },
  105.     { "comments", &FaxRequest::comments },
  106.     { "cover", &FaxRequest::cover },
  107.     { "client", &FaxRequest::client },
  108.     { "owner", &FaxRequest::owner },
  109.     { "groupid", &FaxRequest::groupid },
  110.     { "signalrate", &FaxRequest::sigrate },
  111.     { "dataformat", &FaxRequest::df },
  112.     { "jobtype", &FaxRequest::jobtype },
  113.     { "tagline", &FaxRequest::tagline },
  114.     { "subaddr", &FaxRequest::subaddr },
  115.     { "passwd", &FaxRequest::passwd },
  116.     { "doneop", &FaxRequest::doneop },
  117.     { "commid", &FaxRequest::commid },
  118.     { "csi", &FaxRequest::csi },
  119.     { "nsf", &FaxRequest::nsf },
  120.     { "timeofday", &FaxRequest::timeofday },
  121.     { "errorcode", &FaxRequest::errorcode },
  122. };
  123. FaxRequest::shortval FaxRequest::shortvals[] = {
  124.     { "state", &FaxRequest::state },
  125.     { "npages", &FaxRequest::npages },
  126.     { "skippages", &FaxRequest::skippages },
  127.     { "nocountcover", &FaxRequest::nocountcover },
  128.     { "serverdocover", &FaxRequest::serverdocover },
  129.     { "ignoremodembusy",&FaxRequest::ignoremodembusy },
  130.     { "totpages", &FaxRequest::totpages },
  131.     { "ntries", &FaxRequest::ntries },
  132.     { "ndials", &FaxRequest::ndials },
  133.     { "totdials", &FaxRequest::totdials },
  134.     { "maxdials", &FaxRequest::maxdials },
  135.     { "tottries", &FaxRequest::tottries },
  136.     { "maxtries", &FaxRequest::maxtries },
  137.     { "pagewidth", &FaxRequest::pagewidth },
  138.     { "resolution", &FaxRequest::resolution },
  139.     { "pagelength", &FaxRequest::pagelength },
  140.     { "priority", &FaxRequest::usrpri },
  141.     { "schedpri", &FaxRequest::pri },
  142.     { "minbr", &FaxRequest::minbr },
  143.     { "desiredbr", &FaxRequest::desiredbr },
  144.     { "desiredst", &FaxRequest::desiredst },
  145.     { "desiredec", &FaxRequest::desiredec },
  146.     { "desireddf", &FaxRequest::desireddf },
  147.     { "desiredtl", &FaxRequest::desiredtl },
  148.     { "useccover", &FaxRequest::useccover },
  149.     { "usexvres", &FaxRequest::usexvres },
  150. };
  151. FaxRequest::intval FaxRequest::intvals[] = {
  152.     { "skippedpages", &FaxRequest::skippedpages },
  153. };
  154. const char* FaxRequest::opNames[18] = {
  155.     "fax",
  156.     "tiff",
  157.     "!tiff",
  158.     "pdf",
  159.     "!pdf",
  160.     "postscript",
  161.     "!postscript",
  162.     "pcl",
  163.     "!pcl",
  164.     "data",
  165.     "!data",
  166.     "poll",
  167.     "page",
  168.     "!page",
  169.     "uucp",
  170.     "15", "16", "17"
  171. };
  172. const char* FaxRequest::notifyVals[4] = {
  173.     "none", // no_notice
  174.     "when done", // when_done
  175.     "when requeued", // when_requeued
  176.     "when done+requeued" // when_done|when_requeued
  177. };
  178. const char* FaxRequest::chopVals[4] = {
  179.     "default", // chop_default
  180.     "none", // chop_none
  181.     "all", // chop_all
  182.     "last" // chop_last
  183. };
  184. /*
  185.  * Parse the contents of a job description file.
  186.  */
  187. bool
  188. FaxRequest::readQFile(bool& rejectJob)
  189. {
  190.     rejectJob = false;
  191.     lineno = 0;
  192.     lseek(fd, 0L, SEEK_SET); // XXX should only for re-read
  193.     /*
  194.      * Read the file contents in with one read.  If the
  195.      * file is too large to fit in the buffer allocated
  196.      * on the stack then dynamically allocate one.  The
  197.      * 2K size was chosen based on statistics; most files
  198.      * are less than 1K in size.
  199.      *
  200.      * NB: we don't mmap the file because we're going to
  201.      *     modify its contents in memory during parsing
  202.      *     (plus it's not clear that mmap is a win for
  203.      *     such a small file).
  204.      */
  205.     struct stat sb;
  206.     Sys::fstat(fd, sb);
  207.     if (sb.st_size < 2) {
  208. error("Corrupted file (too small)");
  209. return (false);
  210.     }
  211.     char stackbuf[2048];
  212.     char* buf = stackbuf;
  213.     char* bp = buf;
  214.     if (sb.st_size > (off_t)sizeof(stackbuf)-1) // extra byte for terminating 
  215. bp = buf = new char[sb.st_size+1];
  216.     if (Sys::read(fd, bp, (u_int) sb.st_size) != sb.st_size) {
  217. error("Read error: %s", strerror(errno));
  218. if (buf != stackbuf)
  219.     delete [] buf;
  220. return (false);
  221.     }
  222.     /*
  223.      * Force n-termination of the last line in the
  224.      * file.  This simplifies the logic below by always
  225.      * being able to look for n-termination and not
  226.      * worry about running off the end of the buffer.
  227.      */
  228.     char* ep = bp+sb.st_size;
  229.     if (ep[-1] != 'n')
  230. ep[0] = 'n';
  231.     do {
  232. lineno++;
  233. /*
  234.  * Collect command identifier and calculate hash.
  235.  * The hash value is used to identify the command
  236.  * using a set of pre-calculated values (see the
  237.  * mkhash program).  Note that other strings might
  238.  * hash to valid hash values; we don't care or check
  239.  * for this because we know the client-server protocol
  240.  * process only writes valid entries in the file.
  241.  */
  242. const char* cmd = bp;
  243. HASH_DECLARE(hash);
  244. for (; *bp != ':' && *bp != 'n'; bp++)
  245. {
  246.     HASH_ITERATE(hash, *bp);
  247. }
  248. if (*bp != ':') { // invalid, skip line
  249.     error("Syntax error, missing ':' on line %u", (u_int) lineno);
  250.     while (*bp++ != 'n')
  251. ;
  252.     continue;
  253. }
  254. HASH_FINISH(hash);
  255. *bp++ = ''; // null-terminate cmd
  256. /*
  257.  * Collect the parameter value.
  258.  */
  259. while (*bp == ' ') // skip leading white space
  260.     bp++;
  261. char* tag = bp;
  262. while (*bp != 'n')
  263.     bp++;
  264. *bp++ = ''; // null-terminate tag
  265. // logError("%s[%u]: %s", cmd, hash, tag);
  266. switch (hash) {
  267. case H_EXTERNAL: external = tag; break;
  268. case H_CANONICAL: canonical = tag; break;
  269. case H_NUMBER: number = tag; break;
  270. case H_MAILADDR: mailaddr = tag; break;
  271. case H_SENDER: sender = tag; break;
  272. case H_JOBID: jobid = tag; break;
  273. case H_SERVERDOCOVER: serverdocover = tag[0] - '0'; break;
  274. case H_JOBTAG: jobtag = tag; break;
  275. case H_COMMID: commid = tag; break;
  276. case H_PAGEHANDLING: pagehandling = tag; break;
  277. case H_MODEM: modem = tag; break;
  278. case H_MODEMUSED: modemused = tag; break;
  279. case H_FAXNUMBER: faxnumber = tag; break;
  280. case H_FAXNAME: faxname = tag; break;
  281. case H_TSI: tsi = tag; break;
  282. case H_CSI: csi = tag; break;
  283. case H_RECEIVER: receiver = tag; break;
  284. case H_COMPANY: company = tag; break;
  285. case H_LOCATION: location = tag; break;
  286. case H_VOICE: voice = tag; break;
  287. case H_FROMLOCATION: fromlocation = tag; break;
  288. case H_FROMVOICE: fromvoice = tag; break;
  289. case H_REGARDING: regarding = tag; break;
  290. case H_COVER: cover = tag; break;
  291. case H_CLIENT: client = tag; break;
  292. case H_OWNER: owner = tag; break;
  293. case H_GROUPID: groupid = tag; break;
  294. case H_SIGNALRATE: sigrate = tag; break;
  295. case H_DATAFORMAT: df = tag; break;
  296. case H_JOBTYPE: jobtype = tag; break;
  297. case H_TAGLINE: tagline = tag; break;
  298. case H_TOTTRIES: tottries = atoi(tag); break;
  299. case H_SUBADDR: subaddr = tag; break;
  300. case H_PASSWD: passwd = tag; break;
  301. case H_STATE: state = tag[0] - '0'; break;
  302. case H_COMMENTS: comments = tag; break;
  303. case H_NPAGES: npages = atoi(tag); break;
  304. case H_FROMCOMPANY: fromcompany = tag; break;
  305. case H_TOTPAGES: totpages = atoi(tag); break;
  306. case H_NTRIES: ntries = atoi(tag); break;
  307. case H_MAXTRIES: maxtries = atoi(tag); break;
  308. case H_NDIALS: ndials = atoi(tag); break;
  309. case H_TOTDIALS: totdials = atoi(tag); break;
  310. case H_MAXDIALS: maxdials = atoi(tag); break;
  311. case H_PAGEWIDTH: pagewidth = atoi(tag); break;
  312. case H_RESOLUTION: resolution = atoi(tag); break;
  313. case H_PAGELENGTH: pagelength = atoi(tag); break;
  314. case H_PRIORITY: usrpri = atoi(tag); break;
  315. case H_SCHEDPRI: pri = atoi(tag); break;
  316. case H_SKIPPAGES: skippages = atoi(tag); break;
  317. case H_DESIREDBR: desiredbr = atoi(tag); break;
  318. case H_DESIREDST: desiredst = tag[0] - '0'; break;
  319. case H_DESIREDEC: desiredec = tag[0] - '0'; break;
  320. case H_SKIPPEDPAGES: skippedpages = atoi(tag); break;
  321. case H_DESIREDDF: desireddf = tag[0] - '0'; break;
  322. case H_DESIREDTL: desiredtl = tag[0] - '0'; break;
  323. case H_USECCOVER: useccover = tag[0] - '0'; break;
  324. case H_USEXVRES: usexvres = tag[0] - '0'; break;
  325. case H_IGNOREMODEMBUSY: ignoremodembusy = tag[0] - '0'; break;
  326. case H_TTS:
  327.     tts = atoi(tag);
  328.     if (tts == 0) // distinguish ``now'' from unset
  329. tts = Sys::now();
  330.     break;
  331. case H_NOCOUNTCOVER: nocountcover = atoi(tag); break;
  332. case H_KILLTIME: killtime = atoi(tag); break;
  333. case H_RETRYTIME: retrytime = atoi(tag); break;
  334. case H_NOTIFY: checkNotifyValue(tag); break;
  335. case H_PAGECHOP: checkChopValue(tag); break;
  336. case H_CHOPTHRESHOLD: chopthreshold = atof(tag); break;
  337. case H_NSF: nsf = tag; break;
  338. case H_TIMEOFDAY: timeofday = tag; break;
  339. case H_ERRORCODE: errorcode = tag; break;
  340. case H_DONEOP: doneop = tag; break;
  341. case H_RETURNED: status = (FaxSendStatus) atoi(tag); break;
  342. case H_MINBR: minbr = atoi(tag); break;
  343. case H_STATUS:
  344.     /*
  345.      * Check for multi-line status strings.
  346.      */
  347.     if (bp-tag > 1 && bp[-2] == '\') {
  348. *--bp = 'n'; // put back original n
  349. do {
  350.     lineno++, bp++;
  351.     while (*bp != 'n')
  352. bp++;
  353. } while (*bp == 'n' && bp > tag && bp[-1] == '\');
  354. *bp++ = '';
  355.     }
  356.     notice = tag;
  357.     break;
  358. case H_POLL: addItem(send_poll, tag); break;
  359. case H_FAX: addItem(send_fax, tag); break;
  360. case H_PDF:
  361.     if (cmd[0] == '!')
  362. addItem(send_pdf_saved, tag);
  363.     else
  364. addItem(send_pdf, tag, rejectJob);
  365.     break;
  366. case H_TIFF:
  367.     if (cmd[0] == '!')
  368. addItem(send_tiff_saved, tag);
  369.     else
  370. addItem(send_tiff, tag, rejectJob);
  371.     break;
  372. case H_POSTSCRIPT:
  373.     if (cmd[0] == '!')
  374. addItem(send_postscript_saved, tag);
  375.     else
  376. addItem(send_postscript, tag, rejectJob);
  377.     break;
  378. case H_PCL:
  379.     if (cmd[0] == '!')
  380. addItem(send_pcl_saved, tag);
  381.     else
  382. addItem(send_pcl, tag, rejectJob);
  383.     break;
  384. case H_DATA:
  385.     if (cmd[0] == '!')
  386. addItem(send_data_saved, tag);
  387.     else
  388. addItem(send_data, tag, rejectJob);
  389.     break;
  390. case H_PAGE:
  391.     if (cmd[0] == '!')
  392. addItem(send_page_saved, tag);
  393.     else
  394. addItem(send_page, tag);
  395.     break;
  396. default:
  397.     error("Unknown field %s[%u]: %s", cmd, hash, tag);
  398. }
  399.     } while (bp < ep);
  400.     if (pri == (u_short) -1)
  401. pri = usrpri;
  402.     if (tts == 0) // distinguish ``now'' from unset
  403. tts = Sys::now();
  404.     /*
  405.      * Validate certain items that are assumed to have
  406.      * ``suitable values'' by higher-level code (i.e.
  407.      * the scheduler).
  408.      */
  409.     if (state < state_suspended || state > state_failed) {
  410. error("Invalid scheduler state %u in job request", state);
  411. rejectJob = true;
  412.     }
  413. #define isNull(s) ((s).length() == 0)
  414.     if (isNull(number) || isNull(mailaddr) || isNull(sender) || isNull(jobid)
  415.      || isNull(modem)  || isNull(client)   || isNull(owner)) {
  416. rejectJob = true;
  417. error("Null or missing %s in job request",
  418.     isNull(number)   ? "number" :
  419.     isNull(mailaddr) ? "mailaddr" :
  420.     isNull(sender)   ? "sender" :
  421.     isNull(jobid)    ? "jobid" :
  422.     isNull(modem)    ? "modem" :
  423.     isNull(client)   ? "client" :
  424.        "owner"
  425. );
  426.     }
  427.     if (minbr > BR_33600) minbr = BR_33600;
  428.     if (desiredbr > BR_33600) desiredbr = BR_33600;
  429.     if (desiredst > ST_40MS) desiredst = ST_40MS;
  430.     if (desiredec > EC_ECLFULL) desiredec = EC_ECLFULL;
  431.     if (desireddf > DF_2DMMR) desireddf = DF_2DMMR;
  432.     if (buf != stackbuf) // dynamically allocated buffer
  433. delete [] buf;
  434.     return (true);
  435. }
  436. /*
  437.  * Re-read a job description file.
  438.  *
  439.  * Note that a file that has been written *should*
  440.  * have every entry set in it and so re-reading it
  441.  * will cause all fields to be written.  This means
  442.  * that we should not need to reset the state to the
  443.  * default values assigned when the instance is created;
  444.  * except for the items array which is dynamically
  445.  * allocated and appended to.  If you don't believe
  446.  * this, enable the code notdef'd out below.
  447.  */
  448. bool
  449. FaxRequest::reReadQFile(bool& rejectJob)
  450. {
  451. #ifdef notdef
  452.     reset(); // non-string items
  453.     for (int i = N(strvals)-1; i >= 0; i--) // string stuff
  454. (*this).*strvals[i].p = "";
  455. #endif
  456.     items.resize(0); // document/polling requests
  457.     return (readQFile(rejectJob));
  458. }
  459. #define DUMP(fp, vals, fmt, cast) {
  460.     for (u_int i = 0; i < N(vals); i++)
  461. sb.fput(fmt, vals[i].name, cast((*this).*vals[i].p));
  462. }
  463. /*
  464.  * Maybe write to temp and rename instead of
  465.  * updating in-place.  The files are so small
  466.  * however that we're unlikely to ever get ENOSPC
  467.  * when updating an existing file.  Also by using
  468.  * an existing file we avoid allocating a new
  469.  * disk block each time, instead overwriting the
  470.  * already allocated one.  This can be meaningful
  471.  * on a busy server given the potential number of
  472.  * times we update the q file.
  473.  */
  474. void
  475. FaxRequest::writeQFile()
  476. {
  477.     fxStackBuffer sb;
  478.     sb.fput("tts:%un", tts);
  479.     sb.fput("killtime:%un", killtime);
  480.     sb.fput("retrytime:%un", retrytime);
  481.     /*
  482.      * Pull errorcode out from notice.
  483.      */
  484.     u_int ecodestart = notice.find(0, "{");
  485.     u_int ecodeend = notice.find(ecodestart, "}");
  486.     if (ecodestart < notice.length() && ecodeend <= notice.length() && ecodeend == ecodestart + 5) {
  487. errorcode = notice.extract(ecodestart+1, 4);
  488. notice.remove(ecodestart, 6);
  489.     } else if (notice == "")
  490. errorcode = "";
  491.     DUMP(fp, shortvals, "%s:%dn", (int));
  492.     DUMP(fp, intvals, "%s:%dn", (int));
  493.     DUMP(fp, strvals, "%s:%sn", (const char*));
  494.     /*
  495.      * Escape unprotected n's with \.
  496.      */
  497.     sb.put("status:");
  498.     const char* cp = notice;
  499.     const char* sp = cp;
  500.     while (*cp) {
  501. if (*cp == 'n' && cp[-1] != '\') {
  502.    sb.put(sp, cp-sp);
  503.    sb.put('\');
  504.    sp = cp;
  505. }
  506. cp++;
  507.     }
  508.     sb.put(sp, cp-sp); sb.put('n');
  509.     sb.fput("returned:%dn", status);
  510.     sb.fput("notify:%sn", notifyVals[notify&3]);
  511.     sb.fput("pagechop:%sn", chopVals[pagechop&3]);
  512.     sb.fput("chopthreshold:%gn", chopthreshold);
  513.     for (u_int i = 0; i < items.length(); i++) {
  514. const FaxItem& fitem = items[i];
  515. sb.fput("%s:%u:%s:%sn"
  516.     , opNames[fitem.op&15]
  517.     , fitem.dirnum
  518.     , (const char*) fitem.addr
  519.     , (const char*) fitem.item
  520. );
  521.     }
  522.     lseek(fd, 0L, SEEK_SET);
  523.     Sys::write(fd, sb, sb.getLength());
  524.     int ignore = ftruncate(fd, sb.getLength());
  525.     // XXX maybe should fsync, but not especially portable
  526. }
  527. /*
  528.  * Return the base document name given a
  529.  * per-job document name (either with a
  530.  * jobid suffix or, if a cover page, with
  531.  * a ``.cover'' suffix).
  532.  */
  533. fxStr
  534. FaxRequest::mkbasedoc(const fxStr& file)
  535. {
  536.     fxStr doc(file);
  537.     u_int l = doc.nextR(doc.length(), '.');
  538.     doc.resize(l-1);
  539.     return (doc);
  540. }
  541. /*
  542.  * Check if a document that is about to be removed from
  543.  * the job request was converted from another.  If so,
  544.  * rename the source document according to convention
  545.  * so that all references to the document point to the
  546.  * same file when everything has been sent.  This has the
  547.  * effect of decrementing the link count on the source
  548.  * file and permits us to use the link count as a reference
  549.  * use count for releasing imaged documents (see the
  550.  * large comments explaining this in the scheduler).
  551.  */
  552. void
  553. FaxRequest::renameSaved(u_int fi)
  554. {
  555.     if (fi > 0 && items[fi-1].isSavedOp()) {
  556. FaxItem& src = items[fi-1];
  557. fxStr basedoc = mkbasedoc(src.item);
  558. if (Sys::rename(src.item, basedoc) < 0) {
  559.     logError("Unable to rename transmitted document %s: %s",
  560. (const char*) src.item, strerror(errno));
  561. }
  562. // Posix rename will succeed without doing anything if the
  563. // source and destination files are hard linked
  564. Sys::unlink(src.item); // just remove it
  565. src.item = basedoc; // change job reference
  566.     }
  567. }
  568. /*
  569.  * Does the specified document (assumed to be send_fax)
  570.  * appear to have any potential source references?
  571.  */
  572. bool
  573. FaxRequest::isUnreferenced(u_int fi)
  574. {
  575.     if (fi > 0 && items[fi-1].isSavedOp()) {
  576. struct stat sb;
  577. if (Sys::stat(mkbasedoc(items[fi-1].item), sb) < 0 ||
  578.   sb.st_nlink == 1)
  579.     return (true);
  580.     }
  581.     return (false);
  582. }
  583. static bool
  584. hasDotDot(const char* pathname)
  585. {
  586.     const char* cp = pathname;
  587.     while (cp) {
  588. if (cp[0] == '.') // NB: good enough
  589.     return (true);
  590. if ((cp = strchr(cp, '/')))
  591.     cp++;
  592.     }
  593.     return (false);
  594. }
  595. bool
  596. FaxRequest::checkDocument(const char* pathname)
  597. {
  598.     /*
  599.      * Scan full pathname to disallow access to
  600.      * files outside the spooling hiearchy.
  601.      */
  602.     if (pathname[0] == '/' || hasDotDot(pathname)) {
  603. error("Invalid document file "%s"", pathname);
  604. return (false);
  605.     }
  606.     int fd = Sys::open(pathname, 0);
  607.     if (fd == -1) {
  608. error("Can not access document file "%s": %s",
  609.     pathname, strerror(errno));
  610. return (false);
  611.     }
  612.     Sys::close(fd);
  613.     return (true);
  614. }
  615. /*
  616.  * Add a request entry that does not require checking
  617.  * the document pathname to make sure that it is valid.
  618.  */
  619. void
  620. FaxRequest::addItem(FaxSendOp op, char* tag)
  621. {
  622.     char* cp = tag;
  623.     while (*cp && *cp != ':')
  624. cp++;
  625.     int dirnum;
  626.     if (*cp == ':') { // directory index
  627. dirnum = atoi(tag);
  628. tag = ++cp;
  629.     } else
  630. dirnum = 0;
  631.     while (*cp && *cp != ':')
  632. cp++;
  633.     if (*cp == ':') // address info
  634. *cp++ = '';
  635.     else
  636. cp = tag, tag = '';
  637.     items.append(FaxItem(op, dirnum, tag, cp));
  638. }
  639. /*
  640.  * Add a request entry and verify the document is valid.
  641.  */
  642. void
  643. FaxRequest::addItem(FaxSendOp op, char* tag, bool& rejectJob)
  644. {
  645.     char* cp = tag;
  646.     while (*cp && *cp != ':')
  647. cp++;
  648.     int dirnum;
  649.     if (*cp == ':') { // directory index
  650. dirnum = atoi(tag);
  651. tag = ++cp;
  652.     } else
  653. dirnum = 0;
  654.     while (*cp && *cp != ':')
  655. cp++;
  656.     if (*cp == ':') // address info
  657. *cp++ = '';
  658.     else
  659. cp = tag, tag = '';
  660.     if (!checkDocument(cp)) {
  661.      error("Rejected document in corrupt job request");
  662. rejectJob = true;
  663.     }
  664.     else
  665. items.append(FaxItem(op, dirnum, tag, cp));
  666. }
  667. bool
  668. FaxRequest::isStrCmd(const char* cmd, u_int& ix)
  669. {
  670.     for (int i = N(strvals)-1; i >= 0; i--)
  671. if (strcmp(strvals[i].name, cmd) == 0) {
  672.     ix = i;
  673.     return (true);
  674. }
  675.     return (false);
  676. }
  677. bool
  678. FaxRequest::isShortCmd(const char* cmd, u_int& ix)
  679. {
  680.     for (int i = N(shortvals)-1; i >= 0; i--)
  681. if (strcmp(shortvals[i].name, cmd) == 0) {
  682.     ix = i;
  683.     return (true);
  684. }
  685.     return (false);
  686. }
  687. bool
  688. FaxRequest::isIntCmd(const char* cmd, u_int& ix)
  689. {
  690.     for (int i = N(intvals)-1; i >= 0; i--)
  691. if (strcmp(intvals[i].name, cmd) == 0) {
  692.     ix = i;
  693.     return (true);
  694. }
  695.     return (false);
  696. }
  697. void
  698. FaxRequest::checkNotifyValue(const char* tag)
  699. {
  700.     for (int i = N(notifyVals)-1; i >= 0; i--)
  701.  if (strcmp(notifyVals[i], tag) == 0) {
  702.     notify = i;
  703.     return;
  704. }
  705.     error("Invalid notify value "%s"", tag);
  706. }
  707. void
  708. FaxRequest::checkChopValue(const char* tag)
  709. {
  710.     for (int i = N(chopVals)-1; i >= 0; i--)
  711.  if (strcmp(chopVals[i], tag) == 0) {
  712.     pagechop = i;
  713.     return;
  714. }
  715.     error("Invalid pagechop value "%s"", tag);
  716. }
  717. u_int
  718. FaxRequest::findItem(FaxSendOp op, u_int ix) const
  719. {
  720.     while (ix < items.length()) {
  721. if (items[ix].op == op)
  722.     return (ix);
  723. ix++;
  724.     }
  725.     return fx_invalidArrayIndex;
  726. }
  727. void
  728. FaxRequest::insertFax(u_int ix, const fxStr& file)
  729. {
  730.     items.insert(FaxItem(send_fax, 0, fxStr::null, file), ix);
  731. }
  732. void
  733. FaxRequest::error(const char* fmt0 ...)
  734. {
  735.     fxStr fmt = fxStr::format("%s: line %u: %s", (const char*) qfile, (u_int) lineno, fmt0);
  736.     va_list ap;
  737.     va_start(ap, fmt0);
  738.     vlogError(fmt, ap);
  739.     va_end(ap);
  740. }