xref: /haiku/src/kits/network/libnetservices/DataRequest.cpp (revision 35d8d4d113fc34ee03e141a7c4c8eb3c7cd03672)
1*35d8d4d1SNiels Sascha Reedijk /*
2*35d8d4d1SNiels Sascha Reedijk  * Copyright 2013 Haiku, Inc. All rights reserved.
3*35d8d4d1SNiels Sascha Reedijk  * Distributed under the terms of the MIT License.
4*35d8d4d1SNiels Sascha Reedijk  *
5*35d8d4d1SNiels Sascha Reedijk  * Authors:
6*35d8d4d1SNiels Sascha Reedijk  *		Adrien Destugues, pulkomandy@pulkomandy.tk
7*35d8d4d1SNiels Sascha Reedijk  */
8*35d8d4d1SNiels Sascha Reedijk 
9*35d8d4d1SNiels Sascha Reedijk 
10*35d8d4d1SNiels Sascha Reedijk #include "DataRequest.h"
11*35d8d4d1SNiels Sascha Reedijk 
12*35d8d4d1SNiels Sascha Reedijk #include <AutoDeleter.h>
13*35d8d4d1SNiels Sascha Reedijk #include <HttpAuthentication.h>
14*35d8d4d1SNiels Sascha Reedijk #include <mail_encoding.h>
15*35d8d4d1SNiels Sascha Reedijk #include <stdio.h>
16*35d8d4d1SNiels Sascha Reedijk 
17*35d8d4d1SNiels Sascha Reedijk 
18*35d8d4d1SNiels Sascha Reedijk BDataRequest::BDataRequest(const BUrl& url, BUrlProtocolListener* listener,
19*35d8d4d1SNiels Sascha Reedijk 		BUrlContext* context)
20*35d8d4d1SNiels Sascha Reedijk 	: BUrlRequest(url, listener, context, "data URL parser", "data"),
21*35d8d4d1SNiels Sascha Reedijk 	fResult()
22*35d8d4d1SNiels Sascha Reedijk {
23*35d8d4d1SNiels Sascha Reedijk 	fResult.SetContentType("text/plain");
24*35d8d4d1SNiels Sascha Reedijk }
25*35d8d4d1SNiels Sascha Reedijk 
26*35d8d4d1SNiels Sascha Reedijk 
27*35d8d4d1SNiels Sascha Reedijk const BUrlResult&
28*35d8d4d1SNiels Sascha Reedijk BDataRequest::Result() const
29*35d8d4d1SNiels Sascha Reedijk {
30*35d8d4d1SNiels Sascha Reedijk 	return fResult;
31*35d8d4d1SNiels Sascha Reedijk }
32*35d8d4d1SNiels Sascha Reedijk 
33*35d8d4d1SNiels Sascha Reedijk 
34*35d8d4d1SNiels Sascha Reedijk status_t
35*35d8d4d1SNiels Sascha Reedijk BDataRequest::_ProtocolLoop()
36*35d8d4d1SNiels Sascha Reedijk {
37*35d8d4d1SNiels Sascha Reedijk 	BString mimeType;
38*35d8d4d1SNiels Sascha Reedijk 	BString charset;
39*35d8d4d1SNiels Sascha Reedijk 	const char* payload;
40*35d8d4d1SNiels Sascha Reedijk 	ssize_t length;
41*35d8d4d1SNiels Sascha Reedijk 	bool isBase64 = false;
42*35d8d4d1SNiels Sascha Reedijk 
43*35d8d4d1SNiels Sascha Reedijk 	// The RFC has examples where some characters are URL-Encoded.
44*35d8d4d1SNiels Sascha Reedijk 	fUrl.UrlDecode(true);
45*35d8d4d1SNiels Sascha Reedijk 
46*35d8d4d1SNiels Sascha Reedijk 	// The RFC says this uses a nonstandard scheme, so the path, query and
47*35d8d4d1SNiels Sascha Reedijk 	// fragment are a bit nonsensical. It would be nice to handle them, but
48*35d8d4d1SNiels Sascha Reedijk 	// some software (eg. WebKit) relies on data URIs with embedded "#" char
49*35d8d4d1SNiels Sascha Reedijk 	// in the data...
50*35d8d4d1SNiels Sascha Reedijk 	BString data = fUrl.UrlString();
51*35d8d4d1SNiels Sascha Reedijk 	data.Remove(0, 5); // remove "data:"
52*35d8d4d1SNiels Sascha Reedijk 
53*35d8d4d1SNiels Sascha Reedijk 	int separatorPosition = data.FindFirst(',');
54*35d8d4d1SNiels Sascha Reedijk 
55*35d8d4d1SNiels Sascha Reedijk 	if (fListener != NULL)
56*35d8d4d1SNiels Sascha Reedijk 		fListener->ConnectionOpened(this);
57*35d8d4d1SNiels Sascha Reedijk 
58*35d8d4d1SNiels Sascha Reedijk 	if (separatorPosition >= 0) {
59*35d8d4d1SNiels Sascha Reedijk 		BString meta = data;
60*35d8d4d1SNiels Sascha Reedijk 		meta.Truncate(separatorPosition);
61*35d8d4d1SNiels Sascha Reedijk 		data.Remove(0, separatorPosition + 1);
62*35d8d4d1SNiels Sascha Reedijk 
63*35d8d4d1SNiels Sascha Reedijk 		int pos = 0;
64*35d8d4d1SNiels Sascha Reedijk 		while (meta.Length() > 0) {
65*35d8d4d1SNiels Sascha Reedijk 			// Extract next parameter
66*35d8d4d1SNiels Sascha Reedijk 			pos = meta.FindFirst(';', pos);
67*35d8d4d1SNiels Sascha Reedijk 
68*35d8d4d1SNiels Sascha Reedijk 			BString parameter = meta;
69*35d8d4d1SNiels Sascha Reedijk 			if (pos >= 0) {
70*35d8d4d1SNiels Sascha Reedijk 				parameter.Truncate(pos);
71*35d8d4d1SNiels Sascha Reedijk 				meta.Remove(0, pos+1);
72*35d8d4d1SNiels Sascha Reedijk 			} else
73*35d8d4d1SNiels Sascha Reedijk 				meta.Truncate(0);
74*35d8d4d1SNiels Sascha Reedijk 
75*35d8d4d1SNiels Sascha Reedijk 			// Interpret the parameter
76*35d8d4d1SNiels Sascha Reedijk 			if (parameter == "base64") {
77*35d8d4d1SNiels Sascha Reedijk 				isBase64 = true;
78*35d8d4d1SNiels Sascha Reedijk 			} else if (parameter.FindFirst("charset=") == 0) {
79*35d8d4d1SNiels Sascha Reedijk 				charset = parameter;
80*35d8d4d1SNiels Sascha Reedijk 			} else {
81*35d8d4d1SNiels Sascha Reedijk 				// Must be the MIME type
82*35d8d4d1SNiels Sascha Reedijk 				mimeType = parameter;
83*35d8d4d1SNiels Sascha Reedijk 			}
84*35d8d4d1SNiels Sascha Reedijk 		}
85*35d8d4d1SNiels Sascha Reedijk 
86*35d8d4d1SNiels Sascha Reedijk 		if (charset.Length() > 0)
87*35d8d4d1SNiels Sascha Reedijk 			mimeType << ";" << charset;
88*35d8d4d1SNiels Sascha Reedijk 		fResult.SetContentType(mimeType);
89*35d8d4d1SNiels Sascha Reedijk 
90*35d8d4d1SNiels Sascha Reedijk 	}
91*35d8d4d1SNiels Sascha Reedijk 
92*35d8d4d1SNiels Sascha Reedijk 	ArrayDeleter<char> buffer;
93*35d8d4d1SNiels Sascha Reedijk 	if (isBase64) {
94*35d8d4d1SNiels Sascha Reedijk 		// Check that the base64 data is properly padded (we process characters
95*35d8d4d1SNiels Sascha Reedijk 		// by groups of 4 and there must not be stray chars at the end as
96*35d8d4d1SNiels Sascha Reedijk 		// Base64 specifies padding.
97*35d8d4d1SNiels Sascha Reedijk 		if (data.Length() & 3)
98*35d8d4d1SNiels Sascha Reedijk 			return B_BAD_DATA;
99*35d8d4d1SNiels Sascha Reedijk 
100*35d8d4d1SNiels Sascha Reedijk 		buffer.SetTo(new char[data.Length() * 3 / 4]);
101*35d8d4d1SNiels Sascha Reedijk 		payload = buffer.Get();
102*35d8d4d1SNiels Sascha Reedijk 			// payload must be a const char* so we can assign data.String() to
103*35d8d4d1SNiels Sascha Reedijk 			// it below, but decode_64 modifies buffer.
104*35d8d4d1SNiels Sascha Reedijk 		length = decode_base64(buffer.Get(), data.String(), data.Length());
105*35d8d4d1SNiels Sascha Reedijk 
106*35d8d4d1SNiels Sascha Reedijk 		// There may be some padding at the end of the base64 stream. This
107*35d8d4d1SNiels Sascha Reedijk 		// prevents us from computing the exact length we should get, so allow
108*35d8d4d1SNiels Sascha Reedijk 		// for some error margin.
109*35d8d4d1SNiels Sascha Reedijk 		if (length > data.Length() * 3 / 4
110*35d8d4d1SNiels Sascha Reedijk 			|| length < data.Length() * 3 / 4 - 3) {
111*35d8d4d1SNiels Sascha Reedijk 			return B_BAD_DATA;
112*35d8d4d1SNiels Sascha Reedijk 		}
113*35d8d4d1SNiels Sascha Reedijk 	} else {
114*35d8d4d1SNiels Sascha Reedijk 		payload = data.String();
115*35d8d4d1SNiels Sascha Reedijk 		length = data.Length();
116*35d8d4d1SNiels Sascha Reedijk 	}
117*35d8d4d1SNiels Sascha Reedijk 
118*35d8d4d1SNiels Sascha Reedijk 	fResult.SetLength(length);
119*35d8d4d1SNiels Sascha Reedijk 
120*35d8d4d1SNiels Sascha Reedijk 	if (fListener != NULL) {
121*35d8d4d1SNiels Sascha Reedijk 		fListener->HeadersReceived(this, fResult);
122*35d8d4d1SNiels Sascha Reedijk 		if (length > 0) {
123*35d8d4d1SNiels Sascha Reedijk 			fListener->DataReceived(this, payload, 0, length);
124*35d8d4d1SNiels Sascha Reedijk 			fListener->DownloadProgress(this, length, length);
125*35d8d4d1SNiels Sascha Reedijk 		}
126*35d8d4d1SNiels Sascha Reedijk 	}
127*35d8d4d1SNiels Sascha Reedijk 
128*35d8d4d1SNiels Sascha Reedijk 	return B_OK;
129*35d8d4d1SNiels Sascha Reedijk }
130