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 <utility>
8
9 #include "base/logging.h"
10 #include "base/strings/string_piece.h"
11 #include "base/values.h"
12 #include "net/base/net_errors.h"
13 #include "net/base/parse_number.h"
14 #include "net/log/net_log_event_type.h"
15 #include "net/log/net_log_values.h"
16
17 namespace net {
18
19 // static
20 const int FtpCtrlResponse::kInvalidStatusCode = -1;
21
FtpCtrlResponse()22 FtpCtrlResponse::FtpCtrlResponse() : status_code(kInvalidStatusCode) {}
23
24 FtpCtrlResponse::FtpCtrlResponse(const FtpCtrlResponse& other) = default;
25
26 FtpCtrlResponse::~FtpCtrlResponse() = default;
27
FtpCtrlResponseBuffer(const NetLogWithSource & net_log)28 FtpCtrlResponseBuffer::FtpCtrlResponseBuffer(const NetLogWithSource& net_log)
29 : multiline_(false), net_log_(net_log) {}
30
31 FtpCtrlResponseBuffer::~FtpCtrlResponseBuffer() = default;
32
ConsumeData(const char * data,int data_length)33 int FtpCtrlResponseBuffer::ConsumeData(const char* data, int data_length) {
34 buffer_.append(data, data_length);
35 ExtractFullLinesFromBuffer();
36
37 while (!lines_.empty()) {
38 ParsedLine line = lines_.front();
39 lines_.pop();
40
41 if (multiline_) {
42 if (!line.is_complete || line.status_code != response_buf_.status_code) {
43 line_buf_.append(line.raw_text);
44 continue;
45 }
46
47 response_buf_.lines.push_back(line_buf_);
48
49 line_buf_ = line.status_text;
50 DCHECK_EQ(line.status_code, response_buf_.status_code);
51
52 if (!line.is_multiline) {
53 response_buf_.lines.push_back(line_buf_);
54 responses_.push(response_buf_);
55
56 // Prepare to handle following lines.
57 response_buf_ = FtpCtrlResponse();
58 line_buf_.clear();
59 multiline_ = false;
60 }
61 } else {
62 if (!line.is_complete)
63 return ERR_INVALID_RESPONSE;
64
65 response_buf_.status_code = line.status_code;
66 if (line.is_multiline) {
67 line_buf_ = line.status_text;
68 multiline_ = true;
69 } else {
70 response_buf_.lines.push_back(line.status_text);
71 responses_.push(response_buf_);
72
73 // Prepare to handle following lines.
74 response_buf_ = FtpCtrlResponse();
75 line_buf_.clear();
76 }
77 }
78 }
79
80 return OK;
81 }
82
83 namespace {
84
NetLogFtpCtrlResponseParams(const FtpCtrlResponse * response)85 base::Value NetLogFtpCtrlResponseParams(const FtpCtrlResponse* response) {
86 base::ListValue lines;
87 for (const auto& line : response->lines)
88 lines.Append(NetLogStringValue(line));
89
90 base::DictionaryValue dict;
91 dict.SetInteger("status_code", response->status_code);
92 dict.SetKey("lines", std::move(lines));
93 return std::move(dict);
94 }
95
96 } // namespace
97
PopResponse()98 FtpCtrlResponse FtpCtrlResponseBuffer::PopResponse() {
99 FtpCtrlResponse result = responses_.front();
100 responses_.pop();
101
102 net_log_.AddEvent(NetLogEventType::FTP_CONTROL_RESPONSE,
103 [&] { return NetLogFtpCtrlResponseParams(&result); });
104
105 return result;
106 }
107
ParsedLine()108 FtpCtrlResponseBuffer::ParsedLine::ParsedLine()
109 : has_status_code(false),
110 is_multiline(false),
111 is_complete(false),
112 status_code(FtpCtrlResponse::kInvalidStatusCode) {
113 }
114
115 FtpCtrlResponseBuffer::ParsedLine::ParsedLine(const ParsedLine& other) =
116 default;
117
118 // static
ParseLine(const std::string & line)119 FtpCtrlResponseBuffer::ParsedLine FtpCtrlResponseBuffer::ParseLine(
120 const std::string& line) {
121 ParsedLine result;
122
123 if (line.length() >= 3) {
124 if (ParseInt32(base::StringPiece(line.begin(), line.begin() + 3),
125 ParseIntFormat::NON_NEGATIVE, &result.status_code)) {
126 result.has_status_code =
127 (100 <= result.status_code && result.status_code <= 599);
128 }
129 if (result.has_status_code && line.length() >= 4 && line[3] == ' ') {
130 result.is_complete = true;
131 } else if (result.has_status_code && line.length() >= 4 && line[3] == '-') {
132 result.is_complete = true;
133 result.is_multiline = true;
134 }
135 }
136
137 if (result.is_complete) {
138 result.status_text = line.substr(4);
139 } else {
140 result.status_text = line;
141 }
142
143 result.raw_text = line;
144
145 return result;
146 }
147
ExtractFullLinesFromBuffer()148 void FtpCtrlResponseBuffer::ExtractFullLinesFromBuffer() {
149 int cut_pos = 0;
150 for (size_t i = 0; i < buffer_.length(); i++) {
151 if (i >= 1 && buffer_[i - 1] == '\r' && buffer_[i] == '\n') {
152 lines_.push(ParseLine(buffer_.substr(cut_pos, i - cut_pos - 1)));
153 cut_pos = i + 1;
154 }
155 }
156 buffer_.erase(0, cut_pos);
157 }
158
159 } // namespace net
160