FileSystem.c++
上传用户:weiyuanprp
上传日期:2020-05-20
资源大小:1169k
文件大小:23k
- /* $Id: FileSystem.c++,v 1.11 2009/06/22 06:26:07 faxguy Exp $ */
- /*
- * Copyright (c) 1995-1996 Sam Leffler
- * Copyright (c) 1995-1996 Silicon Graphics, Inc.
- * HylaFAX is a trademark of Silicon Graphics
- *
- * Permission to use, copy, modify, distribute, and sell this software and
- * its documentation for any purpose is hereby granted without fee, provided
- * that (i) the above copyright notices and this permission notice appear in
- * all copies of the software and related documentation, and (ii) the names of
- * Sam Leffler and Silicon Graphics may not be used in any advertising or
- * publicity relating to the software without the specific, prior written
- * permission of Sam Leffler and Silicon Graphics.
- *
- * THE SOFTWARE IS PROVIDED "AS-IS" AND WITHOUT WARRANTY OF ANY KIND,
- * EXPRESS, IMPLIED OR OTHERWISE, INCLUDING WITHOUT LIMITATION, ANY
- * WARRANTY OF MERCHANTABILITY OR FITNESS FOR A PARTICULAR PURPOSE.
- *
- * IN NO EVENT SHALL SAM LEFFLER OR SILICON GRAPHICS BE LIABLE FOR
- * ANY SPECIAL, INCIDENTAL, INDIRECT OR CONSEQUENTIAL DAMAGES OF ANY KIND,
- * OR ANY DAMAGES WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS,
- * WHETHER OR NOT ADVISED OF THE POSSIBILITY OF DAMAGE, AND ON ANY THEORY OF
- * LIABILITY, ARISING OUT OF OR IN CONNECTION WITH THE USE OR PERFORMANCE
- * OF THIS SOFTWARE.
- */
- /*
- * File commands other than transfer.
- */
- #include "HylaFAXServer.h"
- #include "Sys.h"
- #include "tiffio.h"
- #include <limits.h>
- #include <ctype.h>
- /*
- * Delete a file (submitted document or received facsimile).
- */
- void
- HylaFAXServer::deleCmd(const char* pathname)
- {
- struct stat sb;
- SpoolDir* dir = fileAccess(pathname, W_OK, sb);
- if (dir) {
- if (!IS(PRIVILEGED)) {
- if (!S_ISREG(sb.st_mode)) {
- reply(550, "%s: not a plain file.", pathname);
- return;
- }
- if (!dir->deleAble) {
- perror_reply(550, pathname, EPERM);
- return;
- }
- }
- /*
- * XXX must decide what to do with jobs/documents/etc...
- * XXX e.g. should job be treated like jdele; should we
- * XXX cross-check document use for consistency....
- */
- if (Sys::unlink(pathname) < 0)
- perror_reply(550, pathname, errno);
- else {
- const char* cp = strrchr(pathname, '/');
- logNotice("%s of %s [%s] deleted %s%s"
- , (const char*) the_user
- , (const char*) remotehost
- , (const char*) remoteaddr
- , dir->pathname, cp ? cp+1 : pathname
- );
- ack(250, cmdToken(T_DELE));
- FileCache::flush(pathname);
- }
- }
- }
- /*
- * Set ownership of a file.
- */
- void
- HylaFAXServer::chownCmd(const char* pathname, const char* user)
- {
- struct stat sb;
- SpoolDir* dir = fileAccess(pathname, W_OK, sb);
- if (dir) {
- u_int id;
- if (isdigit(user[0]))
- id = (u_int) atoi(user);
- else if (!userID(user, id))
- return;
- if (!FileCache::chown(pathname, sb.st_uid, (gid_t) id))
- perror_reply(550, pathname, errno);
- else
- ack(250, cmdToken(T_CHOWN));
- }
- }
- /*
- * Set protection of a file.
- */
- void
- HylaFAXServer::chmodCmd(const char* pathname, u_int mode)
- {
- struct stat sb;
- SpoolDir* dir = fileAccess(pathname, W_OK, sb);
- if (dir) {
- mode = 0600 | (mode&066);
- if (!FileCache::chmod(pathname, mode))
- perror_reply(550, pathname, errno);
- else
- ack(250, cmdToken(T_CHMOD));
- }
- }
- /*
- * Return the last modification time for a file.
- */
- void
- HylaFAXServer::mdtmCmd(const char* pathname)
- {
- struct stat sb;
- SpoolDir* dir = fileAccess(pathname, X_OK, sb);
- if (dir) {
- struct tm* t = cvtTime(sb.st_mtime);
- reply(213, "%d%02d%02d%02d%02d%02d"
- , t->tm_year+1900
- , t->tm_mon+1
- , t->tm_mday
- , t->tm_hour
- , t->tm_min
- , t->tm_sec
- );
- }
- }
- /*
- * NB: this array is ordered by expected frequency of access.
- */
- SpoolDir HylaFAXServer::dirs[] = {
- { "/status/", false, false, false, 0,
- &HylaFAXServer::isVisibletrue,
- &HylaFAXServer::listStatus, &HylaFAXServer::listStatusFile,
- &HylaFAXServer::nlstStatus, &HylaFAXServer::nlstUnixFile, },
- { "/sendq/", false, false, false, 0,
- &HylaFAXServer::isVisibleSendQFile,
- &HylaFAXServer::listSendQ, &HylaFAXServer::listSendQFile,
- &HylaFAXServer::nlstSendQ, &HylaFAXServer::nlstSendQFile, },
- { "/doneq/", false, false, false, 0,
- &HylaFAXServer::isVisibleSendQFile,
- &HylaFAXServer::listSendQ, &HylaFAXServer::listSendQFile,
- &HylaFAXServer::nlstSendQ, &HylaFAXServer::nlstSendQFile, },
- { "/docq/", false, true, true, 0,
- &HylaFAXServer::isVisibleDocQFile,
- &HylaFAXServer::listDirectory, &HylaFAXServer::listUnixFile,
- &HylaFAXServer::nlstDirectory, &HylaFAXServer::nlstUnixFile, },
- { "/tmp/", false, true, true, 0,
- &HylaFAXServer::isVisibletrue,
- &HylaFAXServer::listDirectory, &HylaFAXServer::listUnixFile,
- &HylaFAXServer::nlstDirectory, &HylaFAXServer::nlstUnixFile, },
- { "/log/", false, false, false, 0,
- &HylaFAXServer::isVisibletrue,
- &HylaFAXServer::listDirectory, &HylaFAXServer::listUnixFile,
- &HylaFAXServer::nlstDirectory, &HylaFAXServer::nlstUnixFile, },
- { "/recvq/", false, false, true, 0,
- &HylaFAXServer::isVisibleRecvQFile,
- &HylaFAXServer::listRecvQ, &HylaFAXServer::listRecvQFile,
- &HylaFAXServer::nlstDirectory, &HylaFAXServer::nlstUnixFile, },
- { "/archive/", false, false, false, 0,
- &HylaFAXServer::isVisibletrue,
- &HylaFAXServer::listDirectory, &HylaFAXServer::listUnixFile,
- &HylaFAXServer::nlstDirectory, &HylaFAXServer::nlstUnixFile, },
- { "/pollq/", false, true, true, 0,
- &HylaFAXServer::isVisibleRecvQFile,
- &HylaFAXServer::listRecvQ, &HylaFAXServer::listRecvQFile,
- &HylaFAXServer::nlstDirectory, &HylaFAXServer::nlstUnixFile, },
- { "/", false, false, false, 0,
- &HylaFAXServer::isVisibleRootFile,
- &HylaFAXServer::listDirectory, &HylaFAXServer::listUnixFile,
- &HylaFAXServer::nlstDirectory, &HylaFAXServer::nlstUnixFile, },
- { "/etc/", true, false, false, 0,
- &HylaFAXServer::isVisibletrue,
- &HylaFAXServer::listDirectory, &HylaFAXServer::listUnixFile,
- &HylaFAXServer::nlstDirectory, &HylaFAXServer::nlstUnixFile, },
- { "/info/", false, false, false, 0,
- &HylaFAXServer::isVisibletrue,
- &HylaFAXServer::listDirectory, &HylaFAXServer::listUnixFile,
- &HylaFAXServer::nlstDirectory, &HylaFAXServer::nlstUnixFile, },
- { "/bin/", true, false, false, 0,
- &HylaFAXServer::isVisibletrue,
- &HylaFAXServer::listDirectory, &HylaFAXServer::listUnixFile,
- &HylaFAXServer::nlstDirectory, &HylaFAXServer::nlstUnixFile, },
- { "/config/", false, false, false, 0,
- &HylaFAXServer::isVisibletrue,
- &HylaFAXServer::listDirectory, &HylaFAXServer::listUnixFile,
- &HylaFAXServer::nlstDirectory, &HylaFAXServer::nlstUnixFile, },
- { "/client/", true, false, false, 0,
- &HylaFAXServer::isVisibletrue,
- &HylaFAXServer::listDirectory, &HylaFAXServer::listUnixFile,
- &HylaFAXServer::nlstDirectory, &HylaFAXServer::nlstUnixFile, },
- };
- #define N(a) (sizeof (a) / sizeof (a[0]))
- /*
- * Initialize the directory handling.
- */
- void
- HylaFAXServer::dirSetup(void)
- {
- for (u_int i = 0, n = N(dirs); i < n; i++) {
- struct stat sb;
- if (!FileCache::lookup(dirs[i].pathname, sb) || !S_ISDIR(sb.st_mode)) {
- logError("%s: Not a directory.", dirs[i].pathname);
- continue;
- }
- dirs[i].ino = sb.st_ino;
- if (streq(dirs[i].pathname, "/"))
- cwd = &dirs[i];
- }
- }
- /*
- * Return a directory handle given a pathname.
- */
- SpoolDir*
- HylaFAXServer::dirLookup(const char* path)
- {
- struct stat sb;
- return (!FileCache::lookup(path, sb) || !S_ISDIR(sb.st_mode) ?
- (SpoolDir*) NULL : dirLookup(sb.st_ino));
- }
- /*
- * Return a directory handle given an inode number.
- */
- SpoolDir*
- HylaFAXServer::dirLookup(ino_t ino)
- {
- for (u_int i = 0, n = N(dirs); i < n; i++)
- if (dirs[i].ino == ino)
- return (&dirs[i]);
- return (NULL);
- }
- /*
- * Check file protection mode; we use the
- * normal ``other'' bits for public access
- * and the group bits for the ``fax uid''.
- */
- bool
- HylaFAXServer::checkFileRights(int op, const struct stat& sb)
- {
- if (IS(PRIVILEGED))
- return (true);
- if (sb.st_mode & op) // public access
- return (true);
- if ((sb.st_gid==uid) && ((sb.st_mode>>3)&op)) // owner access
- return (true);
- return false;
- }
- /*
- * Check if the client is permitted to do the
- * request operation on the specified file.
- * Operations are: X_OK (list/stat), R_OK (look
- * at the contents), W_OK (write/delete contents).
- *
- * If the operation is permitted the stat result
- * for the target file is returned to the caller
- * for use in futher checks (e.g. type of file).
- */
- SpoolDir*
- HylaFAXServer::fileAccess(const char* path, int op, struct stat& sb)
- {
- if (!FileCache::lookup(path, sb)) { // file not found
- if (op != W_OK || errno != ENOENT) {
- perror_reply(550, path, errno);
- return (NULL);
- }
- /*
- * The file does not exist, fake up the
- * information that would result from a
- * successful file creation--for use below.
- */
- sb.st_mode = S_IFREG|S_IRGRP|S_IWGRP;
- sb.st_gid = (gid_t) uid;
- sb.st_ino = 0; // NB: to be created
- }
- /*
- * Validate containing directory and target file.
- * The directory must exist and the client must
- * have permission to access it. The target file
- * must not be a directory if the client is about
- * to do a read/write-style operation or the target
- * file must be owned by the client (other checks
- * are left to the caller).
- */
- struct stat db;
- const char* cp = strrchr(path, '/');
- if (cp) {
- cp++; // include "/"
- if (!FileCache::lookup(fxStr(path, cp-path), db)) {
- perror_reply(550, path, ENOENT);
- return (NULL);
- }
- } else {
- if (!FileCache::lookup(cwd->pathname, db)) { // implicit ref to "."
- perror_reply(550, path, ENOENT);
- return (NULL);
- }
- cp = path;
- }
- SpoolDir* dir = dirLookup(db.st_ino);
- if (!dir) { // no containing dir
- perror_reply(550, path, ENOENT);
- return (NULL);
- } else if (!IS(PRIVILEGED)) { // unprivileged client
- if (dir->adminOnly) { // requires admin priv's
- perror_reply(550, path, EPERM);
- return (NULL);
- } else if (!fileVisible(*dir, cp, sb)) { // not visible w/o admin
- perror_reply(550, path, EPERM);
- return (NULL);
- } else if (op != X_OK) { // client wants to r/w
- if (S_ISDIR(sb.st_mode)) { // cannot r/w directory
- reply(550, "%s: not a plain file.", path);
- return (NULL);
- } else if (sb.st_ino == 0 && !dir->storAble) {
- perror_reply(550, path, EPERM); // cannot stor in dir.
- return (NULL);
- }
- if (!checkFileRights(op, sb)) {
- perror_reply(550, path, EPERM);
- return (NULL);
- }
- }
- } else { // privileged client
- if (op != X_OK && S_ISDIR(sb.st_mode)) { // cannot r/w directory
- reply(550, "%s: not a plain file.", path);
- return (NULL);
- }
- }
- return (dir);
- }
- /*
- * Like fileAccess, but when the target is a directory.
- * The implicit operation is X_OK and the directory handle
- * returned is for the target, not the containing directory.
- */
- SpoolDir*
- HylaFAXServer::dirAccess(const char* path)
- {
- struct stat sb;
- if (!FileCache::lookup(path, sb)) { // file not found
- perror_reply(550, path, errno);
- return (NULL);
- }
- if (!S_ISDIR(sb.st_mode)) {
- perror_reply(550, path, ENOTDIR);
- return (NULL);
- }
- // NB: this assumes all directories are top-level
- SpoolDir* dir = dirLookup(sb.st_ino);
- if (!dir) { // XXX should not happen
- perror_reply(550, path, ENOENT);
- return (NULL);
- } else if (dir->adminOnly && !IS(PRIVILEGED)) { // requires admin priv's
- perror_reply(550, path, EPERM);
- return (NULL);
- }
- return (dir);
- }
- /*
- * Force the specified file to have the GID setup as
- * the effective GID of the process. On System-V-style
- * filesystems this happens automatically and there is
- * nothing to do. On BSD-style filesystems however the
- * file's GID is set from the GID of the containing
- * directory so we must explicitly set the value. Note
- * that we must auto-detect the appropriate semantics
- * because some systems support both styles and the
- * specific scheme is selectable on a per-filesystem
- * basis (e.g. IRIX).
- */
- void
- HylaFAXServer::setFileOwner(const char* file)
- {
- if (IS(CHECKGID)) { // auto-detect how GID handled
- struct stat sb;
- if (!FileCache::lookup(file, sb)) {
- fatal("setFileOwner called for non-existent file (check)");
- }
- if (sb.st_gid != (gid_t) uid) {
- state |= S_SETGID; // not set, must force it
- } else {
- state &= ~S_SETGID; // set by OS, no work to do
- }
- if (TRACE(SERVER)) {
- logDebug("Filesystem has %s-style file creation semantics.",
- IS(SETGID) ? "BSD" : "SysV");
- }
- state &= ~S_CHECKGID;
- }
- if (IS(SETGID)) {
- struct stat sb;
- if (!FileCache::lookup(file, sb)) {
- fatal("setFileOwner called for non-existent file (set)");
- }
- if (!FileCache::chown(file, sb.st_uid, (gid_t) uid)) {
- logError("%s: chown: %s", file, strerror(errno));
- }
- }
- }
- /*
- * Is the specified file visible to the client.
- */
- bool
- HylaFAXServer::fileVisible(const SpoolDir& dir, const char* filename, const struct stat& sb)
- {
- return (IS(PRIVILEGED) || (this->*dir.isVisibleFile)(filename, sb));
- }
- bool
- HylaFAXServer::isVisibletrue(const char*, const struct stat&)
- { return (true); }
- bool
- HylaFAXServer::isVisibleDocQFile(const char* filename, const struct stat&)
- { return (strncmp(filename, "doc", 3) == 0 || strncmp(filename, "cover", 5) == 0 || strncmp(filename, FAX_SEQF, 4) == 0); }
- bool
- HylaFAXServer::isVisibleRootFile(const char*, const struct stat& sb)
- { return (S_ISREG(sb.st_mode) || S_ISDIR(sb.st_mode)); }
- /*
- * Change the working (pseudo) directory.
- */
- void
- HylaFAXServer::cwdCmd(const char* path)
- {
- SpoolDir* dir = dirAccess(path);
- if (dir) {
- if (Sys::chdir(path) >= 0) {
- ack(250, cmdToken(T_CWD));
- cwd = dir;
- } else
- perror_reply(550, path, errno);
- }
- }
- /*
- * Return the path of the current working (pseudo) directory.
- */
- void
- HylaFAXServer::pwdCmd(void)
- {
- u_int len = strlen(cwd->pathname)-1; // strip trailing "/"
- reply(257, ""%.*s" is the current directory.",
- len ? len : len+1, cwd->pathname);
- }
- /*
- * LIST a directory.
- */
- void
- HylaFAXServer::listCmd(const char* pathname)
- {
- SpoolDir* sd = dirAccess(pathname);
- if (sd) {
- DIR* dir = opendir(pathname);
- if (dir != NULL) {
- int code;
- FILE* dout = openDataConn("w", code);
- if (dout != NULL) {
- reply(code, "%s for "%s".", dataConnMsg(code), pathname);
- if (setjmp(urgcatch) == 0) {
- state |= S_TRANSFER;
- (this->*sd->listDirectory)(dout, *sd, dir);
- fflush(dout);
- reply(226, "Transfer complete.");
- }
- state &= ~S_TRANSFER;
- closeDataConn(dout);
- }
- closedir(dir);
- } else if (errno != 0)
- perror_reply(550, pathname, errno);
- else
- reply(550, "%s: Cannot open directory.", pathname);
- }
- }
- void
- HylaFAXServer::listDirectory(FILE* fd, const SpoolDir& sd, DIR* dir)
- {
- /*
- * Use an absolute pathname when doing file
- * lookups to improve cache locality.
- */
- fxStr path(sd.pathname);
- fxStrArray files;
- struct dirent* dp;
- while ((dp = readdir(dir))) {
- files.append(dp->d_name);
- }
- files.qsort();
- for (u_int i = 0, n = files.length(); i < n; i++) {
- if (files[i][0] == '.' &&
- (((const char*)files[i])[1] == ' ' || strcmp(files[i], "..") == 0))
- continue;
- struct stat sb;
- if (!FileCache::update(path | files[i], sb))
- continue;
- if ((this->*sd.isVisibleFile)(files[i], sb)) {
- (this->*sd.listFile)(fd, sd, files[i], sb);
- fputs("rn", fd);
- }
- }
- }
- void
- HylaFAXServer::listUnixFile(FILE* fd, const SpoolDir&,
- const char* filename, const struct stat& sb)
- {
- Fprintf(fd, fileFormat, filename, sb);
- }
- static const char fformat[] = {
- 's', // a (last access time)
- 'b', // b
- 's', // c (create time)
- 'o', // d (device)
- 'e', // e
- 's', // f (filename)
- 'u', // g (GID of file)
- 'h', // h
- 'u', // i (inode number)
- 'j', // j
- 'k', // k
- 'u', // l (link count)
- 's', // m (last modification time)
- 'n', // n
- 's', // o (owner based on file GID)
- 's', // p (fax-style protection flags, no group bits)
- 's', // q (UNIX-style protection flags)
- 'o', // r (root device)
- 'u', // s (file size in bytes)
- 't', // t
- 'u', // u (UID of file)
- 'v', // v
- 'w', // w
- 'x', // x
- 'y', // y
- 'z' // z
- };
- /*
- * Print a formatted string with fields filled in from
- * a file's stat buffer. This functionality is
- * used to permit clients to get modem status listings
- * in preferred formats.
- */
- void
- HylaFAXServer::Fprintf(FILE* fd, const char* fmt,
- const char* filename, const struct stat& sb)
- {
- for (const char* cp = fmt; *cp; cp++) {
- if (*cp == '%') {
- #define MAXSPEC 20
- char fspec[MAXSPEC];
- char* fp = fspec;
- *fp++ = '%';
- char c = *++cp;
- if (c == '-')
- *fp++ = c, c = *++cp;
- if (isdigit(c)) {
- do {
- *fp++ = c;
- } while (isdigit(c = *++cp) && fp < &fspec[MAXSPEC-3]);
- }
- if (c == '.') {
- do {
- *fp++ = c;
- } while (isdigit(c = *++cp) && fp < &fspec[MAXSPEC-2]);
- }
- if (!islower(c)) {
- if (c == '%') // %% -> %
- putc(c, fd);
- else
- fprintf(fd, "%.*s%c", fp-fspec, fspec, c);
- continue;
- }
- fp[0] = fformat[c-'a']; // printf format string
- fp[1] = ' ';
- switch (c) {
- case 'a':
- fprintf(fd, fspec, asctime(cvtTime(sb.st_atime))+4);
- break;
- case 'c':
- fprintf(fd, fspec, asctime(cvtTime(sb.st_ctime))+4);
- break;
- case 'd':
- fprintf(fd, fspec, (u_int) sb.st_dev);
- break;
- case 'f':
- fprintf(fd, fspec, filename);
- break;
- case 'g':
- fprintf(fd, fspec, (u_int) sb.st_gid);
- break;
- case 'i':
- fprintf(fd, fspec, (u_int) sb.st_ino); // XXX
- break;
- case 'l':
- fprintf(fd, fspec, (u_int) sb.st_nlink);
- break;
- case 'm':
- fprintf(fd, fspec, asctime(cvtTime(sb.st_mtime))+4);
- break;
- case 'o':
- fprintf(fd, fspec, userName((u_int) sb.st_gid));
- break;
- case 'p':
- case 'q':
- { char prot[10]; // XXX HP C++
- makeProt(sb, c == 'q', prot);
- fprintf(fd, fspec, prot);
- }
- break;
- case 'r':
- fprintf(fd, fspec, (u_int) sb.st_rdev);
- break;
- case 's':
- fprintf(fd, fspec, (u_int) sb.st_size); // XXX
- break;
- case 'u':
- fprintf(fd, fspec, (u_int) sb.st_uid);
- break;
- }
- } else
- putc(*cp, fd);
- }
- }
- void
- HylaFAXServer::makeProt(const struct stat& sb, bool withGrp, char prot[10])
- {
- char* pp = prot;
- *pp++ = S_ISREG(sb.st_mode) ? '-' :
- S_ISDIR(sb.st_mode) ? 'd' :
- S_ISFIFO(sb.st_mode) ? 'p' :
- #ifdef S_ISSOCK
- S_ISSOCK(sb.st_mode) ? 's' :
- #endif
- '?' ;
- *pp++ = (sb.st_mode&S_IRUSR) ? 'r' : '-';
- *pp++ = (sb.st_mode&S_IWUSR) ? 'w' : '-';
- *pp++ = (sb.st_mode&S_IXUSR) ? 'x' : '-';
- if (withGrp) {
- *pp++ = (sb.st_mode&S_IRGRP) ? 'r' : '-';
- *pp++ = (sb.st_mode&S_IWGRP) ? 'w' : '-';
- *pp++ = (sb.st_mode&S_IXGRP) ? 'x' : '-';
- }
- *pp++ = (sb.st_mode&S_IROTH) ? 'r' : '-';
- *pp++ = (sb.st_mode&S_IWOTH) ? 'w' : '-';
- *pp++ = (sb.st_mode&S_IXOTH) ? 'x' : '-';
- *pp++ = ' ';
- }
- void
- HylaFAXServer::statFileCmd(const char* pathname)
- {
- struct stat sb;
- SpoolDir* dir = fileAccess(pathname, X_OK, sb);
- if (dir) {
- (void) FileCache::update(pathname, sb); // insure up to date info
- lreply(211, "Status of %s:", pathname);
- const char* cp = strrchr(pathname, '/');
- (this->*dir->listFile)(stdout, *dir, cp ? cp+1 : pathname, sb);
- fputs("rn", stdout);
- reply(211, "End of Status");
- }
- }
- /*
- * NLST a directory.
- */
- void
- HylaFAXServer::nlstCmd(const char* pathname)
- {
- SpoolDir* sd = dirAccess(pathname);
- if (sd) {
- DIR* dir = opendir(pathname);
- if (dir != NULL) {
- int code;
- FILE* dout = openDataConn("w", code);
- if (dout != NULL) {
- reply(code, "%s for "%s".", dataConnMsg(code), pathname);
- if (setjmp(urgcatch) == 0) {
- state |= S_TRANSFER;
- (this->*sd->nlstDirectory)(dout, *sd, dir);
- fflush(dout);
- reply(226, "Transfer complete.");
- }
- state &= ~S_TRANSFER;
- closeDataConn(dout);
- }
- closedir(dir);
- } else if (errno != 0)
- perror_reply(550, pathname, errno);
- else
- reply(550, "%s: Cannot open directory.", pathname);
- }
- }
- void
- HylaFAXServer::nlstDirectory(FILE* fd, const SpoolDir& sd, DIR* dir)
- {
- /*
- * Use an absolute pathname when doing file
- * lookups to improve cache locality.
- */
- fxStr path(sd.pathname);
- fxStrArray files;
- struct dirent* dp;
- while ((dp = readdir(dir))) {
- files.append(dp->d_name);
- }
- files.qsort();
- for (u_int i = 0, n = files.length(); i < n; i++) {
- if (files[i][0] == '.' &&
- (((const char*)files[i])[1] == ' ' || strcmp(files[i], "..") == 0))
- continue;
- struct stat sb;
- if (!FileCache::update(path | files[i], sb))
- continue;
- if ((this->*sd.isVisibleFile)(files[i], sb)) {
- (this->*sd.nlstFile)(fd, sd, files[i], sb);
- fputs("rn", fd);
- }
- }
- }
- void
- HylaFAXServer::nlstUnixFile(FILE* fd, const SpoolDir&,
- const char* filename, const struct stat&)
- {
- fprintf(fd, "%s", filename);
- }
- static bool
- isTIFF(const TIFFHeader& h)
- {
- if (h.tiff_magic != TIFF_BIGENDIAN && h.tiff_magic != TIFF_LITTLEENDIAN)
- return (false);
- union {
- int32 i;
- char c[4];
- } u;
- u.i = 1;
- uint16 version = h.tiff_version;
- // byte swap version stamp if opposite byte order
- if ((u.c[0] == 0) ^ (h.tiff_magic == TIFF_BIGENDIAN))
- TIFFSwabShort(&version);
- return (version == TIFF_VERSION);
- }
- /*
- * This is used to identify a submitted document type when the client
- * specified the default (Postscript) or did not make a specification.
- *
- * There is no sure-fire way to type all PCL files. So this is a best-
- * guess approach on PCL. It's best if the client specifies the type
- * with PCL.
- */
- bool
- HylaFAXServer::docType(const char* docname, FaxSendOp& op)
- {
- op = FaxRequest::send_unknown;
- int fd = Sys::open(docname, O_RDONLY);
- if (fd >= 0) {
- struct stat sb;
- if (FileCache::lookup(docname, sb) && S_ISREG(sb.st_mode)) {
- union {
- char buf[512];
- TIFFHeader h;
- } b;
- ssize_t cc = Sys::read(fd, (char*) &b, sizeof (b));
- if (cc > 2 && b.buf[0] == '%' && b.buf[1] == '!')
- op = FaxRequest::send_postscript;
- else if (cc > 2 && b.buf[0] == '%' && b.buf[1] == 'P') {
- logDebug("What we have here is a PDF file");
- op = FaxRequest::send_pdf;
- } else if (cc > 2 && b.buf[0] == 0x1b && (b.buf[1] == 'E' || b.buf[1] == '%' || b.buf[1] == '&' || b.buf[1] == '*')) {
- logDebug("What we have here is a PCL file");
- op = FaxRequest::send_pcl;
- } else if (cc > (ssize_t)sizeof (b.h) && isTIFF(b.h))
- op = FaxRequest::send_tiff;
- else if (cc > 3 && b.buf[0] == '@' && b.buf[1] == 'P' && b.buf[2] == 'J' && b.buf[3] == 'L') {
- logDebug("PJL is unsupported");
- op = FaxRequest::send_unknown;
- }
- else
- op = FaxRequest::send_data;
- }
- Sys::close(fd);
- }
- if (op == FaxRequest::send_unknown)
- logError("Don't know what file");
- return (op != FaxRequest::send_unknown);
- }