135d8d4d1SNiels Sascha Reedijk /*
235d8d4d1SNiels Sascha Reedijk * Copyright 2013 Haiku, Inc. All rights reserved.
335d8d4d1SNiels Sascha Reedijk * Distributed under the terms of the MIT License.
435d8d4d1SNiels Sascha Reedijk *
535d8d4d1SNiels Sascha Reedijk * Authors:
635d8d4d1SNiels Sascha Reedijk * Adrien Destugues, pulkomandy@pulkomandy.tk
735d8d4d1SNiels Sascha Reedijk */
835d8d4d1SNiels Sascha Reedijk
935d8d4d1SNiels Sascha Reedijk
1035d8d4d1SNiels Sascha Reedijk #include "DataRequest.h"
1135d8d4d1SNiels Sascha Reedijk
1235d8d4d1SNiels Sascha Reedijk #include <AutoDeleter.h>
1335d8d4d1SNiels Sascha Reedijk #include <HttpAuthentication.h>
1435d8d4d1SNiels Sascha Reedijk #include <mail_encoding.h>
1535d8d4d1SNiels Sascha Reedijk #include <stdio.h>
1635d8d4d1SNiels Sascha Reedijk
17ce64ffdbSNiels Sascha Reedijk using namespace BPrivate::Network;
18ce64ffdbSNiels Sascha Reedijk
19ce64ffdbSNiels Sascha Reedijk
BDataRequest(const BUrl & url,BDataIO * output,BUrlProtocolListener * listener,BUrlContext * context)20*78b14420SLeorize BDataRequest::BDataRequest(const BUrl& url, BDataIO* output,
21*78b14420SLeorize BUrlProtocolListener* listener,
22*78b14420SLeorize BUrlContext* context)
23*78b14420SLeorize :
24*78b14420SLeorize BUrlRequest(url, output, listener, context, "data URL parser", "data"),
25*78b14420SLeorize fResult()
26*78b14420SLeorize {
27*78b14420SLeorize fResult.SetContentType("text/plain");
28*78b14420SLeorize }
29*78b14420SLeorize
3035d8d4d1SNiels Sascha Reedijk
3135d8d4d1SNiels Sascha Reedijk const BUrlResult&
Result() const3235d8d4d1SNiels Sascha Reedijk BDataRequest::Result() const
3335d8d4d1SNiels Sascha Reedijk {
3435d8d4d1SNiels Sascha Reedijk return fResult;
3535d8d4d1SNiels Sascha Reedijk }
3635d8d4d1SNiels Sascha Reedijk
3735d8d4d1SNiels Sascha Reedijk
3835d8d4d1SNiels Sascha Reedijk status_t
_ProtocolLoop()3935d8d4d1SNiels Sascha Reedijk BDataRequest::_ProtocolLoop()
4035d8d4d1SNiels Sascha Reedijk {
4135d8d4d1SNiels Sascha Reedijk BString mimeType;
4235d8d4d1SNiels Sascha Reedijk BString charset;
4335d8d4d1SNiels Sascha Reedijk const char* payload;
4435d8d4d1SNiels Sascha Reedijk ssize_t length;
4535d8d4d1SNiels Sascha Reedijk bool isBase64 = false;
4635d8d4d1SNiels Sascha Reedijk
4735d8d4d1SNiels Sascha Reedijk // The RFC has examples where some characters are URL-Encoded.
4835d8d4d1SNiels Sascha Reedijk fUrl.UrlDecode(true);
4935d8d4d1SNiels Sascha Reedijk
5035d8d4d1SNiels Sascha Reedijk // The RFC says this uses a nonstandard scheme, so the path, query and
5135d8d4d1SNiels Sascha Reedijk // fragment are a bit nonsensical. It would be nice to handle them, but
5235d8d4d1SNiels Sascha Reedijk // some software (eg. WebKit) relies on data URIs with embedded "#" char
5335d8d4d1SNiels Sascha Reedijk // in the data...
5435d8d4d1SNiels Sascha Reedijk BString data = fUrl.UrlString();
5535d8d4d1SNiels Sascha Reedijk data.Remove(0, 5); // remove "data:"
5635d8d4d1SNiels Sascha Reedijk
5735d8d4d1SNiels Sascha Reedijk int separatorPosition = data.FindFirst(',');
5835d8d4d1SNiels Sascha Reedijk
5935d8d4d1SNiels Sascha Reedijk if (fListener != NULL)
6035d8d4d1SNiels Sascha Reedijk fListener->ConnectionOpened(this);
6135d8d4d1SNiels Sascha Reedijk
6235d8d4d1SNiels Sascha Reedijk if (separatorPosition >= 0) {
6335d8d4d1SNiels Sascha Reedijk BString meta = data;
6435d8d4d1SNiels Sascha Reedijk meta.Truncate(separatorPosition);
6535d8d4d1SNiels Sascha Reedijk data.Remove(0, separatorPosition + 1);
6635d8d4d1SNiels Sascha Reedijk
6735d8d4d1SNiels Sascha Reedijk int pos = 0;
6835d8d4d1SNiels Sascha Reedijk while (meta.Length() > 0) {
6935d8d4d1SNiels Sascha Reedijk // Extract next parameter
7035d8d4d1SNiels Sascha Reedijk pos = meta.FindFirst(';', pos);
7135d8d4d1SNiels Sascha Reedijk
7235d8d4d1SNiels Sascha Reedijk BString parameter = meta;
7335d8d4d1SNiels Sascha Reedijk if (pos >= 0) {
7435d8d4d1SNiels Sascha Reedijk parameter.Truncate(pos);
7535d8d4d1SNiels Sascha Reedijk meta.Remove(0, pos+1);
7635d8d4d1SNiels Sascha Reedijk } else
7735d8d4d1SNiels Sascha Reedijk meta.Truncate(0);
7835d8d4d1SNiels Sascha Reedijk
7935d8d4d1SNiels Sascha Reedijk // Interpret the parameter
8035d8d4d1SNiels Sascha Reedijk if (parameter == "base64") {
8135d8d4d1SNiels Sascha Reedijk isBase64 = true;
8235d8d4d1SNiels Sascha Reedijk } else if (parameter.FindFirst("charset=") == 0) {
8335d8d4d1SNiels Sascha Reedijk charset = parameter;
8435d8d4d1SNiels Sascha Reedijk } else {
8535d8d4d1SNiels Sascha Reedijk // Must be the MIME type
8635d8d4d1SNiels Sascha Reedijk mimeType = parameter;
8735d8d4d1SNiels Sascha Reedijk }
8835d8d4d1SNiels Sascha Reedijk }
8935d8d4d1SNiels Sascha Reedijk
9035d8d4d1SNiels Sascha Reedijk if (charset.Length() > 0)
9135d8d4d1SNiels Sascha Reedijk mimeType << ";" << charset;
9235d8d4d1SNiels Sascha Reedijk fResult.SetContentType(mimeType);
9335d8d4d1SNiels Sascha Reedijk
9435d8d4d1SNiels Sascha Reedijk }
9535d8d4d1SNiels Sascha Reedijk
9635d8d4d1SNiels Sascha Reedijk ArrayDeleter<char> buffer;
9735d8d4d1SNiels Sascha Reedijk if (isBase64) {
9835d8d4d1SNiels Sascha Reedijk // Check that the base64 data is properly padded (we process characters
9935d8d4d1SNiels Sascha Reedijk // by groups of 4 and there must not be stray chars at the end as
10035d8d4d1SNiels Sascha Reedijk // Base64 specifies padding.
10135d8d4d1SNiels Sascha Reedijk if (data.Length() & 3)
10235d8d4d1SNiels Sascha Reedijk return B_BAD_DATA;
10335d8d4d1SNiels Sascha Reedijk
10435d8d4d1SNiels Sascha Reedijk buffer.SetTo(new char[data.Length() * 3 / 4]);
10535d8d4d1SNiels Sascha Reedijk payload = buffer.Get();
10635d8d4d1SNiels Sascha Reedijk // payload must be a const char* so we can assign data.String() to
10735d8d4d1SNiels Sascha Reedijk // it below, but decode_64 modifies buffer.
10835d8d4d1SNiels Sascha Reedijk length = decode_base64(buffer.Get(), data.String(), data.Length());
10935d8d4d1SNiels Sascha Reedijk
11035d8d4d1SNiels Sascha Reedijk // There may be some padding at the end of the base64 stream. This
11135d8d4d1SNiels Sascha Reedijk // prevents us from computing the exact length we should get, so allow
11235d8d4d1SNiels Sascha Reedijk // for some error margin.
11335d8d4d1SNiels Sascha Reedijk if (length > data.Length() * 3 / 4
11435d8d4d1SNiels Sascha Reedijk || length < data.Length() * 3 / 4 - 3) {
11535d8d4d1SNiels Sascha Reedijk return B_BAD_DATA;
11635d8d4d1SNiels Sascha Reedijk }
11735d8d4d1SNiels Sascha Reedijk } else {
11835d8d4d1SNiels Sascha Reedijk payload = data.String();
11935d8d4d1SNiels Sascha Reedijk length = data.Length();
12035d8d4d1SNiels Sascha Reedijk }
12135d8d4d1SNiels Sascha Reedijk
12235d8d4d1SNiels Sascha Reedijk fResult.SetLength(length);
12335d8d4d1SNiels Sascha Reedijk
124*78b14420SLeorize if (fListener != NULL)
125*78b14420SLeorize fListener->HeadersReceived(this);
126*78b14420SLeorize if (length > 0) {
127*78b14420SLeorize if (fOutput != NULL) {
128*78b14420SLeorize size_t written = 0;
129*78b14420SLeorize status_t err = fOutput->WriteExactly(payload, length, &written);
130*78b14420SLeorize if (fListener != NULL && written > 0)
131*78b14420SLeorize fListener->BytesWritten(this, written);
132*78b14420SLeorize if (err != B_OK)
133*78b14420SLeorize return err;
134*78b14420SLeorize if (fListener != NULL)
135*78b14420SLeorize fListener->DownloadProgress(this, written, written);
136*78b14420SLeorize }
137*78b14420SLeorize }
13835d8d4d1SNiels Sascha Reedijk
13935d8d4d1SNiels Sascha Reedijk return B_OK;
14035d8d4d1SNiels Sascha Reedijk }
141