📄 parser.c++
字号:
/* $Id: Parser.c++,v 1.11 2005/05/09 14:57:44 aidan 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. *//* * HylaFAX Client-Server Protocol Parser. */#include "port.h"#include "config.h"#include "Sys.h"#include "Socket.h"#include "HylaFAXServer.h"#include "Dispatcher.h"#include <ctype.h>#define N(a) (sizeof (a) / sizeof (a[0]))/* * Standard protocol commands. */static const tab cmdtab[] = {{ "ABOR", T_ABOR, false, true, "[modem] (abort operation)" },{ "ACCT", T_ACCT, false,false, "(specify account)" },{ "ADMIN", T_ADMIN, true, true, "password" },{ "ALLO", T_ALLO, false,false, "(allocate disk space)" },{ "ANSWER", T_ANSWER, true, true, "modem [DATA|VOICE|FAX]" },{ "APPE", T_APPE, true, true, "file-name" },{ "CWD", T_CWD, true, true, "[directory-name]" },{ "CDUP", T_CDUP, true, true, "(change directory up one level)"},{ "CHMOD", T_CHMOD, true, true, "file-name mode" },{ "CHOWN", T_CHOWN, true, true, "file-name user" },{ "DELE", T_DELE, true, true, "file-name" },{ "DISABLE", T_DISABLE, true, true, "modem [reason]" },{ "ENABLE", T_ENABLE, true, true, "modem" },{ "HELP", T_HELP, false, true, "[<string>]" },{ "FILEFMT", T_FILEFMT, true, true, "[format-string]" },{ "FORM", T_FORM, true, true, "format-type" },{ "IDLE", T_IDLE, true, true, "[max-idle-timeout]" },{ "JDELE", T_JDELE, true, true, "[job-id]" },{ "JINTR", T_JINTR, true, true, "[job-id]" },{ "JKILL", T_JKILL, true, true, "[job-id]" },{ "JNEW", T_JNEW, true, true, "" },{ "JOB", T_JOB, true, true, "[job-id]" },{ "JOBFMT", T_JOBFMT, true, true, "[format-string]" },{ "JPARM", T_JPARM, true, true, "[parm-name [parm-value]]" },{ "JREST", T_JREST, true, true, "(reset current job state)" },{ "JSUBM", T_JSUB, true, true, "[job-id]" },{ "JSUSP", T_JSUSP, true, true, "[job-id]" },{ "JWAIT", T_JWAIT, true, true, "[job-id]" },{ "JGDELE", T_JGDELE, true,false, "[jobgroup-id]" },{ "JGINTR", T_JGINTR, true,false, "[jobgroup-id]" },{ "JGKILL", T_JGKILL, true,false, "[jobgroup-id]" },{ "JGNEW", T_JGNEW, true, true, "" },{ "JGPARM", T_JGPARM, true,false, "parm-name [parm-value]" },{ "JGREST", T_JGREST, true,false, "(reset current job group state)"},{ "JGRP", T_JGRP, true,false, "[jobgroup-id]" },{ "JGSUBM", T_JGSUB, true,false, "[jobgroup-id]" },{ "JGSUSP", T_JGSUSP, true,false, "[jobgroup-id]" },{ "JGWAIT", T_JGWAIT, true,false, "[jobgroup-id]" },{ "LIST", T_LIST, true, true, "[path-name]" },{ "MDTM", T_MDTM, true, true, "path-name" },{ "MODE", T_MODE, false, true, "(specify transfer mode)" },{ "MDMFMT", T_MODEMFMT, true, true, "[format-string]" },{ "NLST", T_NLST, true, true, "[path-name]" },{ "NOOP", T_NOOP, false, true, "" },{ "PASS", T_PASS, false, true, "password" },{ "PASV", T_PASV, true, true, "(set server in passive mode)" },{ "PORT", T_PORT, true, true, "a0,a1,a2,a3,p0,p1" },{ "PWD", T_PWD, true, true, "(print working directory)" },{ "QUIT", T_QUIT, false, true, "(terminate service)", },{ "RCVFMT", T_RCVFMT, true, true, "[format-string]" },{ "REIN", T_REIN, false, true, "(reinitialize server state)" },{ "REST", T_REST, true, true, "restart-marker" },{ "RETP", T_RETP, true, true, "file-name" },{ "RETR", T_RETR, true, true, "file-name" },{ "RNFR", T_RNFR, true,false, "file-name" },{ "RNTO", T_RNTO, true,false, "file-name" },{ "SHUT", T_SHUT, true, true, "NOW|HHSS|YYYYMMDDHHSS [reason]" },{ "SITE", T_SITE, true, true, "site-cmd [arguments]" },{ "SIZE", T_SIZE, true, true, "path-name" },{ "STAT", T_STAT, false, true, "[path-name]" },{ "STOR", T_STOR, true, true, "file-name" },{ "STOT", T_STOT, true, true, "(store unique temporary)" },{ "STOU", T_STOU, true, true, "(store unique)" },{ "STRU", T_STRU, false, true, "file-structure" },{ "SYST", T_SYST, true, true, "(return system type)" },{ "TZONE", T_TZONE, true, true, "[GMT|LOCAL]" },{ "TYPE", T_TYPE, false, true, "transfer-type" },{ "USER", T_USER, false, true, "username" },{ "VRFY", T_VRFY, false, true, "dialstring" },};/* * Job parameter commands/keys. */static const tab parmtab[] = {{ "ACCTINFO", T_ACCTINFO, false,false, "[<string>]" },{ "BEGBR", T_BEGBR, false, true, "[ANY|bit-rate]" },{ "BEGST", T_BEGST, false, true, "[ANY|scanline-time]" },{ "CHOPTHRESHOLD",T_CHOPTHRESH, false, true, "[inches]" },{ "CLIENT", T_CLIENT, false, true, "[<string>]" },{ "COMMENTS", T_COMMENTS, false, true, "[<string>]" },{ "COMMID", T_COMMID, false, true, "(communication identifier)" },{ "COVER", T_COVER, false, true, "path-name" },{ "DATAFORMAT", T_DATAFORMAT, false, true, "[ANY|G31D|G32D|G4]" },{ "DIALSTRING", T_DIALSTRING, false, true, "[<string>]" },{ "DOCUMENT", T_DOCUMENT, false, true, "path-name" },{ "DONEOP", T_DONEOP, false, true, "[<string>]" },{ "EXTERNAL", T_EXTERNAL, false, true, "[<string>]" },{ "FAXNUMBER", T_FAXNUMBER, false, true, "[<string>]" },{ "FROMCOMPANY", T_FROM_COMPANY, false, true, "[<string>]" },{ "FROMLOCATION", T_FROM_LOCATION,false, true, "[<string>]" },{ "FROMUSER", T_FROM_USER, false, true, "[<string>]" },{ "FROMVOICE", T_FROM_VOICE, false, true, "[<string>]" },{ "GROUPID", T_GROUPID, false, true, "(job group identifier)" },{ "HRES", T_HRES, false,false, "[dots-per-inch]" },{ "JOBID", T_JOBID, false, true, "(job identifier)" },{ "JOBINFO", T_JOBINFO, false, true, "[<string>]" },{ "JOBTYPE", T_JOBTYPE, false, true, "(job type)" },{ "LASTTIME", T_LASTTIME, false, true, "[DDHHSS]" },{ "MAXDIALS", T_MAXDIALS, false, true, "[<number>]" },{ "MAXPAGES", T_MAXPAGES, false, true, "[<number>]" },{ "MAXTRIES", T_MAXTRIES, false, true, "[<number>]" },{ "MINBR", T_MINBR, false, true, "[ANY|bit-rate]" },{ "MODEM", T_MODEM, false, true, "[device|class]" },{ "NDIALS", T_NDIALS, false, true, "[<number>]" },{ "NOTIFY", T_NOTIFY, false, true, "[NONE|DONE|REQUEUE|DONE+REQUEUE]" },{ "NOTIFYADDR", T_NOTIFYADDR, false, true, "[email-address]" },{ "NPAGES", T_NPAGES, false, true, "[<number>]" },{ "NTRIES", T_NTRIES, false, true, "[<number>]" },{ "OWNER", T_OWNER, false, true, "[<name>|<number>]" },{ "PAGECHOP", T_PAGECHOP, false, true, "[DEFAULT|NONE|ALL|LAST]" },{ "PAGELENGTH", T_PAGELENGTH, false, true, "[millimeters]" },{ "PAGEWIDTH", T_PAGEWIDTH, false, true, "[millimeters]" },{ "PASSWD", T_PASSWD, false, true, "[<string>]" },{ "POLL", T_POLL, false, true, "selector [passwd]" },{ "REGARDING", T_REGARDING, false, true, "[<string>]" },{ "RETRYTIME", T_RETRYTIME, false, true, "[HHSS]" },{ "SCHEDPRI", T_SCHEDPRI, false, true, "[<number>]" },{ "SENDTIME", T_SENDTIME, false, true, "[NOW|YYYYMMDDHHSS]" },{ "STATE", T_STATE, false, true, "(job state)" },{ "STATUS", T_STATUS, false, true, "[<string>" },{ "SUBADDR", T_SUBADDR, false, true, "[<string>]" },{ "TAGLINE", T_TAGLINE, false, true, "[<string>]" },{ "TOCOMPANY", T_TO_COMPANY, false, true, "[<string>]" },{ "TOLOCATION", T_TO_LOCATION, false, true, "[<string>]" },{ "TOTDIALS", T_TOTDIALS, false, true, "[<number>]" },{ "TOTPAGES", T_TOTPAGES, false, true, "[<number>]" },{ "TOTTRIES", T_TOTTRIES, false, true, "[<number>]" },{ "TOUSER", T_TO_USER, false, true, "[<string>]" },{ "TOVOICE", T_TO_VOICE, false, true, "[<string>]" },{ "TSI", T_TSI, false, true, "[<string>]" },{ "USECONTCOVER", T_USE_CONTCOVER,false, true, "[YES|NO]" },{ "USEXVRES", T_USE_XVRES, false, true, "[YES|NO]" },{ "USEECM", T_USE_ECM, false, true, "[YES|NO]" },{ "USETAGLINE", T_USE_TAGLINE, false, true, "[YES|NO]" },{ "USRKEY", T_USRKEY, false, true, "[<string>]" },{ "VRES", T_VRES, false, true, "[lines-per-inch]" },{ "HELP", T_HELP, false, true, "[<string>]" },};/* * Site-specific commands. */static const tab sitetab[] = {{ "ADDMODEM", T_ADDMODEM, false,false, "modem [speed]"},{ "ADDUSER", T_ADDUSER, false, true, "user-spec [passwd [adminwd]]"},{ "CONFIG", T_CONFIG, false, true, "[parm-name [parm-value]]" },{ "DELMODEM", T_DELMODEM, false,false, "modem" },{ "DELUSER", T_DELUSER, false, true, "user-spec" },{ "TRIGGER", T_TRIGGER, false, true, "spec" },{ "HELP", T_HELP, false, true, "[<string>]" },};static const tab*lookup(const tab* p, u_int n, const char* cmd){ while (n != 0) { if (strcmp(cmd, p->name) == 0) return (p); p++, n--; } return (NULL);}static const char*tokenName(const tab* p, u_int n, Token t){ while (n != 0) { if (p->token == t) return (p->name); p++, n--; } return ("???");}const char*HylaFAXServer::cmdToken(Token t){ return tokenName(cmdtab, N(cmdtab), t);}const char*HylaFAXServer::siteToken(Token t){ return tokenName(sitetab, N(sitetab), t);}const char*HylaFAXServer::parmToken(Token t){ return tokenName(parmtab, N(parmtab), t);}u_intHylaFAXServer::twodigits(const char* cp, u_int range){ return ((cp[0]-'0')*10 + (cp[1]-'0')) % range;}u_intHylaFAXServer::fourdigits(const char* cp){ return (cp[0]-'0')*1000 + (cp[1]-'0')*100 + (cp[2]-'0')*10 + (cp[3]-'0');}inline boolisLoginToken(Token t){ return (t == T_USER || t == T_PASS || t == T_ADMIN);}/* * Parse and process command input received on the * control channel. This method is invoked whenever * data is present on the control channel. We read * everything and parse (and execute) as much as possible * but do not block waiting for more data except when * a partial line of input is received. This is done * to ensure other processing will be handled in a * timely fashion (e.g. processing of messages from * the scheduler received via the FIFO). */intHylaFAXServer::parse(){ if (IS(WAITDATA)) { // recursive invocation state &= ~S_WAITDATA; return (0); } // stop control channel idle timeout Dispatcher::instance().stopTimer(this); pushToken(T_NIL); // reset state for (;;) { /* * Fetch the next complete line of input received on the * control channel. This call will fail when no more data * is *currently* waiting on the control channel. Note * that this does not mean the connection has dropped; just * that data is not available at this instant. Note also * that if a partial line of input is received a complete * line will be waited for (see below). */ if (!getCmdLine(cbuf, sizeof (cbuf))) break; /* * Parse the line of input read above. */ if (getToken(T_STRING, "command token")) { tokenBody.raisecase(); const tab* p = lookup(cmdtab, N(cmdtab), tokenBody); if (p == NULL) reply(500, "%s: Command not recognized.", (const char*) tokenBody); else if (!p->implemented) reply(502, "%s: Command not implemented.", p->name); /* * If the user is not privileged, then check for * the service being shutdown if the command is * not part of the login procedure (USER-PASS-ADMIN). * This permits administrators to gain access to * a system that is shutdown. Other users will get * dropped if they type something else. */ else if (!IS(PRIVILEGED) && !isLoginToken(p->token) && isShutdown(!IS(LOGGEDIN))) { reply(221, "Server shutting down. Goodbye."); dologout(0); // XXX /* * If command requires client to be logged in check * for this. Note that some commands have variants * that do not require the client be logged in--these * cannot check here and must do it specially below. */ } else if (p->checklogin && !checklogin(p->token)) ; /* * Command is valid, implemented, and the server * is available to service it. If the syntax is * correct then reset the number of consecutive * bad commands. Note that since part of the syntax * checking is login validation this logic will also * catch people typing syntacitcally valid but * unacceptable commands just to remain connected. */ else if (cmd(p->token)) { if (p->token != T_REST) restart_point = 0; consecutiveBadCmds = 0; continue; } } /* * If too many consecutive bad commands have been * received disconnect. This is to safeguard against * a client that spews trash down the control connection. */ if (++consecutiveBadCmds >= maxConsecutiveBadCmds) { /* * Check for shutdown so that any shutdown message * will get prepended to client reply. */ (void) isShutdown(!IS(LOGGEDIN)); reply(221, "Server shutting down. Goodbye."); dologout(0); } } Dispatcher::instance().startTimer(idleTimeout, 0, this); return (0);}/* * Protocol command (one line). */boolHylaFAXServer::cmd(Token t){ fxStr s; long n; switch (t) { case T_USER: // user name if (string_param(s, "user name")) { logcmd(t, "%s", (const char*) s); userCmd(s); return (true); } break; case T_PASS: // user password if (string_param(s, "password")) { logcmd(t, "<password>"); passCmd(s); return (true); } break; case T_ADMIN: // administrator password if (opt_CRLF()) s = ""; else if (!string_param(s, "password")) break; logcmd(t, "<password>"); adminCmd(s); return (true); case T_REIN: // reinitialize server logcmd(t); if (IS(LOGGEDIN)) (void) chdir("/"); // return to top of spooling area initServer(); reply(220, "%s server (%s) ready.", (const char*) hostname, version); break; case T_REST: // restart data transfer if (number_param(n)) { logcmd(t, "%lu", n); restart_point = n; reply(350, "Data transfer will restart at %lu, " "send transfer command", n); return (true); } break; case T_SYST: // system identification if (CRLF()) { logcmd(t); // this is what we *emulate* reply(215, "%s", (const char*) systemType); return (true); } break; case T_PORT: // port for data transfer if (SPACE() && hostPort() && CRLF()) { portCmd(); return (true); } break; case T_PASV: // enable passive mode if (CRLF()) { logcmd(t); passiveCmd(); return (true); } break; case T_FORM: // document format if (string_param(s, "document format")) { logcmd(t, "%s", (const char*) s); formCmd(s); return (true); } break; case T_MODE: // data transfer mode if (string_param(s, "transfer mode")) { logcmd(t, "%s", (const char*) s); modeCmd(s); return (true); } break; case T_STRU: // data transfer file structure if (string_param(s, "file structure")) { logcmd(t, "%s", (const char*) s); struCmd(s); return (true); } break;
⌨️ 快捷键说明
复制代码
Ctrl + C
搜索代码
Ctrl + F
全屏模式
F11
切换主题
Ctrl + Shift + D
显示快捷键
?
增大字号
Ctrl + =
减小字号
Ctrl + -