Home | History | Annotate | Download | only in ftp
      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