📄 reply.cpp
字号:
// reply.cc// code for reply.h// copyright SafeTP Development Group, Inc., 2000 Terms of use are as specified in license.txt#include <ctype.h> // isdigit#include <stdlib.h> // atoi#include <stdio.h> // printf#include <string.h> // memcmp#include "xassert.h" // xassert#include "sockutil.h" // socket funcs#include "reply.h" // this module DOB: leave this line here (solves client include dependency problem)#include "security.h" // xSecurity#ifndef SAFETPC# include "lineread.h" // StreamLineReader#endif// not a truly general decimal-wise OR, but works// when 'mask' is a power of 10static unsigned decimalwiseOR(unsigned x, unsigned mask){ return x / mask * mask % (mask * 10);}// test that x has just one decimal digit, namely// the one set to 1 in maskstatic bool isOneDigit(unsigned x, unsigned mask){ return decimalwiseOR(x, mask) == x;}bool valid(ReplyCodeFirst f){ return RCF_POSITIVE_PRELIMINARY <= f && f <= RCF_ENCRYPTED && isOneDigit(f, 100);}bool valid(ReplyCodeSecond f){ return RCS_SYNTAX <= f && f <= RCS_FILE_SYSTEM && isOneDigit(f, 10);}bool valid(ReplyCodeThird t){ return isOneDigit(t, 1);}bool valid(ReplyCode c){ // check constituents.. don't like the code // duplication here but I don't want to spend // all day here return valid((ReplyCodeFirst)decimalwiseOR(c, 100)) && valid((ReplyCodeSecond)decimalwiseOR(c, 10)) && valid((ReplyCodeThird)decimalwiseOR(c, 1));}ReplyCodeFirst first(ReplyCode code){ ReplyCodeFirst ret = (ReplyCodeFirst) decimalwiseOR(code, 100); xassert(valid(ret)); return ret;}ReplyCodeSecond second(ReplyCode code){ ReplyCodeSecond ret = (ReplyCodeSecond) decimalwiseOR(code, 10); xassert(valid(ret)); return ret;}ReplyCodeThird third(ReplyCode code){ ReplyCodeThird ret = (ReplyCodeThird) decimalwiseOR(code, 1); xassert(valid(ret)); // shouldn't be able to fail for any input return ret;}ReplyCode makeCode(ReplyCodeFirst f, ReplyCodeSecond s, ReplyCodeThird t){ ReplyCode ret = ReplyCode(f + s + t); xassert(valid(ret)); return ret;}// returns true when s begins with a three-digit// reply code, and either legal separatorbool replyCodeStart(char const *s){ return isdigit(s[0]) && isdigit(s[1]) && isdigit(s[2]) && (s[3]==' ' || s[3]=='-');}Reply::Reply(ReplyCode replyCode, char const *firstLine) : code(replyCode){ init(); append(firstLine);}Reply::Reply(SOCKET s){ init(); parse(&Reply::socketLineGetter, (void*&)s);}// extract CRLF-delimited lines from a socketSTATICDEF string Reply::socketLineGetter(void *&arg){ SOCKET s = (SOCKET)arg; return recvLine(s);}Reply::Reply(char const *&text){ init(); parse(&Reply::bufferLineGetter, (void*&)text);}// Parse a reply in a buffer. Multiline replies have lines separated by// CRLF, but the last line can either be terminated by CRLF, OR null ('\0').// We will know where the last line is by the reply code separator syntax;// however, 2228 allows the last CRLF to be missing, in which case we may// have to inspect the nul. This is somewhat undesirable, because without it// we could have been parsing lots of concatenated replies. Oh well.// NOTE: This function updates the value of 'arg' (the char* passed into the// constructor) to point at the char just beyond the last inspected.STATICDEF string Reply::bufferLineGetter(void *&arg){ char const *&ptr = (char const*&)arg; char const *start = ptr; // find the next CRLF or null char const *end = ptr; while (end[0] != 0 && !( end[0] == '\r' && end[1] == '\n' )) { end++; } // update ptr if (end[0] == 0) { // ends in null (implicitly should be last line, but we don't // check that here) ptr = end + 1; } else { // ends in CRLF; could be last or could be multiline ptr = end + 2; } // return the string, NOT containing the CRLF (if present); is always // null terminated return string(start, end-start);}#ifndef SAFETPCReply::Reply(StreamLineReader &reader){ init(); void *v = (void*)&reader; // g++ warning hack parse(&Reply::streamLineGetter, v);}STATICDEF string Reply::streamLineGetter(void *&arg){ StreamLineReader &reader = *((StreamLineReader*)arg); string ret; if (!reader.getNextLine(ret)) { // EOF.. we aren't expecting this... so, let's // throw an exception xfailure("stream closed unexpectedly"); } return ret;}#endif// read next reply; should be faithful to 959 specvoid Reply::parse(LineGetter getter, void *&arg){ // collect lines of reply for(;;) { string str = getter(arg); // add text to reply, without reply code if (replyCodeStart(str)) { append(str.pcharc()+4); if (str[3] == ' ') { // last line code = (ReplyCode)atoi(str.pcharc()); if (!( RC_MINIMUM <= code && code <= RC_MAXIMUM )) { // given an assumption that malformed data is due to // an attack, we'll throw xSecurity THROW(xSecurity(stringb("illegal reply code: " << (int)code))); } return; } } else { append(str); } }}// first thing in every constructorvoid Reply::init(){ text = alloc(10); numLines = 0; // we do *not* set 'code', because some ctors // set it first and expect it to stick}Reply::Reply(Reply const &obj){ text = alloc(obj.arraySize); numLines = obj.numLines; for (int i=0; i<numLines; i++) { text[i] = new string(*obj.text[i]); } code = obj.code;}// allocate the array and set 'arraySize'string **Reply::alloc(int size){ arraySize = size; string **ret = new string*[arraySize]; for(int i=0; i<arraySize; i++) { ret[i] = NULL; } return ret;}Reply::~Reply(){ for(int i=0; i<numLines; i++) { delete text[i]; } delete[] text;}// this is the only place the array growsvoid Reply::append(char const *s){ if (numLines == arraySize) { // grow string **newtext = alloc(arraySize*2); for(int i=0; i<numLines; i++) { newtext[i] = text[i]; } delete[] text; text = newtext; } text[numLines++] = new string(s);}// and it allvoid Reply::send(SOCKET s) const{ sendAllString(s, getAllText());}// compose the reply; send format is more restrictive// than 959 specstring Reply::getAllText() const{ stringBuilder sb; for (int curLine=0; curLine<numLines; curLine++) { // output the reply code (and ensure only 3 digits) sb << (int)((unsigned)code % 1000); // output separator if (curLine == numLines-1) { // last line sb << ' '; } else { // not last line sb << '-'; } // output line text sb << *text[curLine] << "\r\n"; } return sb;}string Reply::getNthLine(int n) const{ return *text[n];}string Reply::getLastText() const{ if (numLines > 0) { return *text[numLines-1]; } else { return string(""); }}// this is the most suitable format for the client// to apply un-64 and then decodestring Reply::getAllTextAsOneLine() const{ stringBuilder sb; for (int curLine=0; curLine<numLines; curLine++) { // append line text sb << *text[curLine]; } return sb;}bool Reply::containsADAT() const{ // see if the reply contains some data bool replyHasAdat = (code == RC_SECURITY_DATA_EXCHANGE_COMPLETE || code == RC_LAST_ADAT || code == RC_FIRST_ADAT || code == RC_MIDDLE_ADAT) && 0==memcmp(text[0]->pcharc(), "ADAT=", ADATTagLen); return replyHasAdat;}// ----------------- xReply --------------------xReply::xReply(ReplyCode c, char const *t) : xBase(stringb((int)c << " " << t)), code(c), text(t){}xReply::xReply(Reply const &reply) : xBase(stringb((int)(reply.getCode()) << " " << reply.getLastText())), code(reply.getCode()), text(reply.getLastText()){}xReply::xReply(xReply const &obj) : xBase(obj), code(obj.code), text(obj.text){}xReply::~xReply(){}// ------------ test code ---------------------#ifdef TEST_REPLY#include "test.h" // USUAL_MAINvoid entry(){ char const *text = "200-Some stuff\r\n" "200 Second line\r\n"; Reply reply(text); cout << reply.getAllText(); Reply reply2(reply); // test copy ctor cout << reply2.getAllText(); char const *adatReply = "334 ADAT=blah blah\r\n"; Reply aReply(adatReply); xassert(aReply.containsADAT()); cout << "tests passed\n";}USUAL_MAIN#endif // TEST_REPLY
⌨️ 快捷键说明
复制代码
Ctrl + C
搜索代码
Ctrl + F
全屏模式
F11
切换主题
Ctrl + Shift + D
显示快捷键
?
增大字号
Ctrl + =
减小字号
Ctrl + -