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

传真(Fax)编程

开发平台:

C/C++

  1. /* $Id: SendFaxClient.c++,v 1.7 2008/05/14 00:49:55 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 "config.h"
  27. #include "Sys.h"
  28. #include <ctype.h>
  29. #include <string.h>
  30. #include <errno.h>
  31. #include "SendFaxClient.h"
  32. #include "TypeRules.h"
  33. #include "DialRules.h"
  34. #include "Array.h"
  35. struct FileInfo : public fxObj {
  36.     fxStr name; // user-specified document file
  37.     fxStr temp; // converted temporary file
  38.     fxStr doc; // document name on server
  39.     const TypeRule* rule;
  40.     FileInfo();
  41.     FileInfo(const FileInfo& other);
  42.     ~FileInfo();
  43. };
  44. fxDECLARE_ObjArray(FileInfoArray, FileInfo)
  45. struct PollRequest : public fxObj {
  46.     fxStr sep;
  47.     fxStr pwd;
  48.     PollRequest();
  49.     PollRequest(const PollRequest& other);
  50.     ~PollRequest();
  51. };
  52. fxDECLARE_ObjArray(PollRequestArray, PollRequest)
  53. SendFaxClient::SendFaxClient()
  54. {
  55.     jobs = new SendFaxJobArray;
  56.     typeRules = NULL;
  57.     dialRules = NULL;
  58.     files = new FileInfoArray;
  59.     polls = new PollRequestArray;
  60.     setup = false;
  61.     setupConfig();
  62. }
  63. SendFaxClient::~SendFaxClient()
  64. {
  65.     if (tmpFile != "")
  66. Sys::unlink(tmpFile);
  67.     delete typeRules;
  68.     delete dialRules;
  69.     delete polls;
  70.     delete files;
  71.     delete jobs;
  72. }
  73. void SendFaxClient::vprintError(const char* fmt, va_list ap)
  74.     { FaxClient::vprintError(fmt, ap); }
  75. void SendFaxClient::vprintWarning(const char* fmt, va_list ap)
  76.     { FaxClient::vprintWarning(fmt, ap); }
  77. void SendFaxClient::vtraceServer(const char* fmt, va_list ap)
  78.     { FaxClient::vtraceServer(fmt, ap); }
  79. bool
  80. SendFaxClient::prepareForJobSubmissions(fxStr& emsg)
  81. {
  82.     if (senderName == "" && !setupSenderIdentity(from, emsg))
  83. return (false);
  84.     /*
  85.      * Prepare documents for transmission.
  86.      */
  87.     if (typeRules == NULL) {
  88. typeRules = TypeRules::read(typeRulesFile);
  89. if (!typeRules) {
  90.     emsg = "Unable to setup file typing and conversion rules";
  91.     return (false);
  92. }
  93.     }
  94.     typeRules->setVerbose(verbose);
  95.     if (dialRules == NULL) {
  96. dialRules = new DialStringRules(dialRulesFile);
  97. dialRules->setVerbose(verbose);
  98. /*
  99.  * NB: Not finding a client-side dialrules file is not fatal; it
  100.  *     used to generate a warning message but so many people were
  101.  *     confused by this that the message has been removed so I no
  102.  *     longer have to explain why it's not a problem.
  103.  */
  104. (void) dialRules->parse(false);
  105.     } else
  106. dialRules->setVerbose(verbose);
  107.     /*
  108.      * Lock down job page size information.
  109.      */
  110.     u_int i, n;
  111.     for (i = 0, n = jobs->length(); i < n; i++) {
  112. SendFaxJob& job = (*jobs)[i];
  113. if (job.getPageWidth() != 0 && job.getPageLength() != 0)
  114.     continue;
  115. if (!job.setPageSize(job.getPageSize())) {
  116.     emsg = "Unknown page size " | job.getPageSize();
  117.     return (false);
  118. }
  119.     }
  120.     /*
  121.      * NB: Not (currently) smart enough to recognize when
  122.      *     documents need to be reprocessed.  For now we
  123.      *     just assume document conversions are not affected
  124.      *     by job state changes.
  125.      */
  126.     totalPages = 0;
  127.     for (i = 0, n = files->length(); i < n; i++)
  128. if (!prepareFile((*files)[i], emsg))
  129.     return (false);
  130.     /*
  131.      * Prepare cover pages.
  132.      */
  133.     for (i = 0, n = jobs->length(); i < n; i++) {
  134. SendFaxJob& job = (*jobs)[i];
  135. /*
  136.  * Convert dialstrings to a displayable format.  This
  137.  * deals with problems like calling card access codes
  138.  * getting stuck on the cover sheet and/or displayed in
  139.  * status messages.
  140.  */
  141. job.setExternalNumber(dialRules->displayNumber(job.getDialString()));
  142. /*
  143.  * Suppress the cover page if we're just doing a poll;
  144.  * otherwise, generate a cover sheet for each destination
  145.  * This done now so that we can be sure everything is ready
  146.  * to send before we setup a connection to the server.
  147.  */
  148. if (job.getAutoCoverPage() && getNumberOfFiles() > 0) {
  149.     fxStr file;
  150.     if (!makeCoverPage(job, file, emsg))
  151. return (false);
  152.     job.setCoverPageFile(file, true);
  153. }
  154.     }
  155.     return (setup = true);
  156. }
  157. static fxStr
  158. joinargs(const char* app, const char* av[])
  159. {
  160.     fxStr s(app);
  161.     for (int i = 1; av[i] != NULL; i += 2)
  162. s.append(fxStr::format(" %s '%s'", av[i], av[i+1]));
  163.     return s;
  164. }
  165. static void
  166. addarg(const char* av[], int& ac, const char* flag, const fxStr& opt)
  167. {
  168.     if (opt != "") {
  169. av[ac++] = flag;
  170. av[ac++] = opt;
  171.     }
  172. }
  173. /*
  174.  * Invoke the cover page generation program.
  175.  */
  176. bool
  177. SendFaxClient::makeCoverPage(const SendFaxJob& job, fxStr& file, fxStr& emsg)
  178. {
  179.     const char* templ = _PATH_TMP "/sndfaxXXXXXX";
  180.     char* buff = strcpy(new char[strlen(templ) + 1], templ);
  181.     int fd = Sys::mkstemp(buff);
  182.     tmpFile = buff;
  183.     delete [] buff;
  184.     if (fd >= 0) {
  185. #define MAXARGS 128
  186. const char* av[MAXARGS];
  187. int ac = 0;
  188. const char* cp = strrchr(coverCmd, '/');
  189. // NB: can't use ?: 'cuz of AIX compiler (XXX)
  190. if (cp)
  191.     av[ac++] = cp+1; // program name
  192. else
  193.     av[ac++] = coverCmd;
  194. addarg(av, ac, "-C", job.getCoverTemplate());
  195. addarg(av, ac, "-D", dateFormat);
  196. addarg(av, ac, "-c", job.getCoverComments());
  197. addarg(av, ac, "-f", senderName);
  198. addarg(av, ac, "-l", job.getCoverLocation());
  199. addarg(av, ac, "-n", job.getExternalNumber());
  200. addarg(av, ac, "-r", job.getCoverRegarding());
  201. addarg(av, ac, "-s", job.getPageSize());
  202. addarg(av, ac, "-t", job.getCoverName());
  203. addarg(av, ac, "-v", job.getCoverVoiceNumber());
  204. addarg(av, ac, "-x", job.getCoverCompany());
  205. addarg(av, ac, "-L", job.getCoverFromLocation());
  206. addarg(av, ac, "-N", job.getCoverFromFax());
  207. addarg(av, ac, "-V", job.getCoverFromVoice());
  208. addarg(av, ac, "-X", job.getCoverFromCompany());
  209. addarg(av, ac, "-M", job.getMailbox());
  210. fxStr pages;
  211. if (totalPages > 0) {
  212.     pages = fxStr::format("%u", totalPages);
  213.     addarg(av, ac, "-p", pages);
  214. }
  215. av[ac] = NULL;
  216. if (verbose)
  217.     printf("COVER SHEET "%s"n", (const char*) joinargs(coverCmd,av));
  218. int pfd[2];
  219. if (pipe(pfd) >= 0) {
  220.     pid_t pid = fork();
  221.     switch (pid) {
  222.     case -1: // error
  223. emsg = fxStr::format("Error creating cover sheet; "
  224.     "could not fork subprocess: %s", strerror(errno));
  225. Sys::close(pfd[1]);
  226. break;
  227.     case 0: // child, exec command
  228. if (pfd[1] != STDOUT_FILENO)
  229.     dup2(pfd[1], STDOUT_FILENO);
  230. // XXX should close other descriptors
  231. dup2(STDOUT_FILENO, STDERR_FILENO);
  232. Sys::execv(coverCmd, (char* const*) av);
  233. _exit(-1);
  234. /*NOTREACHED*/
  235.     default: // parent, read from pipe and wait
  236. Sys::close(pfd[1]);
  237. { char buf[16*1024]; // XXX for HP C++ compiler
  238.   int cc, ignore;
  239.   while ((cc = read(pfd[0], buf, sizeof (buf))) > 0) {
  240.       ignore = write(fd, buf, cc);
  241.   }
  242. }
  243. (void) Sys::close(pfd[0]);
  244. (void) Sys::close(fd);
  245. int status;
  246. if (Sys::waitpid(pid, status) == pid && status == 0) {
  247.     file = tmpFile;
  248.     return (true);
  249. }
  250. emsg = fxStr::format("Error creating cover sheet; "
  251.     "command was "%s"; exit status %x"
  252.     , (const char*) joinargs(coverCmd, av)
  253.     , status
  254. );
  255. break;
  256.     }
  257.     Sys::close(pfd[0]);
  258. } else {
  259.     emsg = fxStr::format("Error creating cover sheet; "
  260. "unable to create pipe to subprocess: %s", strerror(errno));
  261. }
  262. #undef MAXARGS
  263.     } else
  264. emsg = fxStr::format("%s: Can not create temporary file for cover page",
  265.     (const char*) tmpFile);
  266.     Sys::unlink(tmpFile);
  267.     return (false);
  268. }
  269. SendFaxJob&
  270. SendFaxClient::addJob(void)
  271. {
  272.     u_int ix = jobs->length();
  273.     jobs->resize(ix+1);
  274.     (*jobs)[ix] = proto;
  275.     setup = false;
  276.     return ((*jobs)[ix]);
  277. }
  278. u_int SendFaxClient::getNumberOfJobs() const { return jobs->length(); }
  279. SendFaxJob*
  280. SendFaxClient::findJob(const fxStr& number, const fxStr& name)
  281. {
  282.     for (u_int i = 0, n = jobs->length(); i < n; i++) {
  283. SendFaxJob& job = (*jobs)[i];
  284. if (job.getDialString() != number)
  285.     continue;
  286. if (name != "" && job.getCoverName() == name)
  287.     return (&job);
  288.     }
  289.     return (NULL);
  290. }
  291. SendFaxJob*
  292. SendFaxClient::findJobByTag(const fxStr& tag)
  293. {
  294.     for (u_int i = 0, n = jobs->length(); i < n; i++) {
  295. SendFaxJob& job = (*jobs)[i];
  296. if (job.getJobTag() == tag)
  297.     return (&job);
  298.     }
  299.     return (NULL);
  300. }
  301. void
  302. SendFaxClient::removeJob(const SendFaxJob& job)
  303. {
  304.     u_int ix = jobs->find(job);
  305.     if (ix != fx_invalidArrayIndex)
  306. jobs->remove(ix);
  307. }
  308. /*
  309.  * Add a new file to send to each destination.
  310.  */
  311. u_int
  312. SendFaxClient::addFile(const fxStr& filename)
  313. {
  314.     u_int ix = files->length();
  315.     files->resize(ix+1);
  316.     (*files)[ix].name = filename;
  317.     setup = false;
  318.     return (ix);
  319. }
  320. const fxStr&
  321. SendFaxClient::getFileDocument(u_int ix) const
  322. {
  323.     return (ix < files->length() ? (*files)[ix].doc : fxStr::null);
  324. }
  325. u_int SendFaxClient::getNumberOfFiles() const { return files->length(); }
  326. u_int
  327. SendFaxClient::findFile(const fxStr& filename) const
  328. {
  329.     for (u_int i = 0, n = files->length(); i < n; i++)
  330. if ((*files)[i].name == filename)
  331.     return (i);
  332.     return (fx_invalidArrayIndex);
  333. }
  334. void
  335. SendFaxClient::removeFile(u_int ix)
  336. {
  337.     if (ix < files->length())
  338. files->remove(ix);
  339. }
  340. void
  341. SendFaxClient::purgeFileConversions()
  342. {
  343.     for (u_int i = 0, n = files->length(); i < n; i++) {
  344. FileInfo& info = (*files)[i];
  345. if (info.temp != "" && info.temp != info.name) {
  346.     Sys::unlink(info.temp);
  347.     info.temp = "";
  348. }
  349.     }
  350. }
  351. u_int
  352. SendFaxClient::addPollRequest()
  353. {
  354.     return addPollRequest(fxStr::null, fxStr::null);
  355. }
  356. u_int
  357. SendFaxClient::addPollRequest(const fxStr& sep)
  358. {
  359.     return addPollRequest(sep, fxStr::null);
  360. }
  361. u_int
  362. SendFaxClient::addPollRequest(const fxStr& sep, const fxStr& pwd)
  363. {
  364.     u_int ix = polls->length();
  365.     polls->resize(ix+1);
  366.     (*polls)[ix].sep = sep;
  367.     (*polls)[ix].pwd = pwd;
  368.     setup = false;
  369.     return (ix);
  370. }
  371. u_int SendFaxClient::getNumberOfPollRequests() const { return polls->length(); }
  372. void
  373. SendFaxClient::getPollRequest(u_int ix, fxStr& sep, fxStr& pwd)
  374. {
  375.     if (ix < polls->length()) {
  376. sep = (*polls)[ix].sep;
  377. pwd = (*polls)[ix].pwd;
  378.     }
  379. }
  380. void
  381. SendFaxClient::removePollRequest(u_int ix)
  382. {
  383.     if (ix < polls->length())
  384. polls->remove(ix);
  385. }
  386. /*
  387.  * Submit documents and jobs to the server.
  388.  */
  389. bool
  390. SendFaxClient::submitJobs(fxStr& emsg)
  391. {
  392.     if (!setup) {
  393. emsg = "Documents not prepared";
  394. return (false);
  395.     }
  396.     if (!isLoggedIn()) {
  397. emsg = "Not logged in to server";
  398. return (false);
  399.     }
  400.     /*
  401.      * Transfer documents to the server.
  402.      */
  403.     if (!sendDocuments(emsg))
  404. return (false);
  405.     /*
  406.      * Construct jobs and submit them.
  407.      */
  408.     if (!(*jobs)[0].initJobs(*this, emsg))
  409. return (false);
  410.     for (u_int i = 0, n = jobs->length(); i < n; i++) {
  411. SendFaxJob& job = (*jobs)[i];
  412. if (!job.createJob(*this, emsg))
  413.     return (false);
  414. if (!jobSubmit(job.getJobID())) {
  415.     emsg = getLastResponse();
  416.     return (false);
  417. }
  418. notifyNewJob(job); // notify client
  419.     }
  420.     return (true);
  421. }
  422. /*
  423.  * Transfer the document files to the server and
  424.  * record the serve-side documents for job submission.
  425.  */
  426. bool
  427. SendFaxClient::sendDocuments(fxStr& emsg)
  428. {
  429.     emsg = "";
  430.     for (u_int i = 0, n = files->length(); i < n; i++) {
  431. FileInfo& info = (*files)[i];
  432. int fd = Sys::open(info.temp, O_RDONLY);
  433. if (fd < 0) {
  434.     emsg = fxStr::format(info.temp | ": Can not open: %s",
  435. strerror(errno));
  436.     return (false); // XXX
  437. }
  438. bool fileSent;
  439. if (info.rule->getResult() == TypeRule::TIFF) {
  440.     fileSent = setFormat(FORM_TIFF)
  441.     && setType(TYPE_I)
  442.     && sendData(fd, &FaxClient::storeTemp, info.doc, emsg);
  443. } else if (info.rule->getResult() == TypeRule::PCL) {
  444.     fileSent = setFormat(FORM_PCL)
  445.          && setType(TYPE_I)
  446.     && sendData(fd, &FaxClient::storeTemp, info.doc, emsg);
  447. } else if (info.rule->getResult() == TypeRule::PDF) {
  448.     fileSent = setFormat(FORM_PDF)
  449.          && setType(TYPE_I)
  450.     && sendData(fd, &FaxClient::storeTemp, info.doc, emsg);
  451. } else {
  452.     fileSent = setFormat(FORM_PS)
  453.          && setType(TYPE_I) // XXX TYPE_A???
  454.     && sendZData(fd, &FaxClient::storeTemp, info.doc, emsg);
  455. }
  456. Sys::close(fd);
  457. if (!fileSent) {
  458.     if (emsg == "")
  459. emsg = "Document transfer failed: " | getLastResponse();
  460.     return (false);
  461. }
  462.     }
  463.     return (true);
  464. }
  465. /*
  466.  * Default notification handler for when a new job is created.
  467.  */
  468. void
  469. SendFaxClient::notifyNewJob(const SendFaxJob& job)
  470. {
  471.     int nfiles = files->length();
  472.     printf("request id is %s (group id %s) for host %s (%u %s)n"
  473. , (const char*) job.getJobID()
  474. , (const char*) job.getGroupID()
  475. , (const char*) getHost()
  476. , nfiles
  477. , nfiles > 1 ? "files" : "file"
  478.     );
  479. }
  480. /*
  481.  * Configuration file support.
  482.  */
  483. #define N(a) (sizeof (a) / sizeof (a[0]))
  484. const SendFaxClient::SF_stringtag SendFaxClient::strings[] = {
  485. { "typerules", &SendFaxClient::typeRulesFile,
  486.   FAX_LIBDATA "/" FAX_TYPERULES },
  487. { "dialrules", &SendFaxClient::dialRulesFile,
  488.   FAX_LIBDATA "/" FAX_DIALRULES },
  489. { "covercmd", &SendFaxClient::coverCmd,
  490.   FAX_CLIENTBIN "/" "faxcover" },
  491. { "from", &SendFaxClient::from, NULL },
  492. { "dateformat", &SendFaxClient::dateFormat, NULL },
  493. };
  494. void
  495. SendFaxClient::setupConfig()
  496. {
  497.     for (int i = N(strings)-1; i >= 0; i--)
  498. (*this).*strings[i].p = (strings[i].def ? strings[i].def : "");
  499.     verbose = false;
  500.     delete typeRules, typeRules = NULL;
  501.     delete dialRules, dialRules = NULL;
  502.     proto.setupConfig();
  503. }
  504. void
  505. SendFaxClient::resetConfig()
  506. {
  507.     FaxClient::resetConfig();
  508.     setupConfig();
  509. }
  510. bool
  511. SendFaxClient::setConfigItem(const char* tag, const char* value)
  512. {
  513.     u_int ix;
  514.     if (findTag(tag, (const tags*) strings, N(strings), ix)) {
  515. (*this).*strings[ix].p = value;
  516.     } else if (streq(tag, "verbose")) {
  517. setVerbose(getBoolean(value));
  518. FaxClient::setVerbose(getVerbose()); // XXX
  519.     } else if (proto.setConfigItem(tag, value)) {
  520. ;
  521.     } else if (!FaxClient::setConfigItem(tag, value))
  522. return (false);
  523.     return (true);
  524. }
  525. #undef N
  526. /*
  527.  * Setup the sender's identity.
  528.  */
  529. bool
  530. SendFaxClient::setupSenderIdentity(const fxStr& from, fxStr& emsg)
  531. {
  532.     FaxClient::setupUserIdentity(emsg); // client identity
  533.     if (from != "") {
  534. u_int l = from.next(0, '<');
  535. if (l == from.length()) {
  536.     l = from.next(0, '(');
  537.     if (l != from.length()) { // joe@foobar (Joe Schmo)
  538. setBlankMailboxes(from.head(l));
  539. l++, senderName = from.token(l, ')');
  540.     } else { // joe
  541. setBlankMailboxes(from);
  542. if (from == getUserName())
  543.     senderName = FaxClient::getSenderName();
  544. else
  545.     senderName = "";
  546.     }
  547. } else { // Joe Schmo <joe@foobar>
  548.     senderName = from.head(l);
  549.     l++, setBlankMailboxes(from.token(l, '>'));
  550. }
  551. // strip leading&trailing white space and quotes
  552. senderName.remove(0, senderName.skip(0, " t""));
  553. senderName.resize(senderName.skipR(senderName.length(), " t""));
  554. if (senderName == "" && getNonBlankMailbox(senderName)) {
  555.     /*
  556.      * Mail address, but no "real name"; construct one from
  557.      * the account name.  Do this by first stripping anything
  558.      * to the right of an '@' and then stripping any leading
  559.      * uucp patch (host!host!...!user).
  560.      */
  561.     senderName.resize(senderName.next(0, '@'));
  562.     senderName.remove(0, senderName.nextR(senderName.length(), '!'));
  563. }
  564. // strip and leading&trailing white space
  565. senderName.remove(0, senderName.skip(0, " t"));
  566. senderName.resize(senderName.skipR(senderName.length(), " t"));
  567.     } else {
  568. senderName = FaxClient::getSenderName();
  569. setBlankMailboxes(getUserName());
  570.     }
  571.     fxStr mbox;
  572.     if (senderName == "" || !getNonBlankMailbox(mbox)) {
  573. emsg = "Malformed (null) sender name or mail address";
  574. return (false);
  575.     } else
  576. return (true);
  577. }
  578. void SendFaxClient::setFromIdentity(const char* s) { from = s; }
  579. /*
  580.  * Assign the specified string to any unspecified email
  581.  * addresses used for notification mail.
  582.  */
  583. void
  584. SendFaxClient::setBlankMailboxes(const fxStr& s)
  585. {
  586.     for (u_int i = 0, n = jobs->length(); i < n; i++) {
  587. SendFaxJob& job = (*jobs)[i];
  588. if (job.getMailbox() == "")
  589.     job.setMailbox(s);
  590.     }
  591. }
  592. /*
  593.  * Return the first non-null mailbox string
  594.  * in the set of jobs.
  595.  */
  596. bool
  597. SendFaxClient::getNonBlankMailbox(fxStr& s)
  598. {
  599.     for (u_int i = 0, n = jobs->length(); i < n; i++) {
  600. SendFaxJob& job = (*jobs)[i];
  601. if (job.getMailbox() != "") {
  602.     s = job.getMailbox();
  603.     return (true);
  604. }
  605.     }
  606.     return (false);
  607. }
  608. /*
  609.  * Process a file submitted for transmission.
  610.  */
  611. bool
  612. SendFaxClient::prepareFile(FileInfo& info, fxStr& emsg)
  613. {
  614.     info.rule = fileType(info.name, emsg);
  615.     if (!info.rule)
  616. return (false);
  617.     if (info.temp != "" && info.temp != info.name)
  618. Sys::unlink(info.temp);
  619.     if (info.rule->getCmd() != "") { // conversion required
  620.         const char* templ = _PATH_TMP "/sndfaxXXXXXX";
  621.         char* buff = strcpy(new char[strlen(templ) + 1], templ);
  622.         Sys::mktemp(buff);
  623.         tmpFile = buff;
  624.         delete [] buff;
  625.         /*
  626.  * XXX **** WARNING **** XXXX
  627.  *
  628.  * We need to generate files according to each job's
  629.  * parameters (page dimensions, resolution, etc.) but
  630.  * the existing protocol does not support doing this
  631.  * right so for now we assume all jobs share these
  632.  * parameters and just use the values from the first
  633.  * prototype job.
  634.  */
  635. fxStr sysCmd = info.rule->getFmtdCmd(info.name, tmpFile,
  636. proto.getHResolution(), proto.getVResolution(),
  637. "1", proto.getPageSize());
  638. if (verbose)
  639.     printf("CONVERT "%s"n", (const char*) sysCmd);
  640. if (system(sysCmd) != 0) {
  641.     Sys::unlink(tmpFile);
  642.     emsg = fxStr::format("Error converting data; command was "%s"",
  643. (const char*) sysCmd);
  644.     return (false);
  645. }
  646. info.temp = tmpFile;
  647.     } else // already postscript, pdf, pcl, or tiff
  648. info.temp = info.name;
  649.     switch (info.rule->getResult()) {
  650.     case TypeRule::TIFF:
  651. countTIFFPages(info.temp);
  652. break;
  653.     case TypeRule::PCL:
  654. // maybe use pclcount/pcl6count from http://www.fea.unicamp.br/pclcount/
  655. break;
  656.     case TypeRule::POSTSCRIPT:
  657.     case TypeRule::PDF:
  658. estimatePostScriptPages(info.temp);
  659. break;
  660.     }
  661.     return (true);
  662. }
  663. /*
  664.  * Return a TypeRule for the specified file.
  665.  */
  666. const TypeRule*
  667. SendFaxClient::fileType(const char* filename, fxStr& emsg)
  668. {
  669.     struct stat sb;
  670.     int fd = Sys::open(filename, O_RDONLY);
  671.     if (fd < 0) {
  672. emsg = fxStr::format("%s: Can not open file", filename);
  673. return (NULL);
  674.     }
  675.     if (Sys::fstat(fd, sb) < 0) {
  676. emsg = fxStr::format("%s: Can not stat file", filename);
  677. Sys::close(fd);
  678. return (NULL);
  679.     }
  680.     if ((sb.st_mode & S_IFMT) != S_IFREG) {
  681. emsg = fxStr::format("%s: Not a regular file", filename);
  682. Sys::close(fd);
  683. return (NULL);
  684.     }
  685.     char buf[512];
  686.     int cc = Sys::read(fd, buf, sizeof (buf));
  687.     Sys::close(fd);
  688.     if (cc == 0) {
  689. emsg = fxStr::format("%s: Empty file", filename);
  690. return (NULL);
  691.     }
  692.     const TypeRule* tr = typeRules->match(buf, cc);
  693.     if (!tr) {
  694. emsg = fxStr::format("%s: Can not determine file type", filename);
  695. return (NULL);
  696.     }
  697.     if (tr->getResult() == TypeRule::ERROR) {
  698. emsg = fxStr::format("%s: ", filename) | tr->getErrMsg();
  699. return (NULL);
  700.     }
  701.     return tr;   
  702. }
  703. #include "tiffio.h"
  704. /*
  705.  * Count the number of ``pages'' in a TIFF file.
  706.  */
  707. void
  708. SendFaxClient::countTIFFPages(const char* filename)
  709. {
  710.     TIFF* tif = TIFFOpen(filename, "r");
  711.     if (tif) {
  712. do {
  713.     totalPages++;
  714. } while (TIFFReadDirectory(tif));
  715. TIFFClose(tif);
  716.     }
  717. }
  718. /*
  719.  * Count the number of pages in a PostScript file.
  720.  * We can really only estimate the number as we
  721.  * depend on the DSC comments to figure this out.
  722.  */
  723. void
  724. SendFaxClient::estimatePostScriptPages(const char* filename)
  725. {
  726.     FILE* fd = fopen(filename, "r");
  727.     if (fd != NULL) {
  728. char line[2048];
  729. if (fgets(line, sizeof (line)-1, fd) != NULL) {
  730.     /*
  731.      * We only consider ``conforming'' PostScript documents.
  732.      */
  733.     if (line[0] == '%' && line[1] == '!') {
  734. int npagecom = 0; // # %%Page comments
  735. int npages = 0; // # pages according to %%Pages comments
  736. while (fgets(line, sizeof (line)-1, fd) != NULL) {
  737.     int n;
  738.     if (strncmp(line, "%%Page:", 7) == 0)
  739. npagecom++;
  740.     else if (sscanf(line, "%%%%Pages: %u", &n) == 1)
  741. npages += n;
  742. }
  743. /*
  744.  * Believe %%Pages comments over counting of %%Page comments.
  745.  */
  746. if (npages > 0)
  747.     totalPages += npages;
  748. else if (npagecom > 0)
  749.     totalPages += npagecom;
  750.     } else if (memcmp(line, "%PDF", 4) == 0) {
  751.         int npages = 0;         // # pages according to "/Type /Page"
  752.         const int slen = 12;    // len of "/Type /Page" + 1 char to check for 's'
  753.         char* endbuf = line+sizeof(line);
  754.   
  755.         rewind(fd);
  756.         char *cp = line;
  757. int len;
  758.         while ((len = fread(cp, 1, endbuf-cp, fd)) > slen+line-cp) {
  759.                     endbuf = cp+len;    // Will only change on the last pass.
  760.             cp = line;
  761.             while ((cp = (char *) memchr((const char*) cp, '/', endbuf-cp-slen))) {
  762.                 if ((memcmp(cp, "/Type /Page", slen-1) == 0 && *(cp+slen-1) != 's') || 
  763.     (memcmp(cp, "/Type/Page",  slen-2) == 0 && *(cp+slen-2) != 's'))
  764.                     npages++;
  765. cp++;
  766.             }
  767.             if ((cp = (char *) memchr((const char*) endbuf-slen, '/', slen))) {
  768.         memcpy(line, cp, endbuf-cp);
  769.         cp = line+(endbuf-cp);
  770.     } else
  771.                 cp = line;
  772.         }
  773.         if (npages > 0)
  774.             totalPages += npages;
  775.     }
  776. }
  777. fclose(fd);
  778.     }
  779. }
  780. FileInfo::FileInfo()
  781. {
  782.     rule = NULL;
  783. }
  784. FileInfo::FileInfo(const FileInfo& other)
  785.     : fxObj(other)
  786.     , name(other.name)
  787.     , temp(other.temp)
  788.     , rule(other.rule)
  789. {
  790. }
  791. FileInfo::~FileInfo()
  792. {
  793.     if (temp != name)
  794. Sys::unlink(temp);
  795. }
  796. fxIMPLEMENT_ObjArray(FileInfoArray, FileInfo)
  797. PollRequest::PollRequest() {}
  798. PollRequest::~PollRequest() {}
  799. PollRequest::PollRequest(const PollRequest& other)
  800.     : fxObj(other)
  801.     , sep(other.sep)
  802.     , pwd(other.pwd)
  803. {}
  804. fxIMPLEMENT_ObjArray(PollRequestArray, PollRequest)