xref: /haiku/src/kits/network/libnetservices/DataRequest.cpp (revision 70b63f18b32a7ae90e4ff49659ba3844ce4c9aa5)
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