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