1/*
2 *  Copyright 2004 The WebRTC Project Authors. All rights reserved.
3 *
4 *  Use of this source code is governed by a BSD-style license
5 *  that can be found in the LICENSE file in the root of the source
6 *  tree. An additional intellectual property rights grant can be found
7 *  in the file PATENTS.  All contributing project authors may
8 *  be found in the AUTHORS file in the root of the source tree.
9 */
10
11
12#include "webrtc/base/common.h"
13#include "webrtc/base/httpcommon.h"
14#include "webrtc/base/multipart.h"
15
16namespace rtc {
17
18///////////////////////////////////////////////////////////////////////////////
19// MultipartStream
20///////////////////////////////////////////////////////////////////////////////
21
22MultipartStream::MultipartStream(const std::string& type,
23                                 const std::string& boundary)
24    : type_(type),
25      boundary_(boundary),
26      adding_(true),
27      current_(0),
28      position_(0) {
29  // The content type should be multipart/*.
30  ASSERT(0 == strncmp(type_.c_str(), "multipart/", 10));
31}
32
33MultipartStream::~MultipartStream() {
34  Close();
35}
36
37void MultipartStream::GetContentType(std::string* content_type) {
38  ASSERT(NULL != content_type);
39  content_type->assign(type_);
40  content_type->append("; boundary=");
41  content_type->append(boundary_);
42}
43
44bool MultipartStream::AddPart(StreamInterface* data_stream,
45                              const std::string& content_disposition,
46                              const std::string& content_type) {
47  if (!AddPart("", content_disposition, content_type))
48    return false;
49  parts_.push_back(data_stream);
50  data_stream->SignalEvent.connect(this, &MultipartStream::OnEvent);
51  return true;
52}
53
54bool MultipartStream::AddPart(const std::string& data,
55                              const std::string& content_disposition,
56                              const std::string& content_type) {
57  ASSERT(adding_);
58  if (!adding_)
59    return false;
60  std::stringstream ss;
61  if (!parts_.empty()) {
62    ss << "\r\n";
63  }
64  ss << "--" << boundary_ << "\r\n";
65  if (!content_disposition.empty()) {
66    ss << ToString(HH_CONTENT_DISPOSITION) << ": "
67       << content_disposition << "\r\n";
68  }
69  if (!content_type.empty()) {
70    ss << ToString(HH_CONTENT_TYPE) << ": "
71       << content_type << "\r\n";
72  }
73  ss << "\r\n" << data;
74  parts_.push_back(new MemoryStream(ss.str().data(), ss.str().size()));
75  return true;
76}
77
78void MultipartStream::EndParts() {
79  ASSERT(adding_);
80  if (!adding_)
81    return;
82
83  std::stringstream ss;
84  if (!parts_.empty()) {
85    ss << "\r\n";
86  }
87  ss << "--" << boundary_ << "--" << "\r\n";
88  parts_.push_back(new MemoryStream(ss.str().data(), ss.str().size()));
89
90  ASSERT(0 == current_);
91  ASSERT(0 == position_);
92  adding_ = false;
93  SignalEvent(this, SE_OPEN | SE_READ, 0);
94}
95
96size_t MultipartStream::GetPartSize(const std::string& data,
97                                    const std::string& content_disposition,
98                                    const std::string& content_type) const {
99  size_t size = 0;
100  if (!parts_.empty()) {
101    size += 2;  // for "\r\n";
102  }
103  size += boundary_.size() + 4;  // for "--boundary_\r\n";
104  if (!content_disposition.empty()) {
105    // for ToString(HH_CONTENT_DISPOSITION): content_disposition\r\n
106    size += std::string(ToString(HH_CONTENT_DISPOSITION)).size() + 2 +
107        content_disposition.size() + 2;
108  }
109  if (!content_type.empty()) {
110    // for ToString(HH_CONTENT_TYPE): content_type\r\n
111    size += std::string(ToString(HH_CONTENT_TYPE)).size() + 2 +
112        content_type.size() + 2;
113  }
114  size += 2 + data.size();  // for \r\ndata
115  return size;
116}
117
118size_t MultipartStream::GetEndPartSize() const {
119  size_t size = 0;
120  if (!parts_.empty()) {
121    size += 2;  // for "\r\n";
122  }
123  size += boundary_.size() + 6;  // for "--boundary_--\r\n";
124  return size;
125}
126
127//
128// StreamInterface
129//
130
131StreamState MultipartStream::GetState() const {
132  if (adding_) {
133    return SS_OPENING;
134  }
135  return (current_ < parts_.size()) ? SS_OPEN : SS_CLOSED;
136}
137
138StreamResult MultipartStream::Read(void* buffer, size_t buffer_len,
139                                   size_t* read, int* error) {
140  if (adding_) {
141    return SR_BLOCK;
142  }
143  size_t local_read;
144  if (!read) read = &local_read;
145  while (current_ < parts_.size()) {
146    StreamResult result = parts_[current_]->Read(buffer, buffer_len, read,
147                                                 error);
148    if (SR_EOS != result) {
149      if (SR_SUCCESS == result) {
150        position_ += *read;
151      }
152      return result;
153    }
154    ++current_;
155  }
156  return SR_EOS;
157}
158
159StreamResult MultipartStream::Write(const void* data, size_t data_len,
160                                    size_t* written, int* error) {
161  if (error) {
162    *error = -1;
163  }
164  return SR_ERROR;
165}
166
167void MultipartStream::Close() {
168  for (size_t i = 0; i < parts_.size(); ++i) {
169    delete parts_[i];
170  }
171  parts_.clear();
172  adding_ = false;
173  current_ = 0;
174  position_ = 0;
175}
176
177bool MultipartStream::SetPosition(size_t position) {
178  if (adding_) {
179    return false;
180  }
181  size_t part_size, part_offset = 0;
182  for (size_t i = 0; i < parts_.size(); ++i) {
183    if (!parts_[i]->GetSize(&part_size)) {
184      return false;
185    }
186    if (part_offset + part_size > position) {
187      for (size_t j = i+1; j < _min(parts_.size(), current_+1); ++j) {
188        if (!parts_[j]->Rewind()) {
189          return false;
190        }
191      }
192      if (!parts_[i]->SetPosition(position - part_offset)) {
193        return false;
194      }
195      current_ = i;
196      position_ = position;
197      return true;
198    }
199    part_offset += part_size;
200  }
201  return false;
202}
203
204bool MultipartStream::GetPosition(size_t* position) const {
205  if (position) {
206    *position = position_;
207  }
208  return true;
209}
210
211bool MultipartStream::GetSize(size_t* size) const {
212  size_t part_size, total_size = 0;
213  for (size_t i = 0; i < parts_.size(); ++i) {
214    if (!parts_[i]->GetSize(&part_size)) {
215      return false;
216    }
217    total_size += part_size;
218  }
219  if (size) {
220    *size = total_size;
221  }
222  return true;
223}
224
225bool MultipartStream::GetAvailable(size_t* size) const {
226  if (adding_) {
227    return false;
228  }
229  size_t part_size, total_size = 0;
230  for (size_t i = current_; i < parts_.size(); ++i) {
231    if (!parts_[i]->GetAvailable(&part_size)) {
232      return false;
233    }
234    total_size += part_size;
235  }
236  if (size) {
237    *size = total_size;
238  }
239  return true;
240}
241
242//
243// StreamInterface Slots
244//
245
246void MultipartStream::OnEvent(StreamInterface* stream, int events, int error) {
247  if (adding_ || (current_ >= parts_.size()) || (parts_[current_] != stream)) {
248    return;
249  }
250  SignalEvent(this, events, error);
251}
252
253}  // namespace rtc
254