📄 snppserver.c++
字号:
reply(500, "%s: Command not implemented.", p->name); else if (isShutdown(!IS(LOGGEDIN))) { reply(421, "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)) { 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(421, "Too many errors, server shutting down. Goodbye."); dologout(0); } } Dispatcher::instance().startTimer(idleTimeout, 0, this); return (0);}/* * Protocol command (one line). */boolSNPPServer::cmd(Token t){ fxStr s; long n; time_t tv; switch (t) { case T_ABOR: // abort active command (e.g. SEND) if (CRLF()) { logcmd(t); ack(250, cmdToken(t)); return (true); } break; case T_DATA: // submit multi-line message data if (CRLF()) { logcmd(t); dataCmd(); return (true); } break; case T_HELP: // return help if (opt_CRLF()) { logcmd(t); helpCmd(cmdtab, (char*) NULL); return (true); } else if (string_param(s, "command name")) { logcmd(t, "%s", (const char*) s); s.raisecase(); if (s == "SITE") helpCmd(sitetab, NULL); else helpCmd(cmdtab, s); return (true); } break; case T_HOLDUNTIL: // set time to send if (SPACE() && SNPPTime(tv)) { if (opt_CRLF()) { holdCmd(tv); return (true); } else if (string_param(s, "GMT-difference")) { // conver GMT offset to numeric value int sign = 1; const char* cp = s; if (*cp == '-') cp++, sign = -1; else if (*cp == '+') cp++; time_t off = (time_t) (strtoul(cp, 0, 10)*60 / 100); holdCmd(tv + sign*off); return (true); } } break; case T_LEVEL: // set level of operation if (number_param(n)) { logcmd(t, "%lu", n); serviceLevel((u_int) n); return (true); } break; case T_LOGIN: // login as user if (SPACE() && STRING(s, "login-ID")) { fxStr pwd; if (opt_CRLF()) { logcmd(t, (const char*) s); loginCmd(s); return (true); } else if (string_param(pwd, "password")) { logcmd(t, "%s <passwd>", (const char*) s); loginCmd(s, pwd); return (true); } } break; case T_MESSAGE: // specify 1-line message data if (SPACE() && multi_STRING(s) && CRLF()) { logcmd(t, "%s", (const char*) s); messageCmd(s); return (true); } break; case T_PAGER: // specify destination pager ID if (SPACE() && STRING(s, "pager-ID")) { fxStr pwd; if (opt_CRLF()) { logcmd(t, "%s", (const char*) s); pagerCmd(s); return (true); } else if (string_param(pwd, "password/PIN")) { logcmd(t, "%s <passwd/PIN>", (const char*) s); pagerCmd(s, pwd); return (true); } } break; case T_PING: // localize/verify pager ID if (string_param(s, "pager-ID")) { logcmd(t, "%s", (const char*) s); pingCmd(s); return (true); } break; case T_QUIT: // terminate session if (CRLF()) { logcmd(t); reply(221, "Goodbye."); dologout(0); return (true); } break; case T_REST: // reset server state logcmd(t); initServer(); reply(220, "%s server (%s) ready.", (const char*) hostname, version); break; case T_SEND: // initiate send operation if (CRLF()) { logcmd(t); sendCmd(); return (true); } break; case T_SITE: // site-specific command if (SPACE() && getToken(T_STRING, "site command")) { tokenBody.raisecase(); const tab* p = lookup(sitetab, N(sitetab), tokenBody); if (p == NULL) { reply(500, "SITE %s: Command not recognized.", (const char*) tokenBody); } else if (!p->implemented) reply(500, "SITE %s: Command not implemented.", p->name); else return (site_cmd(p->token)); } break; case T_STAT: // server status if (CRLF()) { logcmd(t); statusCmd(); return (true); } break; case T_SUBJECT: // message subject if (SPACE() && multi_STRING(s) && CRLF()) { logcmd(t, "%s", (const char*) s); subjectCmd(s); return (true); } break; } return (false);}/* * Site-specific protocol commands (one line). */boolSNPPServer::site_cmd(Token t){ fxStr s; long n; time_t tv; bool b; switch (t) { case T_IDLE: // set/query idle timeout if (opt_CRLF()) { logcmd(t); reply(250, "%u seconds.", idleTimeout); return (true); } else if (number_param(n)) { logcmd(t, "%lu", n); if ((unsigned)n > maxIdleTimeout && !IS(PRIVILEGED)) { idleTimeout = maxIdleTimeout; reply(250, "%lu: Idle timeout too large, set to %u.", n, maxIdleTimeout); } else { idleTimeout = (int) n; reply(250, "Idle timeout set to %u.", idleTimeout); } return (true); } break; case T_HELP: // return help if (opt_CRLF()) { helpCmd(sitetab, (char*) NULL); return (true); } else if (string_param(s, "command name")) { logcmd(T_SITE, "HELP %s", (const char*) s); s.raisecase(); helpCmd(sitetab, s); return (true); } break; case T_JQUEUE: if (boolean_param(b)) { logcmd(t, "%s", b ? "YES" : "NO"); defJob.queued = b; reply(250, "Job will%s be queued.", b ? "" : " not"); return (true); } break; case T_FROM_USER: case T_MODEM: case T_NOTIFY: case T_NOTIFYADDR: if (SPACE() && multi_STRING(s) && CRLF() && setJobParameter(defJob, t, s)) { logcmd(t, "%s", (const char*) s); reply(250, "%s set to \"%s\".", parmToken(t), (const char*) s); return (true); } break; case T_MAXDIALS: case T_MAXTRIES: case T_SCHEDPRI: if (number_param(n) && setJobParameter(defJob, t, (u_short) n)) { logcmd(t, "%lu", n); reply(250, "%s set to %u.", parmToken(t), n); return (true); } break; case T_LASTTIME: // time to kill job if (timespec_param(6, tv) && setJobParameter(defJob, t, tv)) { logcmd(t, "%02d%02d%02d" , tv/(24*60*60) , (tv/(60*60))%24 , (tv/60)%60); reply(250, "%s set to %02d%02d%02d." , parmToken(t) , tv/(24*60*60) , (tv/(60*60))%24 , (tv/60)%60); return (true); } break; case T_RETRYTIME: // retry interval for job if (timespec_param(4, tv) && setJobParameter(defJob, t, tv)) { logcmd(t, "%02d%02d" , tv/60, tv%60); reply(250, "%s set to %02d%02d.", parmToken(t), tv/60, tv%60); return (true); } break; case T_JPARM: // query job parameters if (CRLF()) { logcmd(t); jstatCmd(defJob); return (true); } break; } return (false);}/* * Parse an SNPP time specification. */boolSNPPServer::SNPPTime(time_t& result){ if (getToken(T_STRING, "time specification") && checkNUMBER(tokenBody)) { u_int tlen = tokenBody.length(); const char* cp = tokenBody; // YYMMDDHHMM[SS] if (tlen == 12 || tlen == 10) { struct tm tm; tm.tm_sec = (tlen == 12 ? twodigits(cp+10, 60) : 0); tm.tm_min = twodigits(cp+8, 60); tm.tm_hour = twodigits(cp+6, 24); tm.tm_mday = twodigits(cp+4, 32); tm.tm_mon = twodigits(cp+2, 13) - 1; tm.tm_year = twodigits(cp+0, 100); /* * SNPP botched time specifications by not using 4 digits * to specify a year. This means that we have to guess at * the intended year when the value is far in the future * (as opposed to a year in the past that was given by * mistake). We arbitrarily assume years prior to 1990 * are in the next century and adjust them accordingly. */ if (tm.tm_year < 90) tm.tm_year += 100; tm.tm_isdst= -1; // XXX not sure about this??? /* * The above time is assumed to be relative to * GMT and mktime returns locally adjusted time * so we need to adjust the result to get things * in the right timezone. Note that any additional * timezone correction factor specified by the * client will then be applied to this result. */ result = mktime(&tm) - gmtoff; return (true); } syntaxError(fxStr::format( "bad time specification (expecting 10/12 digits, got %u)", tlen)); } return (false);}voidSNPPServer::syntaxError(const char* msg){ const char* cp = strchr(cbuf, '\0'); if (cp[-1] == '\n') cp--; reply(500, "'%.*s': Syntax error, %s.", cp-cbuf, cbuf, msg);}/* * Command support methods. *//* * Process a multi-line text message. */voidSNPPServer::dataCmd(void){ if (!haveText) { fxStr emsg; u_int seqnum = getDocumentNumber(emsg); if (seqnum == (u_int) -1) { reply(554, "%s", (const char*)emsg); return; } msgFile = fxStr::format("/%s/doc%u.page", FAX_TMPDIR, seqnum); FILE* fout = Sys::fopen(msgFile, "w"); if (fout != NULL) { setFileOwner(msgFile); FileCache::chmod(msgFile, 0660); // sync cache tempFiles.append(msgFile); reply(354, "Begin message input; end with <CRLF>'.'<CRLF>."); char buf[1024]; u_int len = 0; for (;;) { if (getCmdLine(buf, sizeof (buf), true)) { const char* bp = buf; if (bp[0] == '.') { if ((bp[1] == '\n' && bp[2] == '\0') || bp[1] == '\0') break; if (bp[1] == '.' && bp[2] == '\0') // .. -> . bp++; } u_int blen = strlen(bp); if ((len += blen) > maxMsgLength) { reply(550, "Error, message too long; max %u characters.", maxMsgLength); (void) fclose(fout); Sys::unlink(msgFile); return; } // if buf only contains .., we skipped the first . // hence we must write bp, not buf. also blen is // the length of bp, not buf. (void) fwrite((const char*) bp, blen, 1, fout); } } if (fclose(fout)) { perror_reply(554, msgFile, errno); Sys::unlink(msgFile); } else { haveText = true; reply(250, "Message text OK."); } } else perror_reply(554, msgFile, errno); } else reply(503, "Error, message already entered.");}/* * Provide help. We cannot share the base class * implementation of this command because SNPP * defines a different style for responses (sigh). */voidSNPPServer::helpCmd(const tab* ctab, const char* s){ const char* type; u_int NCMDS; if (ctab == sitetab) { type = "SITE "; NCMDS = N(sitetab); } else { type = ""; NCMDS = N(cmdtab); } int width = 0; const tab* c = ctab; for (u_int n = NCMDS; n != 0; c++, n--) { int len = strlen(c->name); if (len > width) width = len; } width = (width + 8) &~ 7; if (s == NULL) { reply(214, "The following %scommands are recognized %s.", type, "(* =>'s unimplemented)"); int columns = 76 / width; if (columns == 0) columns = 1; int lines = (NCMDS + columns - 1) / columns; for (int i = 0; i < lines; i++) { printf("214 "); for (int j = 0; j < columns; j++) { c = &ctab[j*lines + i]; printf("%s%c", c->name, !c->implemented ? '*' : ' '); if (c + lines >= &ctab[NCMDS]) break; int w = strlen(c->name) + 1; while (w < width) { putchar(' '); w++; } } printf("\r\n"); } (void) fflush(stdout); reply(250, "Direct comments to %s.", (const char*) faxContact); } else { c = lookup(ctab, NCMDS, s); if (c == NULL) reply(550, "Unknown command %s.", s); else if (c->implemented) reply(218, "Syntax: %s%s %s", type, c->name, c->help); else reply(218, "%s%-*s\t%s; unimplemented.", type, width, c->name, c->help); }}/* * Specify the hold time (time to send) for a request. */voidSNPPServer::holdCmd(time_t when){
⌨️ 快捷键说明
复制代码
Ctrl + C
搜索代码
Ctrl + F
全屏模式
F11
切换主题
Ctrl + Shift + D
显示快捷键
?
增大字号
Ctrl + =
减小字号
Ctrl + -