1 // libjingle 2 // Copyright 2004--2010, Google Inc. 3 // 4 // Redistribution and use in source and binary forms, with or without 5 // modification, are permitted provided that the following conditions are met: 6 // 7 // 1. Redistributions of source code must retain the above copyright notice, 8 // this list of conditions and the following disclaimer. 9 // 2. Redistributions in binary form must reproduce the above copyright notice, 10 // this list of conditions and the following disclaimer in the documentation 11 // and/or other materials provided with the distribution. 12 // 3. The name of the author may not be used to endorse or promote products 13 // derived from this software without specific prior written permission. 14 // 15 // THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR IMPLIED 16 // WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF 17 // MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO 18 // EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, 19 // SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, 20 // PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; 21 // OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, 22 // WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR 23 // OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF 24 // ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. 25 26 27 #include "talk/base/common.h" 28 #include "talk/base/httpcommon.h" 29 #include "talk/base/multipart.h" 30 31 namespace talk_base { 32 33 /////////////////////////////////////////////////////////////////////////////// 34 // MultipartStream 35 /////////////////////////////////////////////////////////////////////////////// 36 37 MultipartStream::MultipartStream(const std::string& type, 38 const std::string& boundary) 39 : type_(type), 40 boundary_(boundary), 41 adding_(true), 42 current_(0), 43 position_(0) { 44 // The content type should be multipart/*. 45 ASSERT(0 == strncmp(type_.c_str(), "multipart/", 10)); 46 } 47 48 MultipartStream::~MultipartStream() { 49 Close(); 50 } 51 52 void MultipartStream::GetContentType(std::string* content_type) { 53 ASSERT(NULL != content_type); 54 content_type->assign(type_); 55 content_type->append("; boundary="); 56 content_type->append(boundary_); 57 } 58 59 bool MultipartStream::AddPart(StreamInterface* data_stream, 60 const std::string& content_disposition, 61 const std::string& content_type) { 62 if (!AddPart("", content_disposition, content_type)) 63 return false; 64 parts_.push_back(data_stream); 65 data_stream->SignalEvent.connect(this, &MultipartStream::OnEvent); 66 return true; 67 } 68 69 bool MultipartStream::AddPart(const std::string& data, 70 const std::string& content_disposition, 71 const std::string& content_type) { 72 ASSERT(adding_); 73 if (!adding_) 74 return false; 75 std::stringstream ss; 76 if (!parts_.empty()) { 77 ss << "\r\n"; 78 } 79 ss << "--" << boundary_ << "\r\n"; 80 if (!content_disposition.empty()) { 81 ss << ToString(HH_CONTENT_DISPOSITION) << ": " 82 << content_disposition << "\r\n"; 83 } 84 if (!content_type.empty()) { 85 ss << ToString(HH_CONTENT_TYPE) << ": " 86 << content_type << "\r\n"; 87 } 88 ss << "\r\n" << data; 89 parts_.push_back(new MemoryStream(ss.str().data(), ss.str().size())); 90 return true; 91 } 92 93 void MultipartStream::EndParts() { 94 ASSERT(adding_); 95 if (!adding_) 96 return; 97 98 std::stringstream ss; 99 if (!parts_.empty()) { 100 ss << "\r\n"; 101 } 102 ss << "--" << boundary_ << "--" << "\r\n"; 103 parts_.push_back(new MemoryStream(ss.str().data(), ss.str().size())); 104 105 ASSERT(0 == current_); 106 ASSERT(0 == position_); 107 adding_ = false; 108 SignalEvent(this, SE_OPEN | SE_READ, 0); 109 } 110 111 size_t MultipartStream::GetPartSize(const std::string& data, 112 const std::string& content_disposition, 113 const std::string& content_type) const { 114 size_t size = 0; 115 if (!parts_.empty()) { 116 size += 2; // for "\r\n"; 117 } 118 size += boundary_.size() + 4; // for "--boundary_\r\n"; 119 if (!content_disposition.empty()) { 120 // for ToString(HH_CONTENT_DISPOSITION): content_disposition\r\n 121 size += std::string(ToString(HH_CONTENT_DISPOSITION)).size() + 2 + 122 content_disposition.size() + 2; 123 } 124 if (!content_type.empty()) { 125 // for ToString(HH_CONTENT_TYPE): content_type\r\n 126 size += std::string(ToString(HH_CONTENT_TYPE)).size() + 2 + 127 content_type.size() + 2; 128 } 129 size += 2 + data.size(); // for \r\ndata 130 return size; 131 } 132 133 size_t MultipartStream::GetEndPartSize() const { 134 size_t size = 0; 135 if (!parts_.empty()) { 136 size += 2; // for "\r\n"; 137 } 138 size += boundary_.size() + 6; // for "--boundary_--\r\n"; 139 return size; 140 } 141 142 // 143 // StreamInterface 144 // 145 146 StreamState MultipartStream::GetState() const { 147 if (adding_) { 148 return SS_OPENING; 149 } 150 return (current_ < parts_.size()) ? SS_OPEN : SS_CLOSED; 151 } 152 153 StreamResult MultipartStream::Read(void* buffer, size_t buffer_len, 154 size_t* read, int* error) { 155 if (adding_) { 156 return SR_BLOCK; 157 } 158 size_t local_read; 159 if (!read) read = &local_read; 160 while (current_ < parts_.size()) { 161 StreamResult result = parts_[current_]->Read(buffer, buffer_len, read, 162 error); 163 if (SR_EOS != result) { 164 if (SR_SUCCESS == result) { 165 position_ += *read; 166 } 167 return result; 168 } 169 ++current_; 170 } 171 return SR_EOS; 172 } 173 174 StreamResult MultipartStream::Write(const void* data, size_t data_len, 175 size_t* written, int* error) { 176 if (error) { 177 *error = -1; 178 } 179 return SR_ERROR; 180 } 181 182 void MultipartStream::Close() { 183 for (size_t i = 0; i < parts_.size(); ++i) { 184 delete parts_[i]; 185 } 186 parts_.clear(); 187 adding_ = false; 188 current_ = 0; 189 position_ = 0; 190 } 191 192 bool MultipartStream::SetPosition(size_t position) { 193 if (adding_) { 194 return false; 195 } 196 size_t part_size, part_offset = 0; 197 for (size_t i = 0; i < parts_.size(); ++i) { 198 if (!parts_[i]->GetSize(&part_size)) { 199 return false; 200 } 201 if (part_offset + part_size > position) { 202 for (size_t j = i+1; j < _min(parts_.size(), current_+1); ++j) { 203 if (!parts_[j]->Rewind()) { 204 return false; 205 } 206 } 207 if (!parts_[i]->SetPosition(position - part_offset)) { 208 return false; 209 } 210 current_ = i; 211 position_ = position; 212 return true; 213 } 214 part_offset += part_size; 215 } 216 return false; 217 } 218 219 bool MultipartStream::GetPosition(size_t* position) const { 220 if (position) { 221 *position = position_; 222 } 223 return true; 224 } 225 226 bool MultipartStream::GetSize(size_t* size) const { 227 size_t part_size, total_size = 0; 228 for (size_t i = 0; i < parts_.size(); ++i) { 229 if (!parts_[i]->GetSize(&part_size)) { 230 return false; 231 } 232 total_size += part_size; 233 } 234 if (size) { 235 *size = total_size; 236 } 237 return true; 238 } 239 240 bool MultipartStream::GetAvailable(size_t* size) const { 241 if (adding_) { 242 return false; 243 } 244 size_t part_size, total_size = 0; 245 for (size_t i = current_; i < parts_.size(); ++i) { 246 if (!parts_[i]->GetAvailable(&part_size)) { 247 return false; 248 } 249 total_size += part_size; 250 } 251 if (size) { 252 *size = total_size; 253 } 254 return true; 255 } 256 257 // 258 // StreamInterface Slots 259 // 260 261 void MultipartStream::OnEvent(StreamInterface* stream, int events, int error) { 262 if (adding_ || (current_ >= parts_.size()) || (parts_[current_] != stream)) { 263 return; 264 } 265 SignalEvent(this, events, error); 266 } 267 268 } // namespace talk_base 269