2 * Copyright 2010-2013 Haiku Inc. All rights reserved.
3 * Distributed under the terms of the MIT License.
6 * Christophe Huriaux, c.huriaux@gmail.com
18 #include <TypeConstants.h>
22 static int32 kBoundaryRandomSize
= 16;
27 // #pragma mark - BHttpFormData
30 BHttpFormData::BHttpFormData()
32 fDataType(B_HTTPFORM_STRING
),
41 BHttpFormData::BHttpFormData(const BString
& name
, const BString
& value
)
43 fDataType(B_HTTPFORM_STRING
),
54 BHttpFormData::BHttpFormData(const BString
& name
, const BPath
& file
)
56 fDataType(B_HTTPFORM_FILE
),
67 BHttpFormData::BHttpFormData(const BString
& name
, const void* buffer
,
70 fDataType(B_HTTPFORM_BUFFER
),
80 BHttpFormData::BHttpFormData(const BHttpFormData
& other
)
91 BHttpFormData::~BHttpFormData()
94 delete[] reinterpret_cast<const char*>(fBufferValue
);
98 // #pragma mark - Retrieve data informations
102 BHttpFormData::InitCheck() const
104 if (fDataType
== B_HTTPFORM_BUFFER
)
105 return fBufferValue
!= NULL
;
112 BHttpFormData::Name() const
119 BHttpFormData::String() const
126 BHttpFormData::File() const
133 BHttpFormData::Buffer() const
140 BHttpFormData::BufferSize() const
147 BHttpFormData::IsFile() const
154 BHttpFormData::Filename() const
161 BHttpFormData::MimeType() const
168 BHttpFormData::Type() const
174 // #pragma mark - Change behavior
178 BHttpFormData::MarkAsFile(const BString
& filename
, const BString
& mimeType
)
180 if (fDataType
== B_HTTPFORM_UNKNOWN
|| fDataType
== B_HTTPFORM_FILE
)
183 fFilename
= filename
;
184 fMimeType
= mimeType
;
192 BHttpFormData::UnmarkAsFile()
194 fFilename
.Truncate(0, true);
195 fMimeType
.Truncate(0, true);
201 BHttpFormData::CopyBuffer()
203 if (fDataType
!= B_HTTPFORM_BUFFER
)
206 char* copiedBuffer
= new(std::nothrow
) char[fBufferSize
];
207 if (copiedBuffer
== NULL
)
210 memcpy(copiedBuffer
, fBufferValue
, fBufferSize
);
211 fBufferValue
= copiedBuffer
;
212 fCopiedBuffer
= true;
219 BHttpFormData::operator=(const BHttpFormData
& other
)
221 fDataType
= other
.fDataType
;
222 fCopiedBuffer
= false;
223 fFileMark
= other
.fFileMark
;
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
)
239 // #pragma mark - BHttpForm
242 BHttpForm::BHttpForm()
244 fType(B_HTTP_FORM_URL_ENCODED
)
249 BHttpForm::BHttpForm(const BHttpForm
& other
)
251 fFields(other
.fFields
),
253 fMultipartBoundary(other
.fMultipartBoundary
)
258 BHttpForm::BHttpForm(const BString
& formString
)
260 fType(B_HTTP_FORM_URL_ENCODED
)
262 ParseString(formString
);
266 BHttpForm::~BHttpForm()
272 // #pragma mark - Form string parsing
276 BHttpForm::ParseString(const BString
& formString
)
280 while (index
< formString
.Length())
281 _ExtractNameValuePair(formString
, &index
);
286 BHttpForm::RawData() const
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
:
299 case B_HTTPFORM_STRING
:
300 result
<< '&' << BUrl::UrlEncode(currentField
->Name())
301 << '=' << BUrl::UrlEncode(currentField
->String());
304 case B_HTTPFORM_FILE
:
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())
313 reinterpret_cast<const char*>(currentField
->Buffer()),
314 currentField
->BufferSize());
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
:
333 case B_HTTPFORM_STRING
:
334 result
<< currentField
->String();
337 case B_HTTPFORM_FILE
:
339 BFile
upFile(currentField
->File().Path(), B_READ_ONLY
);
340 char readBuffer
[1024];
343 readSize
= upFile
.Read(readBuffer
, 1024);
345 while (readSize
> 0) {
346 result
.Append(readBuffer
, readSize
);
347 readSize
= upFile
.Read(readBuffer
, 1024);
352 case B_HTTPFORM_BUFFER
:
354 reinterpret_cast<const char*>(currentField
->Buffer()),
355 currentField
->BufferSize());
362 result
<< "--" << fMultipartBoundary
<< "--\r\n";
369 // #pragma mark - Form add
373 BHttpForm::AddString(const BString
& fieldName
, const BString
& value
)
375 BHttpFormData
formData(fieldName
, value
);
376 if (!formData
.InitCheck())
379 fFields
.insert(pair
<BString
, BHttpFormData
>(fieldName
, formData
));
385 BHttpForm::AddInt(const BString
& fieldName
, int32 value
)
390 return AddString(fieldName
, strValue
);
395 BHttpForm::AddFile(const BString
& fieldName
, const BPath
& file
)
397 BHttpFormData
formData(fieldName
, file
);
398 if (!formData
.InitCheck())
401 fFields
.insert(pair
<BString
, BHttpFormData
>(fieldName
, formData
));
403 if (fType
!= B_HTTP_FORM_MULTIPART
)
404 SetFormType(B_HTTP_FORM_MULTIPART
);
410 BHttpForm::AddBuffer(const BString
& fieldName
, const void* buffer
,
413 BHttpFormData
formData(fieldName
, buffer
, size
);
414 if (!formData
.InitCheck())
417 fFields
.insert(pair
<BString
, BHttpFormData
>(fieldName
, formData
));
423 BHttpForm::AddBufferCopy(const BString
& fieldName
, const void* buffer
,
426 BHttpFormData
formData(fieldName
, buffer
, size
);
427 if (!formData
.InitCheck())
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
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())
451 it
->second
.MarkAsFile(filename
, mimeType
);
452 if (fType
!= B_HTTP_FORM_MULTIPART
)
453 SetFormType(B_HTTP_FORM_MULTIPART
);
458 BHttpForm::MarkAsFile(const BString
& fieldName
, const BString
& filename
)
460 MarkAsFile(fieldName
, filename
, "");
465 BHttpForm::UnmarkAsFile(const BString
& fieldName
)
467 FormStorage::iterator it
= fFields
.find(fieldName
);
469 if (it
== fFields
.end())
472 it
->second
.UnmarkAsFile();
476 // #pragma mark - Change form type
480 BHttpForm::SetFormType(form_type type
)
484 if (fType
== B_HTTP_FORM_MULTIPART
)
485 _GenerateMultipartBoundary();
489 // #pragma mark - Form test
493 BHttpForm::HasField(const BString
& name
) const
495 return (fFields
.find(name
) != fFields
.end());
499 // #pragma mark - Form retrieve
503 BHttpForm::GetMultipartHeader(const BString
& fieldName
) const
505 FormStorage::const_iterator it
= fFields
.find(fieldName
);
507 if (it
== fFields
.end())
510 return _GetMultipartHeader(&it
->second
);
515 BHttpForm::GetFormType() const
522 BHttpForm::GetMultipartBoundary() const
524 return fMultipartBoundary
;
529 BHttpForm::GetMultipartFooter() const
531 BString result
= "--";
532 result
<< fMultipartBoundary
<< "--\r\n";
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();
551 case B_HTTPFORM_UNKNOWN
:
554 case B_HTTPFORM_STRING
:
555 contentLength
+= c
->String().Length();
558 case B_HTTPFORM_FILE
:
560 BFile
upFile(c
->File().Path(), B_READ_ONLY
);
561 upFile
.Seek(0, SEEK_END
);
562 contentLength
+= upFile
.Position();
566 case B_HTTPFORM_BUFFER
:
567 contentLength
+= c
->BufferSize();
574 contentLength
+= fMultipartBoundary
.Length() + 6;
576 return contentLength
;
580 // #pragma mark Form iterator
584 BHttpForm::GetIterator()
586 return BHttpForm::Iterator(this);
590 // #pragma mark - Form clear
600 // #pragma mark - Overloaded operators
604 BHttpForm::operator[](const BString
& name
)
609 return fFields
[name
];
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
);
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);
629 formString
.CopyInto(value
, *index
,
630 formString
.Length() - *index
);
632 *index
= formString
.Length() + 1;
634 if (firstEqual
!= -1 && firstEqual
< firstAmpersand
) {
635 formString
.CopyInto(name
, *index
, firstEqual
- *index
);
636 formString
.CopyInto(value
, firstEqual
+ 1,
637 firstAmpersand
- firstEqual
- 1);
639 formString
.CopyInto(value
, *index
, firstAmpersand
- *index
);
641 *index
= firstAmpersand
+ 1;
644 AddString(name
, value
);
649 BHttpForm::_GenerateMultipartBoundary()
651 fMultipartBoundary
= "----------------------------";
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
666 BHttpForm::_GetMultipartHeader(const BHttpFormData
* element
) const
669 result
<< "--" << fMultipartBoundary
<< "\r\n";
670 result
<< "Content-Disposition: form-data; name=\"" << element
->Name()
673 switch (element
->Type()) {
674 case B_HTTPFORM_UNKNOWN
:
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: ";
686 if (fileInfo
.GetType(tempMime
) == B_OK
)
689 result
<< "application/octet-stream";
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();
702 result
<< "\r\nContent-Type: text/plain";
707 result
<< "\r\n\r\n";
713 // #pragma mark - Iterator
716 BHttpForm::Iterator::Iterator(BHttpForm
* form
)
721 fStdIterator
= form
->fFields
.begin();
726 BHttpForm::Iterator::Iterator(const Iterator
& other
)
733 BHttpForm::Iterator::HasNext() const
735 return fStdIterator
!= fForm
->fFields
.end();
740 BHttpForm::Iterator::Next()
742 BHttpFormData
* element
= fElement
;
749 BHttpForm::Iterator::Remove()
751 fForm
->fFields
.erase(fStdIterator
);
757 BHttpForm::Iterator::MultipartHeader()
759 return fForm
->_GetMultipartHeader(fPrevElement
);
764 BHttpForm::Iterator::operator=(const Iterator
& other
)
767 fStdIterator
= other
.fStdIterator
;
768 fElement
= other
.fElement
;
769 fPrevElement
= other
.fPrevElement
;
776 BHttpForm::Iterator::_FindNext()
778 fPrevElement
= fElement
;
780 if (fStdIterator
!= fForm
->fFields
.end()) {
781 fElement
= &fStdIterator
->second
;