📄 snppserver.c++
字号:
time_t now = Sys::now(); if (when > now) { defJob.tts = when; const struct tm* tm = cvtTime(defJob.tts); reply(250, "Message will be processed at %02d%02d%02d%02d%02d." , tm->tm_year , tm->tm_mon+1 , tm->tm_mday , tm->tm_hour , tm->tm_min ); } else reply(550, "Invalid delivery date/time; time in the past.");}/* * Login as the specified user. */voidSNPPServer::loginCmd(const char* loginID, const char* pass){ if (IS(LOGGEDIN)) end_login(); the_user = loginID; state &= ~S_PRIVILEGED; adminwd = "*"; // make sure no admin privileges passwd = "*"; // just in case... if (checkUser(loginID)) { if (passwd != "") { if (pass[0] == '\0' || !(streq(crypt(pass, passwd), passwd) || pamCheck(the_user, pass))) { if (++loginAttempts >= maxLoginAttempts) { reply(421, "Login incorrect (closing connection)."); logNotice("Repeated SNPP login failures for user %s from %s [%s]" , (const char*) the_user , (const char*) remotehost , (const char*) remoteaddr ); dologout(0); } reply(550, "Login incorrect."); logInfo("SNPP login failed from %s [%s], %s" , (const char*) remotehost , (const char*) remoteaddr , (const char*) the_user ); return; } } login(250); } else { if (++loginAttempts >= maxLoginAttempts) { reply(421, "Login incorrect (closing connection)."); logNotice("Repeated SNPP login failures for user %s from %s [%s]" , (const char*) the_user , (const char*) remotehost , (const char*) remoteaddr ); dologout(0); } else { reply(421, "User %s access denied.", (const char*) the_user); logNotice("SNPP LOGIN REFUSED (%s) FROM %s [%s], %s" , "user denied" , (const char*) remotehost , (const char*) remoteaddr , (const char*) the_user ); } }}/* * Specify a one-line message text. */voidSNPPServer::messageCmd(const char* msg){ if (!haveText) { u_int len = strlen(msg); if (len > maxMsgLength) { reply(550, "Error, message too long; no more than %u characters accepted.", maxMsgLength); return; } 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); (void) fwrite(msg, len, 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.");}/* * Process a PAGE command. Map the client-specified pager * identification string to a service provider phone number * and destination PIN and create a new job for this request. */voidSNPPServer::pagerCmd(const char* pagerID, const char* pin){ /* * Lookup pager ID and map to a service provider * and, optionally, PIN. */ fxStr provider; fxStr PIN; fxStr emsg; if (!mapPagerID(pagerID, provider, PIN, emsg)) { reply(550, "%s.", (const char*) emsg); return; } /* * RFC 1861 says we should lock the Level, Coverage, * Holdtime, and Alert values for the request at this point. * To do this we create a job (inheriting the current state * from the default job) and fill in any other information. * However we do not submit it until later (when a SEND * request is made). */ curJob = &defJob; // inherit from default job // XXX merge items to same provider (maybe?) if (newJob(emsg) && updateJobOnDisk(*curJob, emsg)) { fxStr file("/" | curJob->qfile); setFileOwner(file); // force ownership FileCache::chmod(file, 0660); // sync cache curJob->lastmod = Sys::now(); // noone else should update curJob->number = provider; // destination phone number if (!pin) // use mapped value pin = PIN; curJob->items.append(FaxItem(FaxRequest::send_page, 0, "", pin)); curJob->items.append( FaxItem(FaxRequest::send_page_saved, 0, "", pin)); reply(250, "Pager ID accepted; provider: %s pin: %s jobid: %s." , (const char*) provider , pin , (const char*) curJob->jobid ); msgs.append(curJob->jobid); } else reply(554, "%s.", (const char*) emsg); initSNPPJob(); // reset job-related state}/* * Validate a client-specified pager ID string. * This is only sort of like the intended usage * but is the only thing that makes sense in * our environment (where the provider is not * directly accessible). */voidSNPPServer::pingCmd(const char* pagerID){ /* * Lookup pager ID and map to a service provider * and, optionally, PIN. */ fxStr provider; fxStr PIN; fxStr emsg; if (mapPagerID(pagerID, provider, PIN, emsg)) reply(821, "Valid pager ID, no location information available."); else reply(550, "%s.", (const char*) emsg);}static boolnotPresent(FaxItemArray& a, const char* name){ for (u_int i = 0, n = a.length(); i < n; i++) if (a[i].op == FaxRequest::send_data && a[i].item == name) return (false); return (true);}/* * Send (submit) a pager request. We complete the * formulation of the job and submit it to the * scheduler. If this job is marked for no queueing * then we also wait around for the job to complete. */voidSNPPServer::sendCmd(void){ if (msgs.length() == 0) { reply(503, "Error, no pager ID specified with PAGE."); return; } /* * If we need to wait for jobs to complete, construct * and register a trigger now before we submit the jobs. */ fxStr emsg; u_int i, n = msgs.length(); bool waitForJobs = false; for (i = 0; i < n; i++) { Job* job = findJob(msgs[i], emsg); if (!job) msgs.remove(i), n--; else if (!job->queued) waitForJobs = true; } if (waitForJobs) { state &= ~S_LOGTRIG; // just process events if (!newTrigger(emsg, "J%04x", 1<<Trigger::JOB_DEAD)) { reply(550, "Cannot register trigger to wait for job completion: %s.", (const char*) emsg); return; } } const char* docname = msgFile; const char* cp = strrchr(docname, '/'); if (!cp) // relative name, e.g. doc123 cp = docname; for (i = 0; i < n; i++) { Job* job = findJob(msgs[i], emsg); if (!job) { msgs.remove(i), n--; continue; } if (*cp != '\0') { /* * Add a reference to the message text for * the current job, if not already present. */ fxStr document = fxStr::format("/" FAX_DOCDIR "%s.", cp) | job->jobid; if (notPresent(job->items, &document[1])) { if (Sys::link(docname, document) < 0) { reply(554, "Unable to link document %s to %s: %s.", docname, (const char*) document, strerror(errno)); return; } job->items.append( FaxItem(FaxRequest::send_data, 0, "", &document[1])); } } /* * Submit the job for scheduling. */ if (job->mailaddr == "") replyBadJob(*job, T_NOTIFYADDR); else if (job->sender == "") replyBadJob(*job, T_FROM_USER); else if (job->modem == "") replyBadJob(*job, T_MODEM); else if (job->client == "") replyBadJob(*job, T_CLIENT); else { if (job->external == "") job->external = job->number; if (job->tts == 0) job->tts = Sys::now(); job->killtime += job->tts; // adjust based on any hold time if (updateJobOnDisk(*job, emsg)) { const char* jobid = job->jobid; /* * NB: we don't mark the lastmod time for the * job since the scheduler should re-write the * queue file to reflect what it did with it * (e.g. what state it placed the job in). */ if (sendQueuerACK(emsg, "S%s", jobid)) continue; reply(554, "Failed to submit message %s: %s.", jobid, (const char*) emsg); } else reply(554, "%s.", (const char*) emsg); } if (waitForJobs) { // cleanup trigger on error cancelTrigger(emsg); /* * Not sure what to do here. If some jobs got * submitted then we want to remove them so the * client can just reissue SEND if the error was * transient (e.g. faxq was temporarily stopped). * However we don't track which jobs got started * and which did not so for now we'll just leave * everything so the client can resubmit things * with a subsequent SEND. Unfortunately this * can result in duplicate pages being sent. */ } return; // failure } if (waitForJobs) { // no queueing, wait for submitted jobs if (setjmp(urgcatch) == 0) { Dispatcher& disp = Dispatcher::instance(); state |= S_WAITTRIG|S_SENDWAIT; bool jobsPending; do { disp.dispatch(); /* * The trigger event handlers update our notion * of the job state asynchronously so we can just * monitor the job state(s) after each event we * receive. */ jobsPending = false; for (i = 0; i < n; i++) { Job* job = findJob(msgs[i], emsg); if (!job) msgs.remove(i), n--; else if (!job->queued && job->state != FaxRequest::state_done && job->state != FaxRequest::state_failed) jobsPending = true; } } while (IS(WAITTRIG) && jobsPending); reply(250, "Message processing completed."); } state &= ~(S_WAITTRIG|S_SENDWAIT); (void) cancelTrigger(emsg); } else // jobs queued, just acknowledge reply(250, "Message%s succesfully queued.", msgs.length() > 1 ? "s" : ""); resetState(); // reset SEND-related state}/* * Set the message service level. We currently use * this just to select a scheduling priority and * job expiration time. */voidSNPPServer::serviceLevel(u_int level){ if (level <= 11) { defJob.usrpri = priMap[level]; defJob.killtime = 60*killMap[level]; defJob.retrytime = retryMap[level]; reply(250, "OK, service level %u accepted.", level); } else reply(550, "Invalid service level %u; we only handle 0-11.", level);}/* * Return server status. */voidSNPPServer::statusCmd(void){ reply(214, "%s SNPP server status:", (const char*) hostname); reply(214, " %s", version); if (!isdigit(remotehost[0])) reply(214, " Connected to %s (%s).", (const char*) remotehost, (const char*) remoteaddr); else reply(214, " Connected to %s.", (const char*) remotehost); if (IS(LOGGEDIN)) { reply(214, " Logged in as user %s (uid %u)." , (const char*) the_user , uid ); } else reply(214, " Waiting for login."); reply(214, " Idle timeout set to %d seconds.", idleTimeout); if (discTime > 0) reply(214, " Server scheduled to be unavailable at %.24s.", asctime(cvtTime(discTime))); else reply(214, " No server down time currently scheduled."); reply(214, " HylaFAX scheduler reached at %s (%sconnected)." , (const char*) faxqFIFOName , faxqFd == -1 ? "not " : "" ); if (clientFd != -1) reply(214, " Server FIFO is /%s (%sopen)." , (const char*) clientFIFOName , clientFd == -1 ? "not " : "" ); if (IS(WAITFIFO)) reply(214, " Waiting for response from HylaFAX scheduler."); if (msgs.length() > 0) { reply(214, " %u message%s prepared for transmission.", msgs.length(), msgs.length() > 1 ? "s" : ""); // XXX dump status of msgs } else reply(214, " No messages prepared for transmission."); reply(214, " %s message text.", haveText ? "Received" : "No received"); reply(250, "End of status");}/* * Set the subject for outgoing messages. * For now we just use it to tag the jobs. */voidSNPPServer::subjectCmd(const char* subj){ defJob.jobtag = subj; // XXX reply(250, "Message subject OK.");}/* * Configuration support. */voidSNPPServer::resetConfig(){ InetFaxServer::resetConfig(); setupConfig();}voidSNPPServer::setupConfig(){ setConfigItem("maxmsglength", "128"); setConfigItem("pageridmapfile","/etc/pagermap"); setConfigItem("prioritymap", "63 127 127 127 127 127 127 127 127 127 127 127"); setConfigItem("killtimemap", " 5 5 5 15 60 240 720 1440 1440 1440 1440 1440"); setConfigItem("retrytimemap", "30 60 60 180 0 0 0 0 0 0 0 0");}static voidsetShortMap(u_short map[12], const char* value){ char* cp; for (int i = 0; i < 12; i++) { map[i] = (u_short) strtoul(value, &cp, 0); if (!cp && *cp == '\0') break; value = cp; }}static voidsetTimeMap(time_t map[12], const char* value){ char* cp; for (int i = 0; i < 12; i++) { map[i] = (time_t) strtoul(value, &cp, 0); if (!cp && *cp == '\0') break; value = cp; }}boolSNPPServer::setConfigItem(const char* tag, const char* value){ if (streq(tag, "maxmsglength")) { maxMsgLength = getNumber(value); } else if (streq(tag, "pageridmapfile")) { pagerIDMapFile = value; } else if (streq(tag, "prioritymap")) { setShortMap(priMap, value); } else if (streq(tag, "killtimemap")) { setTimeMap(killMap, value); } else if (streq(tag, "retrytimemap")) { setTimeMap(retryMap, value); } else if (!InetFaxServer::setConfigItem(tag, value)) return (false); return (true);}#endif /* SNPP_SUPPORT */
⌨️ 快捷键说明
复制代码
Ctrl + C
搜索代码
Ctrl + F
全屏模式
F11
切换主题
Ctrl + Shift + D
显示快捷键
?
增大字号
Ctrl + =
减小字号
Ctrl + -