📄 sftpc.cpp
字号:
StreamInputSource &src, StreamOutputDest &dest, FromCRLFTranslator translator){ int bareCRs=0, bareLFs=0; translator(src, dest, bareCRs, bareLFs); if (bareCRs != 0 || bareLFs != 0) { cout << "warning: Data had " << bareCRs << " bare CRs and " << bareLFs << " bare LFs.\n" << " (Confusing? Try \"help crlf\".)\n"; }}STATICDEF void SFTPC::LF_to_CRLF( StreamInputSource &src, StreamOutputDest &dest){ int CRs=0; ::LF_to_CRLF(src, dest, CRs); if (CRs != 0) { cout << "warning: Data had " << CRs << " CRs.\n" << " (Confusing? Try \"help crlf\".)\n"; }}SFTPC::CRLFTranslator SFTPC::getCRLFTranslator(bool fromCRLF){ if (localConventionIsCRLF) { diagnostic("using CRLF_to_CRLF"); return &CRLF_to_CRLF; } else { if (fromCRLF) { diagnostic("using CRLF_to_LF"); return &CRLF_to_LF; } else { diagnostic("using LF_to_CRLF"); return &LF_to_CRLF; } }}// copy data from a socket to a file// handles ascii and binary transfers// returns number of bytes received over networkint SFTPC::copyFromSocketToStream( SOCKET sourceSocket, StreamOutputDest &destStream, bool asciiMode){ if (asciiMode) { // must convert from CRLF to native // funky source int totlen=0; EncryptedInputStreamer sourceStream(*this, sourceSocket, totlen); // call Flex-generated scanner to do translation CRLFTranslator translator = getCRLFTranslator(true /*fromCRLF*/); translator(sourceStream, destStream); return totlen; } else { // straight copying int totlen=0; for(;;) { int len = readEitherWay(sourceSocket); if (len == 0) { // end of file break; } destStream.write((char const*)dataBuffer.getDataC(), len); totlen+=len; } return totlen; }}// copy data from a file to a socket// handles ascii and binary transfers// returns number of bytes sent over networkint SFTPC::copyFromStreamToSocket( StreamInputSource &sourceStream, SOCKET destSocket, bool asciiMode){ // calculate how much cleartext we can get int cleartextLen; if (isDataChannelEncrypted()) { cleartextLen = security-> data().maximumCleartextSizeForBlock(PBSZ); } else { cleartextLen = UNENCRYPTED_BUFSIZE; } diagnostic("cleartextLen is " << cleartextLen); if (asciiMode) { // must convert from native to CRLF // funky dest int totlen=0; { // inside braces to be sure of dtor order, since the dtor does // protocol stuff EncryptedOutputStreamer destStream( *this, destSocket, cleartextLen, totlen); // call Flex-generated scanner to do translation CRLFTranslator translator = getCRLFTranslator(false /*fromCRLF*/); translator(sourceStream, destStream); } return totlen; } else { // straight copying // copy data from the file to the socket int totlen=0; for(;;) { // read from file xassert(cleartextLen <= dataBuffer.getAllocated()); int len = sourceStream.read((char*)dataBuffer.getData(), cleartextLen); dataBuffer.setDataLen(len); // write to socket writeEitherWay(destSocket); totlen += len; if (len == 0) { // eof break; } } return totlen; }}// read data from s into dataBuffervoid SFTPC::readCleartext(SOCKET socket){ recvAllToEOFBlock(socket, dataBuffer, dataBuffer.getAllocated());}// write data from dataBuffer to svoid SFTPC::writeCleartext(SOCKET socket){ sendAll(socket, (char const*)dataBuffer.getDataC(), dataBuffer.getDataLen());}// read data from 'socket', decrypt it into dataBuffervoid SFTPC::readAndDecrypt(SOCKET socket){ // read block size long blockSize = recvNBO32(socket); xassert(blockSize <= PBSZ); // read that much data recvAllBlock(socket, dataBuffer, blockSize); // decrypt data xassert(security); security->data().decode(dataBuffer);}// encrypt the data in dataBuffer, send it to 'socket'void SFTPC::encryptAndWrite(SOCKET socket){ // encrypt data xassert(security); security->data().encode(dataBuffer); // send block size long blockSize = dataBuffer.getDataLen(); xassert(blockSize <= PBSZ); sendNBO32(socket, blockSize); // send the data sendAll(socket, (char const*)dataBuffer.getDataC(), blockSize);}// auto-multiplexingint SFTPC::readEitherWay(SOCKET socket){ if (isDataChannelEncrypted()) { readAndDecrypt(socket); } else { readCleartext(socket); } return dataBuffer.getDataLen();}void SFTPC::writeEitherWay(SOCKET socket){ if (isDataChannelEncrypted()) { encryptAndWrite(socket); } else { writeCleartext(socket); }}// ---------------- multiple-file commands ----------------------void SFTPC::mlsCommand(char const *pattern){ // get strings that match the pattern Queue<string> queue = getRemoteNames(pattern); // process each file in the queue while (!queue.isEmpty()) { // grab name string name = queue.dequeue(); // print it cout << name << endl; }}void SFTPC::mgetCommand(char const *pattern){ // get strings that match the pattern Queue<string> queue = getRemoteNames(pattern); // process them multipleFileCommand(queue, CMD_RETR);}void SFTPC::mputCommand(char const *pattern){ // get strings that match the pattern Queue<string> queue = getLocalNames(pattern); // process them multipleFileCommand(queue, CMD_STOR);}void SFTPC::mdeleteCommand(char const *pattern){ // get strings that match the pattern Queue<string> queue = getRemoteNames(pattern); // process them multipleFileCommand(queue, CMD_DELE);}// get a single-character responsechar SFTPC::getOneChar(char const *prompt, char const *allowable){ // write prompt cout << prompt; cout.flush(); // read single character setRawMode(true); // terminal buffering off for(;;) { char ch = getConsoleChar(); if (strchr(allowable, ch)) { // ok setRawMode(false); cout << ch << endl; // echo what we accepted return ch; } if (ch == 3) { // in raw mode, I *do* see ^C, at least on some systems setRawMode(false); xfailure("user typed ^C"); // if I see it, bail } // beeping the terminal might be an option here, but whatever }}void SFTPC::multipleFileCommand(Queue<string> &queue, RequestCommand cmd){ // process each file in the queue bool localOverride = false; while (!queue.isEmpty()) { // grab name string name = queue.dequeue(); // print it cout << name; // prompt (if user wants it) if (interactivePrompting && !localOverride) { char ch = getOneChar(" ((y)es/(n)o/yes to (a)ll/(q)uit)? ", "ynaq"); if (ch == 'n') { continue; // go to next name } if (ch == 'q') { return; // stop altogether } if (ch == 'a') { localOverride = true; // and proceed with this transfer } } else { cout << endl; } // do the data transfer for (;;) { // poor-man's goto label try { if (cmd != CMD_DELE) { dataTransfer(cmd, name, name /*local name, if needed*/); } else { checkedRequest(cmd, name); } } catch (xReply &) { // the message will already have been printed, so just prompt // (can't use a goto here because of initialization of 'x' stuff) switch (getOneChar("(c)ontinue, (a)bort, or (r)etry? ", "car")) { case 'a': return; // bail completely case 'r': continue; // go to top of loop } } catch (xBase &x) { // the justification for all this mechanism? when we are using // remote-side globbing, we sometimes get names like "a/b"; this // can be useful, but if the directory "a" doesn't exist, it will // fail; so this loop lets the user go make the directory, then // proceed cout << x << endl; switch (getOneChar("(c)ontinue, (a)bort, or (r)etry? ", "car")) { case 'a': return; // bail completely case 'r': continue; // go to top of loop } } break; } }}Queue<string> SFTPC::getRemoteNames(char const *pattern){ // PORT, etc. SOCKET dataChannel = startDataTransfer(CMD_NLST, localGlobbing? (char const*)NULL : pattern); diagnostic("waiting for directory listing"); // line parser int networkBytes = 0; EncryptedInputStreamer stream(*this, dataChannel, networkBytes); StreamLineReader liner(stream); // add all the strings to the queue Queue<string> queue; string s; while (liner.getNextLine(s)) { //diagnostic("enqueueing: " << s); queue.enqueue(s); } diagnostic("received entire directory listing (" << queue.count() << " names, " << networkBytes << " bytes)"); // close socket, and check 226 reply close_socket(dataChannel); readAndCheckFinalReplyCode(); // filter based on pattern (if local globbing) if (localGlobbing) { filterStrings(queue, pattern); // quick feedback cout << queue.count() << " names match the pattern\n"; } else { cout << queue.count() << " names returned\n"; } return queue;}void SFTPC::filterStrings(Queue<string> &queue, char const *pattern){ // move queue elements into source queue Queue<string> src(queue); queue.empty(); // process them in order while (!src.isEmpty()) { string name = src.dequeue(); if (glob(name, pattern)) { queue.enqueue(name); } }}STATICDEF bool SFTPC::getLocalNamesHelper( char const *fname, void *extra){ Queue<string> *q = (Queue<string>*)extra; if (fileOrDirectoryExists(fname) && // we need this check because some programs (emacs..) create symlinks // that don't link anywhere valid; calling isDirectory on such a file // will fail (and throw an exception, since sftpc installs an // exception-throwing 'fail' routine); but fileOrDirectoryExists simply // returns false when stat(2) fails, so this safely avoids the // exception (could also just catch the exception.. I'm undecided on // which is best) !isDirectory(fname)) { q->enqueue(fname); } return true; // continue;}Queue<string> SFTPC::getLocalNames(char const *pattern){ // retrieve all local names Queue<string> ret; applyToCwdContents(getLocalNamesHelper, &ret); // filter based on pattern filterStrings(ret, pattern); cout << ret.count() << " file names match the pattern\n"; return ret;}// ------------- automatic online testing ------------------// test binary-mode transfervoid SFTPC::testBinaryTransfers(){ // binary transfer mode TransferState bin(false /*ascii*/, false /*localcrlf*/, false /*binaryAnyway*/); // small but pathological static char const smal[] = "\0abc\n\r\r\n\xff\xfe\x01\x02\0\tyadda smacker\0\0\0foo"; DataBlock smallBlock(smal, sizeof(smal)-1); testSendReceiveBlocks(smallBlock, bin, smallBlock, bin); // medium and large sizes, simple structure int sizes[2] = { 2345, 480000 }; loopi(TABLESIZE(sizes)) { // construct data int sz = sizes[i]; DataBlock block(sz); loopj(sz) { block.getData()[j] = (byte)j; } block.setDataLen(sz); // send/recv testSendReceiveBlocks(block, bin, block, bin); }}// test text-mode transfervoid SFTPC::testTextTransfers(){
⌨️ 快捷键说明
复制代码
Ctrl + C
搜索代码
Ctrl + F
全屏模式
F11
切换主题
Ctrl + Shift + D
显示快捷键
?
增大字号
Ctrl + =
减小字号
Ctrl + -