📄 filetransfer.c++
字号:
/* $Id: FileTransfer.c++,v 1.14 2006/04/23 20:17:52 darren 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 transfer commands. */#include "HylaFAXServer.h"#include "Sys.h"#include "config.h"#include "zlib.h"#include "tiffio.h"#include <ctype.h>#include <limits.h>#ifdef HAVE_STDINT_H#include <stdint.h>#endif#ifndef CHAR_BIT#ifdef NBBY#define CHAR_BIT NBBY#else#define CHAR_BIT 8#endif#endif /* CHAR_BIT */#define HAVE_PSLEVEL2 false#define HAVE_PCL5 falsestatic struct { const char* name; // protocol token name bool supported; // true if format is supported const char* suffix; // file suffix const char* help; // help string for HELP FORM command} formats[] = {{ "TIFF", true, "tif", "Tagged Image File Format, Class F only" },{ "PS", true, "ps", "Adobe PostScript Level I" },{ "PS2", HAVE_PSLEVEL2, "ps", "Adobe PostScript Level II" },{ "PCL", HAVE_PCL5, "pcl", "HP Printer Control Language (PCL), Version 5"},{ "PDF", true, "pdf", "Adobe Portable Document Format" },};static const char* typenames[] = { "ASCII", "EBCDIC", "Image", "Local" };static const char* strunames[] = { "File", "Record", "Page", "TIFF" };static const char* modenames[] = { "Stream", "Block", "Compressed", "ZIP" };#define N(a) (sizeof (a) / sizeof (a[0]))/* * Record a file transfer in the log file. */voidHylaFAXServer::logTransfer(const char* direction, const SpoolDir& sd, const char* pathname, time_t start){ time_t now = Sys::now(); time_t xferfaxtime = now - start; if (xferfaxtime == 0) xferfaxtime++; const char* filename = strrchr(pathname, '/'); fxStr msg(fxStr::format("%.24s\t%lu\t%s\t%lu\t%s/%s\t%s\t%s\n" , ctime(&now) , (u_long) xferfaxtime , (const char*) remotehost , (u_long) byte_count , sd.pathname, filename ? filename+1 : pathname , direction , (const char*) the_user )); (void) Sys::write(xferfaxlog, msg, msg.length());}boolHylaFAXServer::restartSend(FILE* fd, off_t marker){ if (type == TYPE_A) { // restart based on line count int c; while ((c = getc(fd)) != EOF) if (c == '\n' && --marker == 0) return (true); return (false); } else // restart based on file offset return (lseek(fileno(fd), marker, SEEK_SET) == marker);}/* * RETRieve a file. */voidHylaFAXServer::retrieveCmd(const char* name){ struct stat sb; SpoolDir* sd = fileAccess(name, R_OK, sb); if (sd) { FILE* fd = fopen(name, "r"); if (fd != NULL) { if (restart_point && !restartSend(fd, restart_point)) { perror_reply(550, name, errno); } else { time_t start_time = Sys::now(); int code; FILE* dout = openDataConn("w", code); if (dout != NULL) { file_size = sb.st_size; reply(code, "%s for %s (%lu bytes).", dataConnMsg(code), name, (u_long) file_size); if (sendData(fd, dout)) reply(226, "Transfer complete."); if (TRACE(OUTXFERS) && xferfaxlog != -1) logTransfer("o", *sd, name, start_time); closeDataConn(dout); } } fclose(fd); } else if (errno != 0) perror_reply(550, name, errno); else reply(550, "%s: Cannot open file.", name); }}/* * TIFF Directory Template used in returning * a single IFD/image from a TIFF file. */typedef struct { TIFFDirEntry SubFileType; TIFFDirEntry ImageWidth; TIFFDirEntry ImageLength; TIFFDirEntry BitsPerSample; TIFFDirEntry Compression; TIFFDirEntry Photometric; TIFFDirEntry FillOrder; TIFFDirEntry StripOffsets; TIFFDirEntry Orientation; TIFFDirEntry SamplesPerPixel; TIFFDirEntry RowsPerStrip; TIFFDirEntry StripByteCounts; TIFFDirEntry XResolution; TIFFDirEntry YResolution; TIFFDirEntry Options; // T4 or T6 TIFFDirEntry ResolutionUnit; TIFFDirEntry PageNumber; TIFFDirEntry BadFaxLines; TIFFDirEntry CleanFaxData; TIFFDirEntry ConsecutiveBadFaxLines; uint32 link; // offset to next directory uint32 xres[2]; // X resolution indirect value uint32 yres[2]; // Y resolution indirect value} DirTemplate;/* * RETrieve one Page from a file. For now the * file must be a TIFF image; we might try to * handle PostScript at a later time (but don't * hold your breath as there's not much reason). */voidHylaFAXServer::retrievePageCmd(const char* name){ TIFF* tif = cachedTIFF; if (tif != NULL && streq(name, TIFFFileName(tif))) { /* * Reuse the cached open file. If no directory * has been specified with a REST command then * return the next consecutive directory in the file. */ if (restart_point == 0) // advance to next directory restart_point = TIFFCurrentDirectory(tif)+1; } else { if (tif) // close cached handle TIFFClose(tif), cachedTIFF = NULL; tif = openTIFF(name); } if (tif != NULL) { if (restart_point && !TIFFSetDirectory(tif, (tdir_t) restart_point)) { reply(550, "%s: Unable to access directory %lu.", name, (u_long) restart_point); } else { time_t start_time = Sys::now(); int code; FILE* dout = openDataConn("w", code); if (dout != NULL) { /* * Calculate "file size" by totalling up the * amount of image data and then adding in * the expected data for the TIFF headers. */ uint32* sb; TIFFGetField(tif, TIFFTAG_STRIPBYTECOUNTS, &sb); file_size = sizeof (DirTemplate) + sizeof (TIFFHeader) + sizeof (uint16); for (tstrip_t s = 0, ns = TIFFNumberOfStrips(tif); s < ns; s++) file_size += sb[s]; reply(code, "%s for %s (%lu bytes).", dataConnMsg(code), name, (u_long) file_size); if (sendTIFFData(tif, dout)) reply(226, "Transfer complete."); if (TRACE(OUTXFERS) && xferfaxlog != -1) { struct stat sb; SpoolDir* sd = fileAccess(name, R_OK, sb); logTransfer("o", *sd, name, start_time); } closeDataConn(dout); } } cachedTIFF = tif; }}/* * Open a file that is expected to hold a TIFF image. */TIFF*HylaFAXServer::openTIFF(const char* name){ struct stat sb; SpoolDir* sd = fileAccess(name, R_OK, sb); if (sd) { int fd = Sys::open(name, O_RDONLY); if (fd >= 0) { union { char buf[512]; TIFFHeader h; } b; ssize_t cc = Sys::read(fd, (char*) &b, sizeof (b)); if (cc > (ssize_t)sizeof (b.h) && b.h.tiff_version == TIFF_VERSION && (b.h.tiff_magic == TIFF_BIGENDIAN || b.h.tiff_magic == TIFF_LITTLEENDIAN)) { (void) lseek(fd, 0L, SEEK_SET); // rewind TIFF* tif = TIFFFdOpen(fd, name, "r"); if (tif != NULL) return (tif); else reply(550, "%s: Incomplete or invalid TIFF file.", name); } else reply(550, "%s: Not a TIFF file.", name); Sys::close(fd); } else if (errno != 0) perror_reply(550, name, errno); else reply(550, "%s: Cannot open file.", name); } return (NULL);}/* * Tranfer the current directory's contents of "tif" to "fdout". */boolHylaFAXServer::sendTIFFData(TIFF* tif, FILE* fdout){ state |= S_TRANSFER; if (setjmp(urgcatch) != 0) { state &= ~S_TRANSFER; return (false); }#define PACK(a,b) (((a)<<8)|(b)) switch (PACK(type,mode)) { case PACK(TYPE_I,MODE_S): case PACK(TYPE_L,MODE_S): if (sendTIFFHeader(tif, fileno(fdout)) && sendITIFFData(tif, fileno(fdout))) { state &= ~S_TRANSFER; return (true); } break; default: reply(550, "TYPE %s, MODE %s not implemented." , typenames[type] , modenames[mode] ); break; }#undef PACK state &= ~S_TRANSFER; return (false);}static voidgetLong(TIFF* tif, TIFFDirEntry& de){ TIFFGetField(tif, de.tdir_tag, &de.tdir_offset);}static voidgetShort(TIFF* tif, TIFFDirEntry& de){ uint16 v; TIFFGetField(tif, de.tdir_tag, &v); de.tdir_offset = (uint32) v;}/* * Send a TIFF header and IFD for the current directory * in the open TIFF file. The image data is expected to * immediately follow this information (i.e. the value of * the StripByteOffsets tag is setup to point to the offset * immediately after this data) and it is assumed that * all image data is concatenated into a single strip. */boolHylaFAXServer::sendTIFFHeader(TIFF* tif, int fdout){ static DirTemplate templ = {#define TIFFdiroff(v) \ (uint32) (sizeof (TIFFHeader) + sizeof (uint16) + \ (intptr_t) &(((DirTemplate*) 0)->v)) { TIFFTAG_SUBFILETYPE, TIFF_LONG, 1 }, { TIFFTAG_IMAGEWIDTH, TIFF_LONG, 1 }, { TIFFTAG_IMAGELENGTH, TIFF_LONG, 1 }, { TIFFTAG_BITSPERSAMPLE, TIFF_SHORT, 1, 1 }, { TIFFTAG_COMPRESSION, TIFF_SHORT, 1 }, { TIFFTAG_PHOTOMETRIC, TIFF_SHORT, 1 }, { TIFFTAG_FILLORDER, TIFF_SHORT, 1 }, { TIFFTAG_STRIPOFFSETS, TIFF_LONG, 1, TIFFdiroff(yres[2]) }, { TIFFTAG_ORIENTATION, TIFF_SHORT, 1 }, { TIFFTAG_SAMPLESPERPIXEL, TIFF_SHORT, 1, 1 }, { TIFFTAG_ROWSPERSTRIP, TIFF_LONG, 1, (uint32) -1 }, { TIFFTAG_STRIPBYTECOUNTS, TIFF_LONG, 1 }, { TIFFTAG_XRESOLUTION, TIFF_RATIONAL, 1, TIFFdiroff(xres[0]) }, { TIFFTAG_YRESOLUTION, TIFF_RATIONAL, 1, TIFFdiroff(yres[0]) }, { TIFFTAG_GROUP3OPTIONS, TIFF_LONG, 1 }, { TIFFTAG_RESOLUTIONUNIT, TIFF_SHORT, 1 }, { TIFFTAG_PAGENUMBER, TIFF_SHORT, 2 }, { TIFFTAG_BADFAXLINES, TIFF_LONG, 1 }, { TIFFTAG_CLEANFAXDATA, TIFF_SHORT, 1 }, { TIFFTAG_CONSECUTIVEBADFAXLINES,TIFF_LONG, 1 }, 0, // next directory { 0, 1 }, { 0, 1 }, // x+y resolutions };#define NTAGS ((TIFFdiroff(link)-TIFFdiroff(SubFileType)) / sizeof (TIFFDirEntry)) /* * Construct the TIFF header for this IFD using * the preconstructed template above. We extract * the necessary information from the open TIFF file. * In case it's not obvious, this code assumes a lot * of things about the contents of the TIFF file. */ struct { TIFFHeader h; uint16 dircount; u_char dirstuff[sizeof (templ)]; } buf; union { int32 i; char c[4]; } u; u.i = 1; buf.h.tiff_magic = (u.c[0] == 0 ? TIFF_BIGENDIAN : TIFF_LITTLEENDIAN); buf.h.tiff_version = TIFF_VERSION; buf.h.tiff_diroff = sizeof (TIFFHeader); buf.dircount = (uint16) NTAGS; getLong(tif, templ.SubFileType); getLong(tif, templ.ImageWidth); getLong(tif, templ.ImageLength); getShort(tif, templ.Compression); getShort(tif, templ.Photometric); getShort(tif, templ.FillOrder); getShort(tif, templ.Orientation); templ.StripByteCounts.tdir_offset = (uint32) file_size - sizeof (buf); float res; TIFFGetField(tif, TIFFTAG_XRESOLUTION, &res); templ.xres[0] = (uint32) res; TIFFGetField(tif, TIFFTAG_YRESOLUTION, &res); templ.yres[0] = (uint32) res; if (templ.Compression.tdir_offset == COMPRESSION_CCITTFAX3) { templ.Options.tdir_tag = TIFFTAG_GROUP3OPTIONS; getLong(tif, templ.Options); } else if (templ.Compression.tdir_offset == COMPRESSION_CCITTFAX4) { templ.Options.tdir_tag = TIFFTAG_GROUP4OPTIONS; getLong(tif, templ.Options); } getShort(tif, templ.ResolutionUnit); TIFFGetField(tif, TIFFTAG_PAGENUMBER, &templ.PageNumber.tdir_offset); getLong(tif, templ.BadFaxLines); getShort(tif, templ.CleanFaxData); getLong(tif, templ.ConsecutiveBadFaxLines); if (buf.h.tiff_magic == TIFF_BIGENDIAN) { TIFFDirEntry* dp = &templ.SubFileType; for (u_int i = 0; i < NTAGS; i++) { if (dp->tdir_type == TIFF_SHORT) dp->tdir_offset <<= 16; dp++; } } memcpy(buf.dirstuff, &templ, sizeof (templ)); if (write(fdout, (const char*) &buf, sizeof (buf)) != sizeof (buf)) { perror_reply(426, "Data connection", errno); return (false); } else { byte_count += sizeof (buf); return (true); }#undef NTAGS#undef offsetof}/* * Send the raw image data for the current directory * in the open TIFF file. If multiple strips are * present in the file they are concatenated w/o * consideration for any padding that might be present * or might be needed. */boolHylaFAXServer::sendITIFFData(TIFF* tif, int fdout){ uint32* sb; (void) TIFFGetField(tif, TIFFTAG_STRIPBYTECOUNTS, &sb); tdata_t buf = _TIFFmalloc(sb[0]); tsize_t bsize = sb[0]; for (tstrip_t s = 0, ns = TIFFNumberOfStrips(tif); s < ns; s++) { tsize_t cc = sb[s]; if (cc > bsize) { buf = _TIFFrealloc(buf, cc); bsize = cc; } if (buf == NULL) { reply(551, "Error allocating intermediate buffer"); return (false); } if (TIFFReadRawStrip(tif, s, buf, cc) != cc) { reply(551, "Error reading input file at strip %u", s); goto bad; } if (write(fdout, buf, (u_int) cc) != cc) { perror_reply(426, "Data connection", errno); goto bad; } byte_count += cc; } _TIFFfree(buf); return (true);bad: _TIFFfree(buf); return (false);}const char*HylaFAXServer::dataConnMsg(int code){ return (code == 125 ? "Using existing data connection" : "Opening new data connection");}voidHylaFAXServer::closeDataConn(FILE* fd){ fclose(fd); data = -1; pdata = -1;}/* * STORe a file. */voidHylaFAXServer::storeCmd(const char* name, const char* mode){ struct stat sb; SpoolDir* sd = fileAccess(name, W_OK, sb); if (sd) { // check filename for magic characters for (const char* cp = name; *cp; cp++)
⌨️ 快捷键说明
复制代码
Ctrl + C
搜索代码
Ctrl + F
全屏模式
F11
切换主题
Ctrl + Shift + D
显示快捷键
?
增大字号
Ctrl + =
减小字号
Ctrl + -