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