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

传真(Fax)编程

开发平台:

C/C++

  1. /* $Id: FileSystem.c++,v 1.11 2009/06/22 06:26:07 faxguy Exp $ */
  2. /*
  3.  * Copyright (c) 1995-1996 Sam Leffler
  4.  * Copyright (c) 1995-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. /*
  27.  * File commands other than transfer.
  28.  */
  29. #include "HylaFAXServer.h"
  30. #include "Sys.h"
  31. #include "tiffio.h"
  32. #include <limits.h>
  33. #include <ctype.h>
  34. /*
  35.  * Delete a file (submitted document or received facsimile).
  36.  */
  37. void
  38. HylaFAXServer::deleCmd(const char* pathname)
  39. {
  40.     struct stat sb;
  41.     SpoolDir* dir = fileAccess(pathname, W_OK, sb);
  42.     if (dir) {
  43. if (!IS(PRIVILEGED)) {
  44.     if (!S_ISREG(sb.st_mode)) {
  45. reply(550, "%s: not a plain file.", pathname);
  46. return;
  47.     }
  48.     if (!dir->deleAble) {
  49. perror_reply(550, pathname, EPERM);
  50. return;
  51.     }
  52. }
  53. /*
  54.  * XXX must decide what to do with jobs/documents/etc...
  55.  * XXX e.g. should job be treated like jdele; should we
  56.  * XXX cross-check document use for consistency....
  57.  */
  58. if (Sys::unlink(pathname) < 0)
  59.     perror_reply(550, pathname, errno);
  60. else {
  61.     const char* cp = strrchr(pathname, '/');
  62.     logNotice("%s of %s [%s] deleted %s%s"
  63. , (const char*) the_user
  64. , (const char*) remotehost
  65. , (const char*) remoteaddr
  66. , dir->pathname, cp ? cp+1 : pathname
  67.     );
  68.     ack(250, cmdToken(T_DELE));
  69.     FileCache::flush(pathname);
  70. }
  71.     }
  72. }
  73. /*
  74.  * Set ownership of a file.
  75.  */
  76. void
  77. HylaFAXServer::chownCmd(const char* pathname, const char* user)
  78. {
  79.     struct stat sb;
  80.     SpoolDir* dir = fileAccess(pathname, W_OK, sb);
  81.     if (dir) {
  82. u_int id;
  83. if (isdigit(user[0]))
  84.     id = (u_int) atoi(user);
  85. else if (!userID(user, id))
  86.     return;
  87. if (!FileCache::chown(pathname, sb.st_uid, (gid_t) id))
  88.     perror_reply(550, pathname, errno);
  89. else
  90.     ack(250, cmdToken(T_CHOWN));
  91.     }
  92. }
  93. /*
  94.  * Set protection of a file.
  95.  */
  96. void
  97. HylaFAXServer::chmodCmd(const char* pathname, u_int mode)
  98. {
  99.     struct stat sb;
  100.     SpoolDir* dir = fileAccess(pathname, W_OK, sb);
  101.     if (dir) {
  102. mode = 0600 | (mode&066);
  103. if (!FileCache::chmod(pathname, mode))
  104.     perror_reply(550, pathname, errno);
  105. else
  106.     ack(250, cmdToken(T_CHMOD));
  107.     }
  108. }
  109. /*
  110.  * Return the last modification time for a file.
  111.  */
  112. void
  113. HylaFAXServer::mdtmCmd(const char* pathname)
  114. {
  115.     struct stat sb;
  116.     SpoolDir* dir = fileAccess(pathname, X_OK, sb);
  117.     if (dir) {
  118. struct tm* t = cvtTime(sb.st_mtime);
  119. reply(213, "%d%02d%02d%02d%02d%02d"
  120.     , t->tm_year+1900
  121.     , t->tm_mon+1
  122.     , t->tm_mday
  123.     , t->tm_hour
  124.     , t->tm_min
  125.     , t->tm_sec
  126. );
  127.     }
  128. }
  129. /*
  130.  * NB: this array is ordered by expected frequency of access.
  131.  */
  132. SpoolDir HylaFAXServer::dirs[] = {
  133. { "/status/", false, false, false, 0,
  134.   &HylaFAXServer::isVisibletrue,
  135.   &HylaFAXServer::listStatus, &HylaFAXServer::listStatusFile,
  136.   &HylaFAXServer::nlstStatus, &HylaFAXServer::nlstUnixFile, },
  137. { "/sendq/", false, false, false, 0,
  138.   &HylaFAXServer::isVisibleSendQFile,
  139.   &HylaFAXServer::listSendQ, &HylaFAXServer::listSendQFile,
  140.   &HylaFAXServer::nlstSendQ, &HylaFAXServer::nlstSendQFile, },
  141. { "/doneq/", false, false, false, 0,
  142.   &HylaFAXServer::isVisibleSendQFile,
  143.   &HylaFAXServer::listSendQ, &HylaFAXServer::listSendQFile,
  144.   &HylaFAXServer::nlstSendQ, &HylaFAXServer::nlstSendQFile, },
  145. { "/docq/", false,  true,  true, 0,
  146.   &HylaFAXServer::isVisibleDocQFile,
  147.   &HylaFAXServer::listDirectory, &HylaFAXServer::listUnixFile,
  148.   &HylaFAXServer::nlstDirectory, &HylaFAXServer::nlstUnixFile, },
  149. { "/tmp/", false,  true,  true, 0,
  150.   &HylaFAXServer::isVisibletrue,
  151.   &HylaFAXServer::listDirectory, &HylaFAXServer::listUnixFile,
  152.   &HylaFAXServer::nlstDirectory, &HylaFAXServer::nlstUnixFile, },
  153. { "/log/", false, false, false, 0,
  154.   &HylaFAXServer::isVisibletrue,
  155.   &HylaFAXServer::listDirectory, &HylaFAXServer::listUnixFile,
  156.   &HylaFAXServer::nlstDirectory, &HylaFAXServer::nlstUnixFile, },
  157. { "/recvq/", false, false,  true, 0,
  158.   &HylaFAXServer::isVisibleRecvQFile,
  159.   &HylaFAXServer::listRecvQ, &HylaFAXServer::listRecvQFile,
  160.   &HylaFAXServer::nlstDirectory, &HylaFAXServer::nlstUnixFile, },
  161. { "/archive/", false, false, false, 0,
  162.   &HylaFAXServer::isVisibletrue,
  163.   &HylaFAXServer::listDirectory, &HylaFAXServer::listUnixFile,
  164.   &HylaFAXServer::nlstDirectory, &HylaFAXServer::nlstUnixFile, },
  165. { "/pollq/", false,  true,  true, 0,
  166.   &HylaFAXServer::isVisibleRecvQFile,
  167.   &HylaFAXServer::listRecvQ, &HylaFAXServer::listRecvQFile,
  168.   &HylaFAXServer::nlstDirectory, &HylaFAXServer::nlstUnixFile, },
  169. { "/", false, false, false, 0,
  170.   &HylaFAXServer::isVisibleRootFile,
  171.   &HylaFAXServer::listDirectory, &HylaFAXServer::listUnixFile,
  172.   &HylaFAXServer::nlstDirectory, &HylaFAXServer::nlstUnixFile, },
  173. { "/etc/",  true, false, false, 0,
  174.   &HylaFAXServer::isVisibletrue,
  175.   &HylaFAXServer::listDirectory, &HylaFAXServer::listUnixFile,
  176.   &HylaFAXServer::nlstDirectory, &HylaFAXServer::nlstUnixFile, },
  177. { "/info/", false, false, false, 0,
  178.   &HylaFAXServer::isVisibletrue,
  179.   &HylaFAXServer::listDirectory, &HylaFAXServer::listUnixFile,
  180.   &HylaFAXServer::nlstDirectory, &HylaFAXServer::nlstUnixFile, },
  181. { "/bin/",  true, false, false, 0,
  182.   &HylaFAXServer::isVisibletrue,
  183.   &HylaFAXServer::listDirectory, &HylaFAXServer::listUnixFile,
  184.   &HylaFAXServer::nlstDirectory, &HylaFAXServer::nlstUnixFile, },
  185. { "/config/", false, false, false, 0,
  186.   &HylaFAXServer::isVisibletrue,
  187.   &HylaFAXServer::listDirectory, &HylaFAXServer::listUnixFile,
  188.   &HylaFAXServer::nlstDirectory, &HylaFAXServer::nlstUnixFile, },
  189. { "/client/",  true, false, false, 0,
  190.   &HylaFAXServer::isVisibletrue,
  191.   &HylaFAXServer::listDirectory, &HylaFAXServer::listUnixFile,
  192.   &HylaFAXServer::nlstDirectory, &HylaFAXServer::nlstUnixFile, },
  193. };
  194. #define N(a) (sizeof (a) / sizeof (a[0]))
  195. /*
  196.  * Initialize the directory handling.
  197.  */
  198. void
  199. HylaFAXServer::dirSetup(void)
  200. {
  201.     for (u_int i = 0, n = N(dirs); i < n; i++) {
  202. struct stat sb;
  203. if (!FileCache::lookup(dirs[i].pathname, sb) || !S_ISDIR(sb.st_mode)) {
  204.     logError("%s: Not a directory.", dirs[i].pathname);
  205.     continue;
  206. }
  207. dirs[i].ino = sb.st_ino;
  208. if (streq(dirs[i].pathname, "/"))
  209.     cwd = &dirs[i];
  210.     }
  211. }
  212. /*
  213.  * Return a directory handle given a pathname.
  214.  */
  215. SpoolDir*
  216. HylaFAXServer::dirLookup(const char* path)
  217. {
  218.     struct stat sb;
  219.     return (!FileCache::lookup(path, sb) || !S_ISDIR(sb.st_mode) ?
  220. (SpoolDir*) NULL : dirLookup(sb.st_ino));
  221. }
  222. /*
  223.  * Return a directory handle given an inode number.
  224.  */
  225. SpoolDir*
  226. HylaFAXServer::dirLookup(ino_t ino)
  227. {
  228.     for (u_int i = 0, n = N(dirs); i < n; i++)
  229. if (dirs[i].ino == ino)
  230.     return (&dirs[i]);
  231.     return (NULL);
  232. }
  233. /*
  234.  * Check file protection mode; we use the
  235.  * normal ``other'' bits for public access
  236.  * and the group bits for the ``fax uid''.
  237.  */
  238. bool
  239. HylaFAXServer::checkFileRights(int op, const struct stat& sb)
  240. {
  241.     if (IS(PRIVILEGED))
  242. return (true);
  243.     if (sb.st_mode & op) // public access
  244. return (true);
  245.     if ((sb.st_gid==uid) && ((sb.st_mode>>3)&op)) // owner access
  246. return (true);
  247.     return false;
  248. }
  249. /*
  250.  * Check if the client is permitted to do the
  251.  * request operation on the specified file.
  252.  * Operations are: X_OK (list/stat), R_OK (look
  253.  * at the contents), W_OK (write/delete contents).
  254.  *
  255.  * If the operation is permitted the stat result
  256.  * for the target file is returned to the caller
  257.  * for use in futher checks (e.g. type of file).
  258.  */
  259. SpoolDir*
  260. HylaFAXServer::fileAccess(const char* path, int op, struct stat& sb)
  261. {
  262.     if (!FileCache::lookup(path, sb)) { // file not found
  263. if (op != W_OK || errno != ENOENT) {
  264.     perror_reply(550, path, errno);
  265.     return (NULL);
  266. }
  267. /*
  268.  * The file does not exist, fake up the
  269.  * information that would result from a
  270.  * successful file creation--for use below.
  271.  */
  272. sb.st_mode = S_IFREG|S_IRGRP|S_IWGRP;
  273. sb.st_gid = (gid_t) uid;
  274. sb.st_ino = 0; // NB: to be created
  275.     }
  276.     /*
  277.      * Validate containing directory and target file.
  278.      * The directory must exist and the client must
  279.      * have permission to access it.  The target file
  280.      * must not be a directory if the client is about
  281.      * to do a read/write-style operation or the target
  282.      * file must be owned by the client (other checks
  283.      * are left to the caller).
  284.      */
  285.     struct stat db;
  286.     const char* cp = strrchr(path, '/');
  287.     if (cp) {
  288. cp++; // include "/"
  289. if (!FileCache::lookup(fxStr(path, cp-path), db)) {
  290.     perror_reply(550, path, ENOENT);
  291.     return (NULL);
  292. }
  293.     } else {
  294. if (!FileCache::lookup(cwd->pathname, db)) { // implicit ref to "."
  295.     perror_reply(550, path, ENOENT);
  296.     return (NULL);
  297. }
  298. cp = path;
  299.     }
  300.     SpoolDir* dir = dirLookup(db.st_ino);
  301.     if (!dir) { // no containing dir
  302. perror_reply(550, path, ENOENT);
  303. return (NULL);
  304.     } else if (!IS(PRIVILEGED)) { // unprivileged client
  305. if (dir->adminOnly) { // requires admin priv's
  306.     perror_reply(550, path, EPERM);
  307.     return (NULL);
  308. } else if (!fileVisible(*dir, cp, sb)) { // not visible w/o admin
  309.     perror_reply(550, path, EPERM);
  310.     return (NULL);
  311. } else if (op != X_OK) { // client wants to r/w
  312.     if (S_ISDIR(sb.st_mode)) { // cannot r/w directory
  313. reply(550, "%s: not a plain file.", path);
  314. return (NULL);
  315.     } else if (sb.st_ino == 0 && !dir->storAble) {
  316. perror_reply(550, path, EPERM); // cannot stor in dir.
  317. return (NULL);
  318.     }
  319.     if (!checkFileRights(op, sb)) {
  320. perror_reply(550, path, EPERM);
  321. return (NULL);
  322.     }
  323. }
  324.     } else { // privileged client
  325. if (op != X_OK && S_ISDIR(sb.st_mode)) { // cannot r/w directory
  326.     reply(550, "%s: not a plain file.", path);
  327.     return (NULL);
  328. }
  329.     }
  330.     return (dir);
  331. }
  332. /*
  333.  * Like fileAccess, but when the target is a directory.
  334.  * The implicit operation is X_OK and the directory handle
  335.  * returned is for the target, not the containing directory.
  336.  */
  337. SpoolDir*
  338. HylaFAXServer::dirAccess(const char* path)
  339. {
  340.     struct stat sb;
  341.     if (!FileCache::lookup(path, sb)) { // file not found
  342. perror_reply(550, path, errno);
  343. return (NULL);
  344.     }
  345.     if (!S_ISDIR(sb.st_mode)) {
  346. perror_reply(550, path, ENOTDIR);
  347. return (NULL);
  348.     }
  349.     // NB: this assumes all directories are top-level
  350.     SpoolDir* dir = dirLookup(sb.st_ino);
  351.     if (!dir) { // XXX should not happen
  352. perror_reply(550, path, ENOENT);
  353. return (NULL);
  354.     } else if (dir->adminOnly && !IS(PRIVILEGED)) { // requires admin priv's
  355. perror_reply(550, path, EPERM);
  356. return (NULL);
  357.     }
  358.     return (dir);
  359. }
  360. /*
  361.  * Force the specified file to have the GID setup as
  362.  * the effective GID of the process.  On System-V-style
  363.  * filesystems this happens automatically and there is
  364.  * nothing to do.  On BSD-style filesystems however the
  365.  * file's GID is set from the GID of the containing
  366.  * directory so we must explicitly set the value.  Note
  367.  * that we must auto-detect the appropriate semantics
  368.  * because some systems support both styles and the
  369.  * specific scheme is selectable on a per-filesystem
  370.  * basis (e.g. IRIX).
  371.  */
  372. void
  373. HylaFAXServer::setFileOwner(const char* file)
  374. {
  375.     if (IS(CHECKGID)) { // auto-detect how GID handled
  376.         struct stat sb;
  377.         if (!FileCache::lookup(file, sb)) {
  378.             fatal("setFileOwner called for non-existent file (check)");
  379.         }
  380.         if (sb.st_gid != (gid_t) uid) {
  381.             state |= S_SETGID; // not set, must force it
  382.         } else {
  383.             state &= ~S_SETGID; // set by OS, no work to do
  384.         }
  385.         if (TRACE(SERVER)) {
  386.             logDebug("Filesystem has %s-style file creation semantics.",
  387.                 IS(SETGID) ? "BSD" : "SysV");
  388.         }
  389.         state &= ~S_CHECKGID;
  390.     }
  391.     if (IS(SETGID)) {
  392.         struct stat sb;
  393.         if (!FileCache::lookup(file, sb)) {
  394.             fatal("setFileOwner called for non-existent file (set)");
  395.         }
  396.         if (!FileCache::chown(file, sb.st_uid, (gid_t) uid)) {
  397.             logError("%s: chown: %s", file, strerror(errno));
  398.         }
  399.     }
  400. }
  401. /*
  402.  * Is the specified file visible to the client.
  403.  */
  404. bool
  405. HylaFAXServer::fileVisible(const SpoolDir& dir, const char* filename, const struct stat& sb)
  406. {
  407.     return (IS(PRIVILEGED) || (this->*dir.isVisibleFile)(filename, sb));
  408. }
  409. bool
  410. HylaFAXServer::isVisibletrue(const char*, const struct stat&)
  411.     { return (true); }
  412. bool
  413. HylaFAXServer::isVisibleDocQFile(const char* filename, const struct stat&)
  414.     { return (strncmp(filename, "doc", 3) == 0 || strncmp(filename, "cover", 5) == 0 || strncmp(filename, FAX_SEQF, 4) == 0); }
  415. bool
  416. HylaFAXServer::isVisibleRootFile(const char*, const struct stat& sb)
  417.     { return (S_ISREG(sb.st_mode) || S_ISDIR(sb.st_mode)); }
  418. /*
  419.  * Change the working (pseudo) directory.
  420.  */
  421. void
  422. HylaFAXServer::cwdCmd(const char* path)
  423. {
  424.     SpoolDir* dir = dirAccess(path);
  425.     if (dir) {
  426. if (Sys::chdir(path) >= 0) {
  427.     ack(250, cmdToken(T_CWD));
  428.     cwd = dir;
  429. } else
  430.     perror_reply(550, path, errno);
  431.     }
  432. }
  433. /*
  434.  * Return the path of the current working (pseudo) directory.
  435.  */
  436. void
  437. HylaFAXServer::pwdCmd(void)
  438. {
  439.     u_int len = strlen(cwd->pathname)-1; // strip trailing "/"
  440.     reply(257, ""%.*s" is the current directory.",
  441. len ? len : len+1, cwd->pathname);
  442. }
  443. /*
  444.  * LIST a directory.
  445.  */
  446. void
  447. HylaFAXServer::listCmd(const char* pathname)
  448. {
  449.     SpoolDir* sd = dirAccess(pathname);
  450.     if (sd) {
  451. DIR* dir = opendir(pathname);
  452. if (dir != NULL) {
  453.     int code;
  454.     FILE* dout = openDataConn("w", code);
  455.     if (dout != NULL) {
  456. reply(code, "%s for "%s".", dataConnMsg(code), pathname);
  457. if (setjmp(urgcatch) == 0) {
  458.     state |= S_TRANSFER;
  459.     (this->*sd->listDirectory)(dout, *sd, dir);
  460.     fflush(dout);
  461.     reply(226, "Transfer complete.");
  462. }
  463. state &= ~S_TRANSFER;
  464. closeDataConn(dout);
  465.     }
  466.     closedir(dir);
  467. } else if (errno != 0)
  468.     perror_reply(550, pathname, errno);
  469. else
  470.     reply(550, "%s: Cannot open directory.", pathname);
  471.     }
  472. }
  473. void
  474. HylaFAXServer::listDirectory(FILE* fd, const SpoolDir& sd, DIR* dir)
  475. {
  476.     /*
  477.      * Use an absolute pathname when doing file
  478.      * lookups to improve cache locality.
  479.      */
  480.     fxStr path(sd.pathname);
  481.     fxStrArray files;
  482.     struct dirent* dp;
  483.     while ((dp = readdir(dir))) {
  484. files.append(dp->d_name);
  485.     }
  486.     files.qsort();
  487.     for (u_int i = 0, n = files.length(); i < n; i++) {
  488. if (files[i][0] == '.' &&
  489.    (((const char*)files[i])[1] == '' || strcmp(files[i], "..") == 0))
  490.     continue;
  491. struct stat sb;
  492. if (!FileCache::update(path | files[i], sb))
  493.     continue;
  494. if ((this->*sd.isVisibleFile)(files[i], sb)) {
  495.     (this->*sd.listFile)(fd, sd, files[i], sb);
  496.     fputs("rn", fd);
  497. }
  498.     }
  499. }
  500. void
  501. HylaFAXServer::listUnixFile(FILE* fd, const SpoolDir&,
  502.     const char* filename, const struct stat& sb)
  503. {
  504.     Fprintf(fd, fileFormat, filename, sb);
  505. }
  506. static const char fformat[] = {
  507.     's', // a (last access time)
  508.     'b', // b
  509.     's', // c (create time)
  510.     'o', // d (device)
  511.     'e', // e
  512.     's', // f (filename)
  513.     'u', // g (GID of file)
  514.     'h', // h
  515.     'u', // i (inode number)
  516.     'j', // j
  517.     'k', // k
  518.     'u', // l (link count)
  519.     's', // m (last modification time)
  520.     'n', // n
  521.     's', // o (owner based on file GID)
  522.     's', // p (fax-style protection flags, no group bits)
  523.     's', // q (UNIX-style protection flags)
  524.     'o', // r (root device)
  525.     'u', // s (file size in bytes)
  526.     't', // t
  527.     'u', // u (UID of file)
  528.     'v', // v
  529.     'w', // w
  530.     'x', // x
  531.     'y', // y
  532.     'z' // z
  533. };
  534. /*
  535.  * Print a formatted string with fields filled in from
  536.  * a file's stat buffer.  This functionality is
  537.  * used to permit clients to get modem status listings
  538.  * in preferred formats.
  539.  */
  540. void
  541. HylaFAXServer::Fprintf(FILE* fd, const char* fmt,
  542.     const char* filename, const struct stat& sb)
  543. {
  544.     for (const char* cp = fmt; *cp; cp++) {
  545. if (*cp == '%') {
  546. #define MAXSPEC 20
  547.     char fspec[MAXSPEC];
  548.     char* fp = fspec;
  549.     *fp++ = '%';
  550.     char c = *++cp;
  551.     if (c == '-')
  552. *fp++ = c, c = *++cp;
  553.     if (isdigit(c)) {
  554. do {
  555.     *fp++ = c;
  556. } while (isdigit(c = *++cp) && fp < &fspec[MAXSPEC-3]);
  557.     }
  558.     if (c == '.') {
  559. do {
  560.     *fp++ = c;
  561. } while (isdigit(c = *++cp) && fp < &fspec[MAXSPEC-2]);
  562.     }
  563.     if (!islower(c)) {
  564. if (c == '%') // %% -> %
  565.     putc(c, fd);
  566. else
  567.     fprintf(fd, "%.*s%c", fp-fspec, fspec, c);
  568. continue;
  569.     }
  570.     fp[0] = fformat[c-'a']; // printf format string
  571.     fp[1] = '';
  572.     switch (c) {
  573.     case 'a':
  574. fprintf(fd, fspec, asctime(cvtTime(sb.st_atime))+4);
  575. break;
  576.     case 'c':
  577. fprintf(fd, fspec, asctime(cvtTime(sb.st_ctime))+4);
  578. break;
  579.     case 'd':
  580. fprintf(fd, fspec, (u_int) sb.st_dev);
  581. break;
  582.     case 'f':
  583. fprintf(fd, fspec, filename);
  584. break;
  585.     case 'g':
  586. fprintf(fd, fspec, (u_int) sb.st_gid);
  587. break;
  588.     case 'i':
  589. fprintf(fd, fspec, (u_int) sb.st_ino); // XXX
  590. break;
  591.     case 'l':
  592. fprintf(fd, fspec, (u_int) sb.st_nlink);
  593. break;
  594.     case 'm':
  595. fprintf(fd, fspec, asctime(cvtTime(sb.st_mtime))+4);
  596. break;
  597.     case 'o':
  598. fprintf(fd, fspec, userName((u_int) sb.st_gid));
  599. break;
  600.     case 'p':
  601.     case 'q':
  602. { char prot[10]; // XXX HP C++
  603.   makeProt(sb, c == 'q', prot);
  604.   fprintf(fd, fspec, prot);
  605. }
  606. break;
  607.     case 'r':
  608. fprintf(fd, fspec, (u_int) sb.st_rdev);
  609. break;
  610.     case 's':
  611. fprintf(fd, fspec, (u_int) sb.st_size); // XXX
  612. break;
  613.     case 'u':
  614. fprintf(fd, fspec, (u_int) sb.st_uid);
  615. break;
  616.     }
  617. } else
  618.     putc(*cp, fd);
  619.     }
  620. }
  621. void
  622. HylaFAXServer::makeProt(const struct stat& sb, bool withGrp, char prot[10])
  623. {
  624.     char* pp = prot;
  625.     *pp++ = S_ISREG(sb.st_mode)  ? '-' :
  626.     S_ISDIR(sb.st_mode)  ? 'd' :
  627.     S_ISFIFO(sb.st_mode) ? 'p' :
  628. #ifdef S_ISSOCK
  629.     S_ISSOCK(sb.st_mode) ? 's' :
  630. #endif
  631.    '?' ;
  632.     *pp++ = (sb.st_mode&S_IRUSR) ? 'r' : '-';
  633.     *pp++ = (sb.st_mode&S_IWUSR) ? 'w' : '-';
  634.     *pp++ = (sb.st_mode&S_IXUSR) ? 'x' : '-';
  635.     if (withGrp) {
  636. *pp++ = (sb.st_mode&S_IRGRP) ? 'r' : '-';
  637. *pp++ = (sb.st_mode&S_IWGRP) ? 'w' : '-';
  638. *pp++ = (sb.st_mode&S_IXGRP) ? 'x' : '-';
  639.     }
  640.     *pp++ = (sb.st_mode&S_IROTH) ? 'r' : '-';
  641.     *pp++ = (sb.st_mode&S_IWOTH) ? 'w' : '-';
  642.     *pp++ = (sb.st_mode&S_IXOTH) ? 'x' : '-';
  643.     *pp++ = '';
  644. }
  645. void
  646. HylaFAXServer::statFileCmd(const char* pathname)
  647. {
  648.     struct stat sb;
  649.     SpoolDir* dir = fileAccess(pathname, X_OK, sb);
  650.     if (dir) {
  651. (void) FileCache::update(pathname, sb); // insure up to date info
  652. lreply(211, "Status of %s:", pathname);
  653. const char* cp = strrchr(pathname, '/');
  654. (this->*dir->listFile)(stdout, *dir, cp ? cp+1 : pathname, sb);
  655. fputs("rn", stdout);
  656. reply(211, "End of Status");
  657.     }
  658. }
  659. /*
  660.  * NLST a directory.
  661.  */
  662. void
  663. HylaFAXServer::nlstCmd(const char* pathname)
  664. {
  665.     SpoolDir* sd = dirAccess(pathname);
  666.     if (sd) {
  667. DIR* dir = opendir(pathname);
  668. if (dir != NULL) {
  669.     int code;
  670.     FILE* dout = openDataConn("w", code);
  671.     if (dout != NULL) {
  672. reply(code, "%s for "%s".", dataConnMsg(code), pathname);
  673. if (setjmp(urgcatch) == 0) {
  674.     state |= S_TRANSFER;
  675.     (this->*sd->nlstDirectory)(dout, *sd, dir);
  676.     fflush(dout);
  677.     reply(226, "Transfer complete.");
  678. }
  679. state &= ~S_TRANSFER;
  680. closeDataConn(dout);
  681.     }
  682.     closedir(dir);
  683. } else if (errno != 0)
  684.     perror_reply(550, pathname, errno);
  685. else
  686.     reply(550, "%s: Cannot open directory.", pathname);
  687.     }
  688. }
  689. void
  690. HylaFAXServer::nlstDirectory(FILE* fd, const SpoolDir& sd, DIR* dir)
  691. {
  692.     /*
  693.      * Use an absolute pathname when doing file
  694.      * lookups to improve cache locality.
  695.      */
  696.     fxStr path(sd.pathname);
  697.     fxStrArray files;
  698.     struct dirent* dp;
  699.     while ((dp = readdir(dir))) {
  700. files.append(dp->d_name);
  701.     }
  702.     files.qsort();
  703.     for (u_int i = 0, n = files.length(); i < n; i++) {
  704. if (files[i][0] == '.' &&
  705.    (((const char*)files[i])[1] == '' || strcmp(files[i], "..") == 0))
  706.     continue;
  707. struct stat sb;
  708. if (!FileCache::update(path | files[i], sb))
  709.     continue;
  710. if ((this->*sd.isVisibleFile)(files[i], sb)) {
  711.     (this->*sd.nlstFile)(fd, sd, files[i], sb);
  712.     fputs("rn", fd);
  713. }
  714.     }
  715. }
  716. void
  717. HylaFAXServer::nlstUnixFile(FILE* fd, const SpoolDir&,
  718.     const char* filename, const struct stat&)
  719. {
  720.     fprintf(fd, "%s", filename);
  721. }
  722. static bool
  723. isTIFF(const TIFFHeader& h)
  724. {
  725.     if (h.tiff_magic != TIFF_BIGENDIAN && h.tiff_magic != TIFF_LITTLEENDIAN)
  726. return (false);
  727.     union {
  728. int32 i;
  729. char c[4];
  730.     } u;
  731.     u.i = 1;
  732.     uint16 version = h.tiff_version;
  733.     // byte swap version stamp if opposite byte order
  734.     if ((u.c[0] == 0) ^ (h.tiff_magic == TIFF_BIGENDIAN))
  735. TIFFSwabShort(&version);
  736.     return (version == TIFF_VERSION);
  737. }
  738. /*
  739.  * This is used to identify a submitted document type when the client 
  740.  * specified the default (Postscript) or did not make a specification.
  741.  *
  742.  * There is no sure-fire way to type all PCL files.  So this is a best-
  743.  * guess approach on PCL.  It's best if the client specifies the type
  744.  * with PCL.
  745.  */
  746. bool
  747. HylaFAXServer::docType(const char* docname, FaxSendOp& op)
  748. {
  749.     op = FaxRequest::send_unknown;
  750.     int fd = Sys::open(docname, O_RDONLY);
  751.     if (fd >= 0) {
  752. struct stat sb;
  753. if (FileCache::lookup(docname, sb) && S_ISREG(sb.st_mode)) {
  754.     union {
  755. char buf[512];
  756. TIFFHeader h;
  757.     } b;
  758.     ssize_t cc = Sys::read(fd, (char*) &b, sizeof (b));
  759.     if (cc > 2 && b.buf[0] == '%' && b.buf[1] == '!')
  760. op = FaxRequest::send_postscript;
  761.     else if (cc > 2 && b.buf[0] == '%' && b.buf[1] == 'P') {
  762.      logDebug("What we have here is a PDF file");
  763.      op = FaxRequest::send_pdf;
  764.     } else if (cc > 2 && b.buf[0] == 0x1b && (b.buf[1] == 'E' || b.buf[1] == '%' || b.buf[1] == '&' || b.buf[1] == '*')) {
  765.      logDebug("What we have here is a PCL file");
  766.      op = FaxRequest::send_pcl;
  767.     } else if (cc > (ssize_t)sizeof (b.h) && isTIFF(b.h))
  768. op = FaxRequest::send_tiff;
  769.     else if (cc > 3 && b.buf[0] == '@' && b.buf[1] == 'P' && b.buf[2] == 'J' && b.buf[3] == 'L') {
  770.      logDebug("PJL is unsupported");
  771.      op = FaxRequest::send_unknown;
  772.     }
  773.     else
  774. op = FaxRequest::send_data;
  775. }
  776. Sys::close(fd);
  777.     }
  778.     if (op == FaxRequest::send_unknown)
  779.           logError("Don't know what file");
  780.     return (op != FaxRequest::send_unknown);   
  781. }