// Copyright (c) 2011 The Chromium Authors. All rights reserved. // Use of this source code is governed by a BSD-style license that can be // found in the LICENSE file. #include "net/ftp/ftp_ctrl_response_buffer.h" #include #include "base/bind.h" #include "base/logging.h" #include "net/base/parse_number.h" #include "base/strings/string_piece.h" #include "base/values.h" #include "net/base/net_errors.h" #include "net/log/net_log_event_type.h" namespace net { // static const int FtpCtrlResponse::kInvalidStatusCode = -1; FtpCtrlResponse::FtpCtrlResponse() : status_code(kInvalidStatusCode) {} FtpCtrlResponse::FtpCtrlResponse(const FtpCtrlResponse& other) = default; FtpCtrlResponse::~FtpCtrlResponse() = default; FtpCtrlResponseBuffer::FtpCtrlResponseBuffer(const NetLogWithSource& net_log) : multiline_(false), net_log_(net_log) {} FtpCtrlResponseBuffer::~FtpCtrlResponseBuffer() = default; int FtpCtrlResponseBuffer::ConsumeData(const char* data, int data_length) { buffer_.append(data, data_length); ExtractFullLinesFromBuffer(); while (!lines_.empty()) { ParsedLine line = lines_.front(); lines_.pop(); if (multiline_) { if (!line.is_complete || line.status_code != response_buf_.status_code) { line_buf_.append(line.raw_text); continue; } response_buf_.lines.push_back(line_buf_); line_buf_ = line.status_text; DCHECK_EQ(line.status_code, response_buf_.status_code); if (!line.is_multiline) { response_buf_.lines.push_back(line_buf_); responses_.push(response_buf_); // Prepare to handle following lines. response_buf_ = FtpCtrlResponse(); line_buf_.clear(); multiline_ = false; } } else { if (!line.is_complete) return ERR_INVALID_RESPONSE; response_buf_.status_code = line.status_code; if (line.is_multiline) { line_buf_ = line.status_text; multiline_ = true; } else { response_buf_.lines.push_back(line.status_text); responses_.push(response_buf_); // Prepare to handle following lines. response_buf_ = FtpCtrlResponse(); line_buf_.clear(); } } } return OK; } namespace { std::unique_ptr NetLogFtpCtrlResponseCallback( const FtpCtrlResponse* response, NetLogCaptureMode capture_mode) { std::unique_ptr lines(new base::ListValue()); lines->AppendStrings(response->lines); std::unique_ptr dict(new base::DictionaryValue()); dict->SetInteger("status_code", response->status_code); dict->Set("lines", std::move(lines)); return std::move(dict); } } // namespace FtpCtrlResponse FtpCtrlResponseBuffer::PopResponse() { FtpCtrlResponse result = responses_.front(); responses_.pop(); net_log_.AddEvent(NetLogEventType::FTP_CONTROL_RESPONSE, base::Bind(&NetLogFtpCtrlResponseCallback, &result)); return result; } FtpCtrlResponseBuffer::ParsedLine::ParsedLine() : has_status_code(false), is_multiline(false), is_complete(false), status_code(FtpCtrlResponse::kInvalidStatusCode) { } FtpCtrlResponseBuffer::ParsedLine::ParsedLine(const ParsedLine& other) = default; // static FtpCtrlResponseBuffer::ParsedLine FtpCtrlResponseBuffer::ParseLine( const std::string& line) { ParsedLine result; if (line.length() >= 3) { if (ParseInt32(base::StringPiece(line.begin(), line.begin() + 3), ParseIntFormat::NON_NEGATIVE, &result.status_code)) { result.has_status_code = (100 <= result.status_code && result.status_code <= 599); } if (result.has_status_code && line.length() >= 4 && line[3] == ' ') { result.is_complete = true; } else if (result.has_status_code && line.length() >= 4 && line[3] == '-') { result.is_complete = true; result.is_multiline = true; } } if (result.is_complete) { result.status_text = line.substr(4); } else { result.status_text = line; } result.raw_text = line; return result; } void FtpCtrlResponseBuffer::ExtractFullLinesFromBuffer() { int cut_pos = 0; for (size_t i = 0; i < buffer_.length(); i++) { if (i >= 1 && buffer_[i - 1] == '\r' && buffer_[i] == '\n') { lines_.push(ParseLine(buffer_.substr(cut_pos, i - cut_pos - 1))); cut_pos = i + 1; } } buffer_.erase(0, cut_pos); } } // namespace net