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