⭐ 欢迎来到虫虫下载站! | 📦 资源下载 📁 资源专辑 ℹ️ 关于我们
⭐ 虫虫下载站

📄 snppserver.c++

📁 fax相关的东西
💻 C++
📖 第 1 页 / 共 3 页
字号:
/*	$Id: SNPPServer.c++,v 1.14 2006/03/27 21:36:28 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. */#ifdef SNPP_SUPPORT/* * Simple Network Paging Protocol (SNPP) Support. */#include "config.h"#include "Sys.h"#include "Socket.h"#include "SNPPServer.h"#include "Dispatcher.h"#include "RE.h"#include <ctype.h>#if HAS_CRYPT_H#include <crypt.h>#endifextern "C" {#include <netdb.h>#include <arpa/inet.h>#include <netinet/in_systm.h>#include <netinet/ip.h>}SNPPSuperServer::SNPPSuperServer(const char* p, int bl)    : SuperServer("SNPP", bl)    , port(p){}SNPPSuperServer::~SNPPSuperServer() {}boolSNPPSuperServer::startServer(void){    /*     * Switch to super-user to do the bind in case     * we are to use a port in the privileged region     * on a BSD system (ports <=1024 are reserved     * and the default SNPP port is 444).     *     * NB: We do it for both the socket+bind calls     *     to workaround a bug in Solaris 2.5.     */    uid_t ouid = geteuid();    (void) seteuid(0);    int s = socket(AF_INET, SOCK_STREAM, 0);    if (s >= 0) {	struct sockaddr_in sin;	memset(&sin, 0, sizeof (sin));	sin.sin_family = AF_INET;	const char* cp = port;	struct servent* sp = getservbyname(cp, SNPP_PROTONAME);	if (!sp) {	    if (isdigit(cp[0]))		sin.sin_port = htons(atoi(cp));	    else		sin.sin_port = htons(SNPP_DEFPORT);	} else	    sin.sin_port = sp->s_port;	if (Socket::bind(s, &sin, sizeof (sin)) >= 0) {	    (void) listen(s, getBacklog());	    (void) seteuid(ouid);	    Dispatcher::instance().link(s, Dispatcher::ReadMask, this);	    return (true);				// success	}	Sys::close(s);	logError("HylaFAX %s: bind (port %u): %m",	    getKind(), ntohs(sin.sin_port));    } else	logError("HylaFAX %s: socket: %m", getKind());    (void) seteuid(ouid);    return (false);}HylaFAXServer* SNPPSuperServer::newChild(void)	{ return new SNPPServer; }SNPPServer::SNPPServer() {}SNPPServer::~SNPPServer() {}voidSNPPServer::open(void){    setupNetwork(STDIN_FILENO);    initServer();				// complete state initialization    fxStr emsg;    if (!initClientFIFO(emsg)) {        logInfo("connection refused (%s) from %s [%s]",	    (const char*) emsg,	    (const char*) remotehost, (const char*) remoteaddr);	reply(420, "%s server cannot initialize: %s",	    (const char*) hostname, (const char*) emsg);	dologout(-1);    }    ctrlFlags = fcntl(STDIN_FILENO, F_GETFL);	// for parser    if (isShutdown(true)) {	reply(421, "%s SNPP server unavailable; try again later.",	    (const char*) hostname);	dologout(-1);    }    reply(220, "%s SNPP server (%s) ready.", (const char*) hostname, version);    if (TRACE(CONNECT))        logInfo("SNPP connection from %s [%s]",	    (const char*) remotehost, (const char*) remoteaddr);}voidSNPPServer::initServer(void){    InetFaxServer::initServer();    state &= ~S_USEGMT;			// SNPP uses local times    resetState();}voidSNPPServer::initDefaultJob(void){    InetFaxServer::initDefaultJob();    defJob.jobtype = "pager";    initSNPPJob();}voidSNPPServer::initSNPPJob(void){    defJob.queued = false;		// jobs not queued--per protocol    defJob.maxdials = SNPP_DEFREDIALS;	// configuration defaults...    defJob.maxtries = SNPP_DEFRETRIES;    defJob.killtime = 60*killMap[SNPP_DEFLEVEL];    defJob.retrytime = retryMap[SNPP_DEFLEVEL];    defJob.usrpri = priMap[SNPP_DEFLEVEL];    // XXX default notification}voidSNPPServer::resetState(void){    initDefaultJob();			// default job state    msgFile = "";    haveText = false;			// no message text yet    msgs.resize(0);			// purge any message refs}voidSNPPServer::dologout(int status){    /*     * Purge any partially constructed jobs.  If we were     * doing a SEND when the connection was dropped then     * we do not purge jobs but instead just let them run     * detached.  This may not be the best thing; we might     * instead want to kill the jobs--we'll need to think     * about this some more before changing this behaviour.     */    if (msgs.length() > 0 && !IS(SENDWAIT)) {	fxStr emsg;	for (u_int i = 0, n = msgs.length(); i < n; i++) {	    Job* job = findJob(msgs[i], emsg);	    if (job) {		for (u_int j = 0, m = job->items.length(); j < m; j++)		    if (job->items[j].op == FaxRequest::send_data)			(void) Sys::unlink(job->items[j].item);		(void) Sys::unlink(job->qfile);		delete job;	    }	}    }    InetFaxServer::dologout(status);}static inline boolisMagic(char c){    return (c == '[' || c == ']' || c == '*' || c == '.' || c == '^' ||	c == '$' || c == '-' || c == '+' || c == '{' || c == '}' ||	c == '(' || c == ')');}/* * Handle \escapes for a pager ID replacement string. */static voidsubRHS(fxStr& result, const RE& re, const fxStr& match){    /*     * Do ``&'' and ``\n'' interpolations in the replacement.     */    for (u_int i = 0, n = result.length(); i < n; i++) {	if (result[i] == '\\') {		// process \<char> escapes	    result.remove(i), n--;	    if (isdigit(result[i])) {		int mn = result[i] - '0';		int ms = re.StartOfMatch(mn);		int mlen = re.EndOfMatch(mn) - ms;		result.remove(i);		// delete \n		if (ms >= 0)		    result.insert(match.extract(ms, mlen), i);		else		    logError("Invalid backreference in pagermap: \\%d", mn);		n = result.length();		// adjust string length ...		i += mlen - 1;			// ... and scan index	    }	} else if (result[i] == '&') {		// process & replacement	    int ms = re.StartOfMatch(0);	    int mlen = re.EndOfMatch(0) - ms;	    result.remove(i);			// delete &	    result.insert(match.extract(ms, mlen), i);	    n = result.length();		// adjust string length ...	    i += mlen - 1;			// ... and scan index	}    }}/* * Given a client-supplied pager ID return the phone number * to dial of the service provider and the PIN to supply * to the provider when talking IXO/TAP (for aliases). */boolSNPPServer::mapPagerID(const char* pagerID, fxStr& number, fxStr& pin, fxStr& emsg){    if (pagerIDMapFile != "") {	FILE* fd = fopen(fixPathname(pagerIDMapFile), "r");	if (fd != NULL) {	    char buf[1024];	    while (fgets(buf, sizeof (buf), fd)) {		char* cp;		for (cp = buf; isspace(*cp); cp++)	// leading whitespace		    ;		if (*cp == '#' || *cp == '\0')		    continue;		/*		 * Syntax is:		 *		 * <pattern> <dialstring>[/<PIN>]		 *		 * where <pattern> can be a simple string of alpha		 * numerics or a regular expression.  The first line		 * that matches the client-specified pager ID is used.		 * If no <PIN> is specified then the client-specified		 * string is sent to the provider.  If <dialstring>		 * is "reject" (verbatim) then matches are rejected.		 * <PIN> is treated as the RHS of an RE-style substitution:		 * \n and & escapes are replaced according to the RE		 * matching work.		 *		 * Leading white space on a line is ignored.  Lines		 * that begin with '#' are ignored (i.e. comments).		 */		const char* pattern = cp;		bool isRE = false;		for (; *cp != '\0' && !isspace(*cp); cp++)		    if (isMagic(*cp))			isRE = true;		if (*cp != '\0')			// \0-term. <pattern>		    *cp++ = '\0';		bool match;		RE* re;		if (isRE) {		    re = new RE(pattern);		    match = re->Find(pagerID, strlen(pagerID));		} else {		    match = streq(pattern, pagerID);		    re = NULL;		}		if (match) {		    while (isspace(*cp))		// leading whitespace			cp++;		    if (*cp != '\n' && *cp != '\0') {	// got <dialstring>			char* np = strchr(cp, '\n');	// remove trailing \n			if (np)			    *np = '\0';			np = cp;			for (; *cp && *cp != '/'; cp++)			    ;			if (*cp == '/') {		// <dialstring>/<PIN>			    *cp++ = '\0';			    number = np;			    pin = cp;			    if (re) {			// do substitutions				    subRHS(pin, *re, pagerID);                    subRHS(number, *re, pagerID);                }			} else {			// <dialstring>			    number = np;			    pin = pagerID;			}			delete re;			(void) fclose(fd);			fxStr s(number);			s.raisecase();			if (s == "REJECT") {			    emsg = fxStr::format("Invalid pager ID %s",pagerID);			    return (false);			} else			    return (true);		    }		}		delete re;	    }	    (void) fclose(fd);	    emsg = fxStr::format("Unknown or illegal pager ID %s", pagerID);	} else {	    emsg = fxStr::format("Cannot open pager ID mapping file %s (%s)", 		(const char*) pagerIDMapFile, strerror(errno));	    logError("%s", (const char*) emsg);	}    } else {	emsg = "No pager ID mapping file found";	logError("%s", (const char*) emsg);    }    return (false);}/* * SNPP Parser. */#define	N(a)	(sizeof (a) / sizeof (a[0]))/* * Standard protocol commands. */static const tab cmdtab[] = {{ "2WAY", T_2WAY,	 true,false, "(preface 2-way transaction)" },{ "ABOR", T_ABOR,	false, true, "(abort operation)" },{ "ACKR", T_ACKREAD,	 true,false, "0|1" },{ "ALER", T_ALERT,	 true,false, "alert-level" },{ "CALL", T_CALLERID,    true,false, "caller-ID" },{ "COVE", T_COVERAGE,    true,false, "alternate-area" },{ "DATA", T_DATA,	 true, true, "(specify multi-line message)" },{ "EXPT", T_EXPTAG,	 true,false, "hours" },{ "HELP", T_HELP,	false, true, "[<string>]" },{ "HOLD", T_HOLDUNTIL,   true, true, "YYYYMMDDHHMMSS [+/-GMT-diff]" },{ "KTAG", T_KTAG,	 true,false, "message-tag pass-code" },{ "LEVE", T_LEVEL,	 true, true, "service-level" },{ "LOGI", T_LOGIN,	false, true, "username [password]" },{ "MCRE", T_MCRESPONSE,  true,false, "2-byte-code response-text" },{ "MESS", T_MESSAGE,	 true, true, "message" },{ "MSTA", T_MSTATUS,	 true,false, "message-tag pass-code" },{ "NOQU", T_NOQUEUEING,  true,false, "(disable message queueing)" },{ "PAGE", T_PAGER,	 true, true, "pager-ID|alias [PIN]" },{ "PING", T_PING,	 true, true, "pager-ID|alias" },{ "QUIT", T_QUIT,	false, true, "(terminate service)" },{ "RESE", T_REST,	 true, true, "(reset state)" },{ "RTYP", T_RTYPE,	 true,false, "reply-type-code" },{ "SEND", T_SEND,	 true, true, "(send message)" },{ "SITE", T_SITE,	 true, true, "site-cmd [arguments]" },{ "STAT", T_STAT,	false, true, "(return server status)" },{ "SUBJ", T_SUBJECT,	 true, true, "message-subject" },};/* * Site-specific commands. */static const tab sitetab[] = {{ "FROMUSER",   T_FROM_USER,  true, true, "[<string>]" },{ "HELP",       T_HELP,      false, true, "[<string>]" },{ "IDLE",       T_IDLE,       true, true, "[max-idle-timeout]" },{ "JPARM",      T_JPARM,      true, true, "(print job parameter status)" },{ "JQUEUE",	T_JQUEUE,     true, true, "[on|off]" },{ "LASTTIME",   T_LASTTIME,  false, true, "[DDHHSS]" },{ "MAXDIALS",   T_MAXDIALS,   true, true, "[<number>]" },{ "MAXTRIES",   T_MAXTRIES,   true, true, "[<number>]" },{ "MODEM",      T_MODEM,      true, true, "[device|class]" },{ "NOTIFY",     T_NOTIFY,     true, true, "[NONE|DONE|REQUEUE|DONE+REQUEUE]"},{ "MAILADDR",   T_NOTIFYADDR, true, true, "[email-address]" },{ "RETRYTIME",  T_RETRYTIME,  true, true, "[HHSS]" },{ "SCHEDPRI",   T_SCHEDPRI,   true, true, "[<number>]" },};static const tab*lookup(const tab* p, u_int n, const char* cmd){    while (n != 0) {        if (strneq(cmd, p->name, 4))		// NB: match on first 4 chars	    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*SNPPServer::cmdToken(Token t){    return tokenName(cmdtab, N(cmdtab), t);}const char*SNPPServer::siteToken(Token t){    return tokenName(sitetab, N(sitetab), t);}boolSNPPServer::checklogin(Token t){    if (!IS(LOGGEDIN)) {	cmdFailure(t, "not logged in");	reply(530, "Please login with LOGIN.");	return (false);    } else	return (true);}/* * 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). */intSNPPServer::parse(){    if (IS(WAITDATA)) {			// recursive invocation	state &= ~S_WAITDATA;	return (0);    }    if (IS(SENDWAIT)) {			// waiting in SEND, discard data	while (getCmdLine(cbuf, sizeof (cbuf)))	    ;	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)

⌨️ 快捷键说明

复制代码 Ctrl + C
搜索代码 Ctrl + F
全屏模式 F11
切换主题 Ctrl + Shift + D
显示快捷键 ?
增大字号 Ctrl + =
减小字号 Ctrl + -