1 // Copyright (c) 2011 The Chromium Authors. All rights reserved. 2 // Use of this source code is governed by a BSD-style license that can be 3 // found in the LICENSE file. 4 5 #include "net/ftp/ftp_ctrl_response_buffer.h" 6 7 #include "base/bind.h" 8 #include "base/logging.h" 9 #include "base/strings/string_number_conversions.h" 10 #include "base/strings/string_piece.h" 11 #include "base/values.h" 12 #include "net/base/net_errors.h" 13 14 namespace net { 15 16 // static 17 const int FtpCtrlResponse::kInvalidStatusCode = -1; 18 19 FtpCtrlResponse::FtpCtrlResponse() : status_code(kInvalidStatusCode) {} 20 21 FtpCtrlResponse::~FtpCtrlResponse() {} 22 23 FtpCtrlResponseBuffer::FtpCtrlResponseBuffer(const BoundNetLog& net_log) 24 : multiline_(false), 25 net_log_(net_log) { 26 } 27 28 FtpCtrlResponseBuffer::~FtpCtrlResponseBuffer() {} 29 30 int FtpCtrlResponseBuffer::ConsumeData(const char* data, int data_length) { 31 buffer_.append(data, data_length); 32 ExtractFullLinesFromBuffer(); 33 34 while (!lines_.empty()) { 35 ParsedLine line = lines_.front(); 36 lines_.pop(); 37 38 if (multiline_) { 39 if (!line.is_complete || line.status_code != response_buf_.status_code) { 40 line_buf_.append(line.raw_text); 41 continue; 42 } 43 44 response_buf_.lines.push_back(line_buf_); 45 46 line_buf_ = line.status_text; 47 DCHECK_EQ(line.status_code, response_buf_.status_code); 48 49 if (!line.is_multiline) { 50 response_buf_.lines.push_back(line_buf_); 51 responses_.push(response_buf_); 52 53 // Prepare to handle following lines. 54 response_buf_ = FtpCtrlResponse(); 55 line_buf_.clear(); 56 multiline_ = false; 57 } 58 } else { 59 if (!line.is_complete) 60 return ERR_INVALID_RESPONSE; 61 62 response_buf_.status_code = line.status_code; 63 if (line.is_multiline) { 64 line_buf_ = line.status_text; 65 multiline_ = true; 66 } else { 67 response_buf_.lines.push_back(line.status_text); 68 responses_.push(response_buf_); 69 70 // Prepare to handle following lines. 71 response_buf_ = FtpCtrlResponse(); 72 line_buf_.clear(); 73 } 74 } 75 } 76 77 return OK; 78 } 79 80 namespace { 81 82 base::Value* NetLogFtpCtrlResponseCallback(const FtpCtrlResponse* response, 83 NetLog::LogLevel log_level) { 84 base::ListValue* lines = new base::ListValue(); 85 lines->AppendStrings(response->lines); 86 87 base::DictionaryValue* dict = new base::DictionaryValue(); 88 dict->SetInteger("status_code", response->status_code); 89 dict->Set("lines", lines); 90 return dict; 91 } 92 93 } // namespace 94 95 FtpCtrlResponse FtpCtrlResponseBuffer::PopResponse() { 96 FtpCtrlResponse result = responses_.front(); 97 responses_.pop(); 98 99 net_log_.AddEvent(NetLog::TYPE_FTP_CONTROL_RESPONSE, 100 base::Bind(&NetLogFtpCtrlResponseCallback, &result)); 101 102 return result; 103 } 104 105 FtpCtrlResponseBuffer::ParsedLine::ParsedLine() 106 : has_status_code(false), 107 is_multiline(false), 108 is_complete(false), 109 status_code(FtpCtrlResponse::kInvalidStatusCode) { 110 } 111 112 // static 113 FtpCtrlResponseBuffer::ParsedLine FtpCtrlResponseBuffer::ParseLine( 114 const std::string& line) { 115 ParsedLine result; 116 117 if (line.length() >= 3) { 118 if (base::StringToInt(base::StringPiece(line.begin(), line.begin() + 3), 119 &result.status_code)) 120 result.has_status_code = (100 <= result.status_code && 121 result.status_code <= 599); 122 if (result.has_status_code && line.length() >= 4 && line[3] == ' ') { 123 result.is_complete = true; 124 } else if (result.has_status_code && line.length() >= 4 && line[3] == '-') { 125 result.is_complete = true; 126 result.is_multiline = true; 127 } 128 } 129 130 if (result.is_complete) { 131 result.status_text = line.substr(4); 132 } else { 133 result.status_text = line; 134 } 135 136 result.raw_text = line; 137 138 return result; 139 } 140 141 void FtpCtrlResponseBuffer::ExtractFullLinesFromBuffer() { 142 int cut_pos = 0; 143 for (size_t i = 0; i < buffer_.length(); i++) { 144 if (i >= 1 && buffer_[i - 1] == '\r' && buffer_[i] == '\n') { 145 lines_.push(ParseLine(buffer_.substr(cut_pos, i - cut_pos - 1))); 146 cut_pos = i + 1; 147 } 148 } 149 buffer_.erase(0, cut_pos); 150 } 151 152 } // namespace net 153