BPicture: Fix archive constructor.
[haiku.git] / src / kits / network / libnetapi / HttpForm.cpp
blobcfebcbd5bc48fb17d7d1de98a890e16ef698d67a
1 /*
2 * Copyright 2010-2013 Haiku Inc. All rights reserved.
3 * Distributed under the terms of the MIT License.
5 * Authors:
6 * Christophe Huriaux, c.huriaux@gmail.com
7 */
10 #include <HttpForm.h>
12 #include <cstdlib>
13 #include <cstring>
14 #include <ctime>
16 #include <File.h>
17 #include <NodeInfo.h>
18 #include <TypeConstants.h>
19 #include <Url.h>
22 static int32 kBoundaryRandomSize = 16;
24 using namespace std;
27 // #pragma mark - BHttpFormData
30 BHttpFormData::BHttpFormData()
32 fDataType(B_HTTPFORM_STRING),
33 fCopiedBuffer(false),
34 fFileMark(false),
35 fBufferValue(NULL),
36 fBufferSize(0)
41 BHttpFormData::BHttpFormData(const BString& name, const BString& value)
43 fDataType(B_HTTPFORM_STRING),
44 fCopiedBuffer(false),
45 fFileMark(false),
46 fName(name),
47 fStringValue(value),
48 fBufferValue(NULL),
49 fBufferSize(0)
54 BHttpFormData::BHttpFormData(const BString& name, const BPath& file)
56 fDataType(B_HTTPFORM_FILE),
57 fCopiedBuffer(false),
58 fFileMark(false),
59 fName(name),
60 fPathValue(file),
61 fBufferValue(NULL),
62 fBufferSize(0)
67 BHttpFormData::BHttpFormData(const BString& name, const void* buffer,
68 ssize_t size)
70 fDataType(B_HTTPFORM_BUFFER),
71 fCopiedBuffer(false),
72 fFileMark(false),
73 fName(name),
74 fBufferValue(buffer),
75 fBufferSize(size)
80 BHttpFormData::BHttpFormData(const BHttpFormData& other)
82 fCopiedBuffer(false),
83 fFileMark(false),
84 fBufferValue(NULL),
85 fBufferSize(0)
87 *this = other;
91 BHttpFormData::~BHttpFormData()
93 if (fCopiedBuffer)
94 delete[] reinterpret_cast<const char*>(fBufferValue);
98 // #pragma mark - Retrieve data informations
101 bool
102 BHttpFormData::InitCheck() const
104 if (fDataType == B_HTTPFORM_BUFFER)
105 return fBufferValue != NULL;
107 return true;
111 const BString&
112 BHttpFormData::Name() const
114 return fName;
118 const BString&
119 BHttpFormData::String() const
121 return fStringValue;
125 const BPath&
126 BHttpFormData::File() const
128 return fPathValue;
132 const void*
133 BHttpFormData::Buffer() const
135 return fBufferValue;
139 ssize_t
140 BHttpFormData::BufferSize() const
142 return fBufferSize;
146 bool
147 BHttpFormData::IsFile() const
149 return fFileMark;
153 const BString&
154 BHttpFormData::Filename() const
156 return fFilename;
160 const BString&
161 BHttpFormData::MimeType() const
163 return fMimeType;
167 form_content_type
168 BHttpFormData::Type() const
170 return fDataType;
174 // #pragma mark - Change behavior
177 status_t
178 BHttpFormData::MarkAsFile(const BString& filename, const BString& mimeType)
180 if (fDataType == B_HTTPFORM_UNKNOWN || fDataType == B_HTTPFORM_FILE)
181 return B_ERROR;
183 fFilename = filename;
184 fMimeType = mimeType;
185 fFileMark = true;
187 return B_OK;
191 void
192 BHttpFormData::UnmarkAsFile()
194 fFilename.Truncate(0, true);
195 fMimeType.Truncate(0, true);
196 fFileMark = false;
200 status_t
201 BHttpFormData::CopyBuffer()
203 if (fDataType != B_HTTPFORM_BUFFER)
204 return B_ERROR;
206 char* copiedBuffer = new(std::nothrow) char[fBufferSize];
207 if (copiedBuffer == NULL)
208 return B_NO_MEMORY;
210 memcpy(copiedBuffer, fBufferValue, fBufferSize);
211 fBufferValue = copiedBuffer;
212 fCopiedBuffer = true;
214 return B_OK;
218 BHttpFormData&
219 BHttpFormData::operator=(const BHttpFormData& other)
221 fDataType = other.fDataType;
222 fCopiedBuffer = false;
223 fFileMark = other.fFileMark;
224 fName = other.fName;
225 fStringValue = other.fStringValue;
226 fPathValue = other.fPathValue;
227 fBufferValue = other.fBufferValue;
228 fBufferSize = other.fBufferSize;
229 fFilename = other.fFilename;
230 fMimeType = other.fMimeType;
232 if (other.fCopiedBuffer)
233 CopyBuffer();
235 return *this;
239 // #pragma mark - BHttpForm
242 BHttpForm::BHttpForm()
244 fType(B_HTTP_FORM_URL_ENCODED)
249 BHttpForm::BHttpForm(const BHttpForm& other)
251 fFields(other.fFields),
252 fType(other.fType),
253 fMultipartBoundary(other.fMultipartBoundary)
258 BHttpForm::BHttpForm(const BString& formString)
260 fType(B_HTTP_FORM_URL_ENCODED)
262 ParseString(formString);
266 BHttpForm::~BHttpForm()
268 Clear();
272 // #pragma mark - Form string parsing
275 void
276 BHttpForm::ParseString(const BString& formString)
278 int32 index = 0;
280 while (index < formString.Length())
281 _ExtractNameValuePair(formString, &index);
285 BString
286 BHttpForm::RawData() const
288 BString result;
290 if (fType == B_HTTP_FORM_URL_ENCODED) {
291 for (FormStorage::const_iterator it = fFields.begin();
292 it != fFields.end(); it++) {
293 const BHttpFormData* currentField = &it->second;
295 switch (currentField->Type()) {
296 case B_HTTPFORM_UNKNOWN:
297 break;
299 case B_HTTPFORM_STRING:
300 result << '&' << BUrl::UrlEncode(currentField->Name())
301 << '=' << BUrl::UrlEncode(currentField->String());
302 break;
304 case B_HTTPFORM_FILE:
305 break;
307 case B_HTTPFORM_BUFFER:
308 // Send the buffer only if its not marked as a file
309 if (!currentField->IsFile()) {
310 result << '&' << BUrl::UrlEncode(currentField->Name())
311 << '=';
312 result.Append(
313 reinterpret_cast<const char*>(currentField->Buffer()),
314 currentField->BufferSize());
316 break;
320 result.Remove(0, 1);
321 } else if (fType == B_HTTP_FORM_MULTIPART) {
322 // Very slow and memory consuming method since we're caching the
323 // file content, this should be preferably handled by the protocol
324 for (FormStorage::const_iterator it = fFields.begin();
325 it != fFields.end(); it++) {
326 const BHttpFormData* currentField = &it->second;
327 result << _GetMultipartHeader(currentField);
329 switch (currentField->Type()) {
330 case B_HTTPFORM_UNKNOWN:
331 break;
333 case B_HTTPFORM_STRING:
334 result << currentField->String();
335 break;
337 case B_HTTPFORM_FILE:
339 BFile upFile(currentField->File().Path(), B_READ_ONLY);
340 char readBuffer[1024];
341 ssize_t readSize;
343 readSize = upFile.Read(readBuffer, 1024);
345 while (readSize > 0) {
346 result.Append(readBuffer, readSize);
347 readSize = upFile.Read(readBuffer, 1024);
349 break;
352 case B_HTTPFORM_BUFFER:
353 result.Append(
354 reinterpret_cast<const char*>(currentField->Buffer()),
355 currentField->BufferSize());
356 break;
359 result << "\r\n";
362 result << "--" << fMultipartBoundary << "--\r\n";
365 return result;
369 // #pragma mark - Form add
372 status_t
373 BHttpForm::AddString(const BString& fieldName, const BString& value)
375 BHttpFormData formData(fieldName, value);
376 if (!formData.InitCheck())
377 return B_ERROR;
379 fFields.insert(pair<BString, BHttpFormData>(fieldName, formData));
380 return B_OK;
384 status_t
385 BHttpForm::AddInt(const BString& fieldName, int32 value)
387 BString strValue;
388 strValue << value;
390 return AddString(fieldName, strValue);
394 status_t
395 BHttpForm::AddFile(const BString& fieldName, const BPath& file)
397 BHttpFormData formData(fieldName, file);
398 if (!formData.InitCheck())
399 return B_ERROR;
401 fFields.insert(pair<BString, BHttpFormData>(fieldName, formData));
403 if (fType != B_HTTP_FORM_MULTIPART)
404 SetFormType(B_HTTP_FORM_MULTIPART);
405 return B_OK;
409 status_t
410 BHttpForm::AddBuffer(const BString& fieldName, const void* buffer,
411 ssize_t size)
413 BHttpFormData formData(fieldName, buffer, size);
414 if (!formData.InitCheck())
415 return B_ERROR;
417 fFields.insert(pair<BString, BHttpFormData>(fieldName, formData));
418 return B_OK;
422 status_t
423 BHttpForm::AddBufferCopy(const BString& fieldName, const void* buffer,
424 ssize_t size)
426 BHttpFormData formData(fieldName, buffer, size);
427 if (!formData.InitCheck())
428 return B_ERROR;
430 // Copy the buffer of the inserted form data copy to
431 // avoid an unneeded copy of the buffer upon insertion
432 pair<FormStorage::iterator, bool> insertResult
433 = fFields.insert(pair<BString, BHttpFormData>(fieldName, formData));
435 return insertResult.first->second.CopyBuffer();
439 // #pragma mark - Mark a field as a filename
442 void
443 BHttpForm::MarkAsFile(const BString& fieldName, const BString& filename,
444 const BString& mimeType)
446 FormStorage::iterator it = fFields.find(fieldName);
448 if (it == fFields.end())
449 return;
451 it->second.MarkAsFile(filename, mimeType);
452 if (fType != B_HTTP_FORM_MULTIPART)
453 SetFormType(B_HTTP_FORM_MULTIPART);
457 void
458 BHttpForm::MarkAsFile(const BString& fieldName, const BString& filename)
460 MarkAsFile(fieldName, filename, "");
464 void
465 BHttpForm::UnmarkAsFile(const BString& fieldName)
467 FormStorage::iterator it = fFields.find(fieldName);
469 if (it == fFields.end())
470 return;
472 it->second.UnmarkAsFile();
476 // #pragma mark - Change form type
479 void
480 BHttpForm::SetFormType(form_type type)
482 fType = type;
484 if (fType == B_HTTP_FORM_MULTIPART)
485 _GenerateMultipartBoundary();
489 // #pragma mark - Form test
492 bool
493 BHttpForm::HasField(const BString& name) const
495 return (fFields.find(name) != fFields.end());
499 // #pragma mark - Form retrieve
502 BString
503 BHttpForm::GetMultipartHeader(const BString& fieldName) const
505 FormStorage::const_iterator it = fFields.find(fieldName);
507 if (it == fFields.end())
508 return BString("");
510 return _GetMultipartHeader(&it->second);
514 form_type
515 BHttpForm::GetFormType() const
517 return fType;
521 const BString&
522 BHttpForm::GetMultipartBoundary() const
524 return fMultipartBoundary;
528 BString
529 BHttpForm::GetMultipartFooter() const
531 BString result = "--";
532 result << fMultipartBoundary << "--\r\n";
533 return result;
537 ssize_t
538 BHttpForm::ContentLength() const
540 if (fType == B_HTTP_FORM_URL_ENCODED)
541 return RawData().Length();
543 ssize_t contentLength = 0;
545 for (FormStorage::const_iterator it = fFields.begin();
546 it != fFields.end(); it++) {
547 const BHttpFormData* c = &it->second;
548 contentLength += _GetMultipartHeader(c).Length();
550 switch (c->Type()) {
551 case B_HTTPFORM_UNKNOWN:
552 break;
554 case B_HTTPFORM_STRING:
555 contentLength += c->String().Length();
556 break;
558 case B_HTTPFORM_FILE:
560 BFile upFile(c->File().Path(), B_READ_ONLY);
561 upFile.Seek(0, SEEK_END);
562 contentLength += upFile.Position();
563 break;
566 case B_HTTPFORM_BUFFER:
567 contentLength += c->BufferSize();
568 break;
571 contentLength += 2;
574 contentLength += fMultipartBoundary.Length() + 6;
576 return contentLength;
580 // #pragma mark Form iterator
583 BHttpForm::Iterator
584 BHttpForm::GetIterator()
586 return BHttpForm::Iterator(this);
590 // #pragma mark - Form clear
593 void
594 BHttpForm::Clear()
596 fFields.clear();
600 // #pragma mark - Overloaded operators
603 BHttpFormData&
604 BHttpForm::operator[](const BString& name)
606 if (!HasField(name))
607 AddString(name, "");
609 return fFields[name];
613 void
614 BHttpForm::_ExtractNameValuePair(const BString& formString, int32* index)
616 // Look for a name=value pair
617 int16 firstAmpersand = formString.FindFirst("&", *index);
618 int16 firstEqual = formString.FindFirst("=", *index);
620 BString name;
621 BString value;
623 if (firstAmpersand == -1) {
624 if (firstEqual != -1) {
625 formString.CopyInto(name, *index, firstEqual - *index);
626 formString.CopyInto(value, firstEqual + 1,
627 formString.Length() - firstEqual - 1);
628 } else
629 formString.CopyInto(value, *index,
630 formString.Length() - *index);
632 *index = formString.Length() + 1;
633 } else {
634 if (firstEqual != -1 && firstEqual < firstAmpersand) {
635 formString.CopyInto(name, *index, firstEqual - *index);
636 formString.CopyInto(value, firstEqual + 1,
637 firstAmpersand - firstEqual - 1);
638 } else
639 formString.CopyInto(value, *index, firstAmpersand - *index);
641 *index = firstAmpersand + 1;
644 AddString(name, value);
648 void
649 BHttpForm::_GenerateMultipartBoundary()
651 fMultipartBoundary = "----------------------------";
653 srand(time(NULL));
654 // TODO: Maybe a more robust way to seed the random number
655 // generator is needed?
657 for (int32 i = 0; i < kBoundaryRandomSize; i++)
658 fMultipartBoundary << (char)(rand() % 10 + '0');
662 // #pragma mark - Field information access by std iterator
665 BString
666 BHttpForm::_GetMultipartHeader(const BHttpFormData* element) const
668 BString result;
669 result << "--" << fMultipartBoundary << "\r\n";
670 result << "Content-Disposition: form-data; name=\"" << element->Name()
671 << '"';
673 switch (element->Type()) {
674 case B_HTTPFORM_UNKNOWN:
675 break;
677 case B_HTTPFORM_FILE:
679 result << "; filename=\"" << element->File().Leaf() << '"';
681 BNode fileNode(element->File().Path());
682 BNodeInfo fileInfo(&fileNode);
684 result << "\r\nContent-Type: ";
685 char tempMime[128];
686 if (fileInfo.GetType(tempMime) == B_OK)
687 result << tempMime;
688 else
689 result << "application/octet-stream";
691 break;
694 case B_HTTPFORM_STRING:
695 case B_HTTPFORM_BUFFER:
696 if (element->IsFile()) {
697 result << "; filename=\"" << element->Filename() << '"';
699 if (element->MimeType().Length() > 0)
700 result << "\r\nContent-Type: " << element->MimeType();
701 else
702 result << "\r\nContent-Type: text/plain";
704 break;
707 result << "\r\n\r\n";
709 return result;
713 // #pragma mark - Iterator
716 BHttpForm::Iterator::Iterator(BHttpForm* form)
718 fElement(NULL)
720 fForm = form;
721 fStdIterator = form->fFields.begin();
722 _FindNext();
726 BHttpForm::Iterator::Iterator(const Iterator& other)
728 *this = other;
732 bool
733 BHttpForm::Iterator::HasNext() const
735 return fStdIterator != fForm->fFields.end();
739 BHttpFormData*
740 BHttpForm::Iterator::Next()
742 BHttpFormData* element = fElement;
743 _FindNext();
744 return element;
748 void
749 BHttpForm::Iterator::Remove()
751 fForm->fFields.erase(fStdIterator);
752 fElement = NULL;
756 BString
757 BHttpForm::Iterator::MultipartHeader()
759 return fForm->_GetMultipartHeader(fPrevElement);
763 BHttpForm::Iterator&
764 BHttpForm::Iterator::operator=(const Iterator& other)
766 fForm = other.fForm;
767 fStdIterator = other.fStdIterator;
768 fElement = other.fElement;
769 fPrevElement = other.fPrevElement;
771 return *this;
775 void
776 BHttpForm::Iterator::_FindNext()
778 fPrevElement = fElement;
780 if (fStdIterator != fForm->fFields.end()) {
781 fElement = &fStdIterator->second;
782 fStdIterator++;
783 } else
784 fElement = NULL;