xref: /aosp_15_r20/external/google-breakpad/src/common/windows/http_upload.cc (revision 9712c20fc9bbfbac4935993a2ca0b3958c5adad2)
1*9712c20fSFrederick Mayle // Copyright 2006 Google LLC
2*9712c20fSFrederick Mayle //
3*9712c20fSFrederick Mayle // Redistribution and use in source and binary forms, with or without
4*9712c20fSFrederick Mayle // modification, are permitted provided that the following conditions are
5*9712c20fSFrederick Mayle // met:
6*9712c20fSFrederick Mayle //
7*9712c20fSFrederick Mayle //     * Redistributions of source code must retain the above copyright
8*9712c20fSFrederick Mayle // notice, this list of conditions and the following disclaimer.
9*9712c20fSFrederick Mayle //     * Redistributions in binary form must reproduce the above
10*9712c20fSFrederick Mayle // copyright notice, this list of conditions and the following disclaimer
11*9712c20fSFrederick Mayle // in the documentation and/or other materials provided with the
12*9712c20fSFrederick Mayle // distribution.
13*9712c20fSFrederick Mayle //     * Neither the name of Google LLC nor the names of its
14*9712c20fSFrederick Mayle // contributors may be used to endorse or promote products derived from
15*9712c20fSFrederick Mayle // this software without specific prior written permission.
16*9712c20fSFrederick Mayle //
17*9712c20fSFrederick Mayle // THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
18*9712c20fSFrederick Mayle // "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
19*9712c20fSFrederick Mayle // LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
20*9712c20fSFrederick Mayle // A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
21*9712c20fSFrederick Mayle // OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
22*9712c20fSFrederick Mayle // SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
23*9712c20fSFrederick Mayle // LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
24*9712c20fSFrederick Mayle // DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
25*9712c20fSFrederick Mayle // THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
26*9712c20fSFrederick Mayle // (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
27*9712c20fSFrederick Mayle // OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
28*9712c20fSFrederick Mayle 
29*9712c20fSFrederick Mayle #ifdef HAVE_CONFIG_H
30*9712c20fSFrederick Mayle #include <config.h>  // Must come first
31*9712c20fSFrederick Mayle #endif
32*9712c20fSFrederick Mayle 
33*9712c20fSFrederick Mayle #include <assert.h>
34*9712c20fSFrederick Mayle 
35*9712c20fSFrederick Mayle // Disable exception handler warnings.
36*9712c20fSFrederick Mayle #pragma warning(disable:4530)
37*9712c20fSFrederick Mayle 
38*9712c20fSFrederick Mayle #include <fstream>
39*9712c20fSFrederick Mayle #include <vector>
40*9712c20fSFrederick Mayle 
41*9712c20fSFrederick Mayle #include "common/windows/string_utils-inl.h"
42*9712c20fSFrederick Mayle 
43*9712c20fSFrederick Mayle #include "common/windows/http_upload.h"
44*9712c20fSFrederick Mayle 
45*9712c20fSFrederick Mayle namespace {
46*9712c20fSFrederick Mayle   using std::string;
47*9712c20fSFrederick Mayle   using std::wstring;
48*9712c20fSFrederick Mayle   using std::map;
49*9712c20fSFrederick Mayle   using std::vector;
50*9712c20fSFrederick Mayle   using std::ifstream;
51*9712c20fSFrederick Mayle   using std::ios;
52*9712c20fSFrederick Mayle 
53*9712c20fSFrederick Mayle   const wchar_t kUserAgent[] = L"Breakpad/1.0 (Windows)";
54*9712c20fSFrederick Mayle 
55*9712c20fSFrederick Mayle   // Helper class which closes an internet handle when it goes away
56*9712c20fSFrederick Mayle   class AutoInternetHandle {
57*9712c20fSFrederick Mayle   public:
AutoInternetHandle(HINTERNET handle)58*9712c20fSFrederick Mayle     explicit AutoInternetHandle(HINTERNET handle) : handle_(handle) {}
~AutoInternetHandle()59*9712c20fSFrederick Mayle     ~AutoInternetHandle() {
60*9712c20fSFrederick Mayle       if (handle_) {
61*9712c20fSFrederick Mayle         InternetCloseHandle(handle_);
62*9712c20fSFrederick Mayle       }
63*9712c20fSFrederick Mayle     }
64*9712c20fSFrederick Mayle 
get()65*9712c20fSFrederick Mayle     HINTERNET get() { return handle_; }
66*9712c20fSFrederick Mayle 
67*9712c20fSFrederick Mayle   private:
68*9712c20fSFrederick Mayle     HINTERNET handle_;
69*9712c20fSFrederick Mayle   };
70*9712c20fSFrederick Mayle 
UTF8ToWide(const string & utf8)71*9712c20fSFrederick Mayle   wstring UTF8ToWide(const string& utf8) {
72*9712c20fSFrederick Mayle     if (utf8.length() == 0) {
73*9712c20fSFrederick Mayle       return wstring();
74*9712c20fSFrederick Mayle     }
75*9712c20fSFrederick Mayle 
76*9712c20fSFrederick Mayle     // compute the length of the buffer we'll need
77*9712c20fSFrederick Mayle     int charcount = MultiByteToWideChar(CP_UTF8, 0, utf8.c_str(), -1, NULL, 0);
78*9712c20fSFrederick Mayle 
79*9712c20fSFrederick Mayle     if (charcount == 0) {
80*9712c20fSFrederick Mayle       return wstring();
81*9712c20fSFrederick Mayle     }
82*9712c20fSFrederick Mayle 
83*9712c20fSFrederick Mayle     // convert
84*9712c20fSFrederick Mayle     wchar_t* buf = new wchar_t[charcount];
85*9712c20fSFrederick Mayle     MultiByteToWideChar(CP_UTF8, 0, utf8.c_str(), -1, buf, charcount);
86*9712c20fSFrederick Mayle     wstring result(buf);
87*9712c20fSFrederick Mayle     delete[] buf;
88*9712c20fSFrederick Mayle     return result;
89*9712c20fSFrederick Mayle   }
90*9712c20fSFrederick Mayle 
WideToMBCP(const wstring & wide,unsigned int cp)91*9712c20fSFrederick Mayle   string WideToMBCP(const wstring& wide, unsigned int cp) {
92*9712c20fSFrederick Mayle     if (wide.length() == 0) {
93*9712c20fSFrederick Mayle       return string();
94*9712c20fSFrederick Mayle     }
95*9712c20fSFrederick Mayle 
96*9712c20fSFrederick Mayle     // compute the length of the buffer we'll need
97*9712c20fSFrederick Mayle     int charcount = WideCharToMultiByte(cp, 0, wide.c_str(), -1,
98*9712c20fSFrederick Mayle         NULL, 0, NULL, NULL);
99*9712c20fSFrederick Mayle     if (charcount == 0) {
100*9712c20fSFrederick Mayle       return string();
101*9712c20fSFrederick Mayle     }
102*9712c20fSFrederick Mayle 
103*9712c20fSFrederick Mayle     // convert
104*9712c20fSFrederick Mayle     char *buf = new char[charcount];
105*9712c20fSFrederick Mayle     WideCharToMultiByte(cp, 0, wide.c_str(), -1, buf, charcount,
106*9712c20fSFrederick Mayle         NULL, NULL);
107*9712c20fSFrederick Mayle 
108*9712c20fSFrederick Mayle     string result(buf);
109*9712c20fSFrederick Mayle     delete[] buf;
110*9712c20fSFrederick Mayle     return result;
111*9712c20fSFrederick Mayle   }
112*9712c20fSFrederick Mayle 
GetFileContents(const wstring & filename,vector<char> * contents)113*9712c20fSFrederick Mayle   bool GetFileContents(const wstring& filename, vector<char>* contents) {
114*9712c20fSFrederick Mayle     bool rv = false;
115*9712c20fSFrederick Mayle     // The "open" method on pre-MSVC8 ifstream implementations doesn't accept a
116*9712c20fSFrederick Mayle     // wchar_t* filename, so use _wfopen directly in that case.  For VC8 and
117*9712c20fSFrederick Mayle     // later, _wfopen has been deprecated in favor of _wfopen_s, which does
118*9712c20fSFrederick Mayle     // not exist in earlier versions, so let the ifstream open the file itself.
119*9712c20fSFrederick Mayle     // GCC doesn't support wide file name and opening on FILE* requires ugly
120*9712c20fSFrederick Mayle     // hacks, so fallback to multi byte file.
121*9712c20fSFrederick Mayle #ifdef _MSC_VER
122*9712c20fSFrederick Mayle     ifstream file;
123*9712c20fSFrederick Mayle     file.open(filename.c_str(), ios::binary);
124*9712c20fSFrederick Mayle #else // GCC
125*9712c20fSFrederick Mayle     ifstream file(WideToMBCP(filename, CP_ACP).c_str(), ios::binary);
126*9712c20fSFrederick Mayle #endif  // _MSC_VER >= 1400
127*9712c20fSFrederick Mayle     if (file.is_open()) {
128*9712c20fSFrederick Mayle       file.seekg(0, ios::end);
129*9712c20fSFrederick Mayle       std::streamoff length = file.tellg();
130*9712c20fSFrederick Mayle       // Check for loss of data when converting lenght from std::streamoff into
131*9712c20fSFrederick Mayle       // std::vector<char>::size_type
132*9712c20fSFrederick Mayle       std::vector<char>::size_type vector_size =
133*9712c20fSFrederick Mayle         static_cast<std::vector<char>::size_type>(length);
134*9712c20fSFrederick Mayle       if (static_cast<std::streamoff>(vector_size) == length) {
135*9712c20fSFrederick Mayle         contents->resize(vector_size);
136*9712c20fSFrederick Mayle         if (length != 0) {
137*9712c20fSFrederick Mayle           file.seekg(0, ios::beg);
138*9712c20fSFrederick Mayle           file.read(&((*contents)[0]), length);
139*9712c20fSFrederick Mayle         }
140*9712c20fSFrederick Mayle         rv = true;
141*9712c20fSFrederick Mayle       }
142*9712c20fSFrederick Mayle       file.close();
143*9712c20fSFrederick Mayle     }
144*9712c20fSFrederick Mayle     return rv;
145*9712c20fSFrederick Mayle   }
146*9712c20fSFrederick Mayle 
CheckParameters(const map<wstring,wstring> & parameters)147*9712c20fSFrederick Mayle   bool CheckParameters(const map<wstring, wstring>& parameters) {
148*9712c20fSFrederick Mayle     for (map<wstring, wstring>::const_iterator pos = parameters.begin();
149*9712c20fSFrederick Mayle           pos != parameters.end(); ++pos) {
150*9712c20fSFrederick Mayle       const wstring& str = pos->first;
151*9712c20fSFrederick Mayle       if (str.size() == 0) {
152*9712c20fSFrederick Mayle         return false;  // disallow empty parameter names
153*9712c20fSFrederick Mayle       }
154*9712c20fSFrederick Mayle       for (unsigned int i = 0; i < str.size(); ++i) {
155*9712c20fSFrederick Mayle         wchar_t c = str[i];
156*9712c20fSFrederick Mayle         if (c < 32 || c == '"' || c > 127) {
157*9712c20fSFrederick Mayle           return false;
158*9712c20fSFrederick Mayle         }
159*9712c20fSFrederick Mayle       }
160*9712c20fSFrederick Mayle     }
161*9712c20fSFrederick Mayle     return true;
162*9712c20fSFrederick Mayle   }
163*9712c20fSFrederick Mayle 
164*9712c20fSFrederick Mayle   // Converts a UTF16 string to UTF8.
WideToUTF8(const wstring & wide)165*9712c20fSFrederick Mayle   string WideToUTF8(const wstring& wide) {
166*9712c20fSFrederick Mayle     return WideToMBCP(wide, CP_UTF8);
167*9712c20fSFrederick Mayle   }
168*9712c20fSFrederick Mayle 
ReadResponse(HINTERNET request,wstring * response)169*9712c20fSFrederick Mayle   bool ReadResponse(HINTERNET request, wstring *response) {
170*9712c20fSFrederick Mayle     bool has_content_length_header = false;
171*9712c20fSFrederick Mayle     wchar_t content_length[32];
172*9712c20fSFrederick Mayle     DWORD content_length_size = sizeof(content_length);
173*9712c20fSFrederick Mayle     DWORD claimed_size = 0;
174*9712c20fSFrederick Mayle     string response_body;
175*9712c20fSFrederick Mayle 
176*9712c20fSFrederick Mayle     if (HttpQueryInfo(request, HTTP_QUERY_CONTENT_LENGTH,
177*9712c20fSFrederick Mayle         static_cast<LPVOID>(&content_length),
178*9712c20fSFrederick Mayle         &content_length_size, 0)) {
179*9712c20fSFrederick Mayle       has_content_length_header = true;
180*9712c20fSFrederick Mayle       claimed_size = wcstol(content_length, NULL, 10);
181*9712c20fSFrederick Mayle       response_body.reserve(claimed_size);
182*9712c20fSFrederick Mayle     }
183*9712c20fSFrederick Mayle 
184*9712c20fSFrederick Mayle     DWORD bytes_available;
185*9712c20fSFrederick Mayle     DWORD total_read = 0;
186*9712c20fSFrederick Mayle     BOOL return_code;
187*9712c20fSFrederick Mayle 
188*9712c20fSFrederick Mayle     while (((return_code = InternetQueryDataAvailable(request, &bytes_available,
189*9712c20fSFrederick Mayle         0, 0)) != 0) && bytes_available > 0) {
190*9712c20fSFrederick Mayle       vector<char> response_buffer(bytes_available);
191*9712c20fSFrederick Mayle       DWORD size_read;
192*9712c20fSFrederick Mayle 
193*9712c20fSFrederick Mayle       return_code = InternetReadFile(request,
194*9712c20fSFrederick Mayle           &response_buffer[0],
195*9712c20fSFrederick Mayle           bytes_available, &size_read);
196*9712c20fSFrederick Mayle 
197*9712c20fSFrederick Mayle       if (return_code && size_read > 0) {
198*9712c20fSFrederick Mayle         total_read += size_read;
199*9712c20fSFrederick Mayle         response_body.append(&response_buffer[0], size_read);
200*9712c20fSFrederick Mayle       }
201*9712c20fSFrederick Mayle       else {
202*9712c20fSFrederick Mayle         break;
203*9712c20fSFrederick Mayle       }
204*9712c20fSFrederick Mayle     }
205*9712c20fSFrederick Mayle 
206*9712c20fSFrederick Mayle     bool succeeded = return_code && (!has_content_length_header ||
207*9712c20fSFrederick Mayle         (total_read == claimed_size));
208*9712c20fSFrederick Mayle     if (succeeded && response) {
209*9712c20fSFrederick Mayle       *response = UTF8ToWide(response_body);
210*9712c20fSFrederick Mayle     }
211*9712c20fSFrederick Mayle 
212*9712c20fSFrederick Mayle     return succeeded;
213*9712c20fSFrederick Mayle   }
214*9712c20fSFrederick Mayle 
SendRequestInner(const wstring & url,const wstring & http_method,const wstring & content_type_header,const string & request_body,int * timeout_ms,wstring * response_body,int * response_code)215*9712c20fSFrederick Mayle   bool SendRequestInner(
216*9712c20fSFrederick Mayle       const wstring& url,
217*9712c20fSFrederick Mayle       const wstring& http_method,
218*9712c20fSFrederick Mayle       const wstring& content_type_header,
219*9712c20fSFrederick Mayle       const string& request_body,
220*9712c20fSFrederick Mayle       int* timeout_ms,
221*9712c20fSFrederick Mayle       wstring* response_body,
222*9712c20fSFrederick Mayle       int* response_code) {
223*9712c20fSFrederick Mayle     if (response_code) {
224*9712c20fSFrederick Mayle       *response_code = 0;
225*9712c20fSFrederick Mayle     }
226*9712c20fSFrederick Mayle 
227*9712c20fSFrederick Mayle     // Break up the URL and make sure we can handle it
228*9712c20fSFrederick Mayle     wchar_t scheme[16], host[256], path[1024];
229*9712c20fSFrederick Mayle     URL_COMPONENTS components;
230*9712c20fSFrederick Mayle     memset(&components, 0, sizeof(components));
231*9712c20fSFrederick Mayle     components.dwStructSize = sizeof(components);
232*9712c20fSFrederick Mayle     components.lpszScheme = scheme;
233*9712c20fSFrederick Mayle     components.dwSchemeLength = sizeof(scheme) / sizeof(scheme[0]);
234*9712c20fSFrederick Mayle     components.lpszHostName = host;
235*9712c20fSFrederick Mayle     components.dwHostNameLength = sizeof(host) / sizeof(host[0]);
236*9712c20fSFrederick Mayle     components.lpszUrlPath = path;
237*9712c20fSFrederick Mayle     components.dwUrlPathLength = sizeof(path) / sizeof(path[0]);
238*9712c20fSFrederick Mayle     if (!InternetCrackUrl(url.c_str(), static_cast<DWORD>(url.size()),
239*9712c20fSFrederick Mayle         0, &components)) {
240*9712c20fSFrederick Mayle       DWORD err = GetLastError();
241*9712c20fSFrederick Mayle       wprintf(L"%d\n", err);
242*9712c20fSFrederick Mayle       return false;
243*9712c20fSFrederick Mayle     }
244*9712c20fSFrederick Mayle     bool secure = false;
245*9712c20fSFrederick Mayle     if (wcscmp(scheme, L"https") == 0) {
246*9712c20fSFrederick Mayle       secure = true;
247*9712c20fSFrederick Mayle     }
248*9712c20fSFrederick Mayle     else if (wcscmp(scheme, L"http") != 0) {
249*9712c20fSFrederick Mayle       return false;
250*9712c20fSFrederick Mayle     }
251*9712c20fSFrederick Mayle 
252*9712c20fSFrederick Mayle     AutoInternetHandle internet(InternetOpen(kUserAgent,
253*9712c20fSFrederick Mayle         INTERNET_OPEN_TYPE_PRECONFIG,
254*9712c20fSFrederick Mayle         NULL,  // proxy name
255*9712c20fSFrederick Mayle         NULL,  // proxy bypass
256*9712c20fSFrederick Mayle         0));   // flags
257*9712c20fSFrederick Mayle     if (!internet.get()) {
258*9712c20fSFrederick Mayle       return false;
259*9712c20fSFrederick Mayle     }
260*9712c20fSFrederick Mayle 
261*9712c20fSFrederick Mayle     AutoInternetHandle connection(InternetConnect(internet.get(),
262*9712c20fSFrederick Mayle         host,
263*9712c20fSFrederick Mayle         components.nPort,
264*9712c20fSFrederick Mayle         NULL,    // user name
265*9712c20fSFrederick Mayle         NULL,    // password
266*9712c20fSFrederick Mayle         INTERNET_SERVICE_HTTP,
267*9712c20fSFrederick Mayle         0,       // flags
268*9712c20fSFrederick Mayle         0));  // context
269*9712c20fSFrederick Mayle     if (!connection.get()) {
270*9712c20fSFrederick Mayle       return false;
271*9712c20fSFrederick Mayle     }
272*9712c20fSFrederick Mayle 
273*9712c20fSFrederick Mayle     DWORD http_open_flags = secure ? INTERNET_FLAG_SECURE : 0;
274*9712c20fSFrederick Mayle     http_open_flags |= INTERNET_FLAG_NO_COOKIES;
275*9712c20fSFrederick Mayle     AutoInternetHandle request(HttpOpenRequest(connection.get(),
276*9712c20fSFrederick Mayle         http_method.c_str(),
277*9712c20fSFrederick Mayle         path,
278*9712c20fSFrederick Mayle         NULL,    // version
279*9712c20fSFrederick Mayle         NULL,    // referer
280*9712c20fSFrederick Mayle         NULL,    // agent type
281*9712c20fSFrederick Mayle         http_open_flags,
282*9712c20fSFrederick Mayle         0));  // context
283*9712c20fSFrederick Mayle     if (!request.get()) {
284*9712c20fSFrederick Mayle       return false;
285*9712c20fSFrederick Mayle     }
286*9712c20fSFrederick Mayle 
287*9712c20fSFrederick Mayle     if (!content_type_header.empty()) {
288*9712c20fSFrederick Mayle       HttpAddRequestHeaders(request.get(),
289*9712c20fSFrederick Mayle           content_type_header.c_str(),
290*9712c20fSFrederick Mayle           static_cast<DWORD>(-1),
291*9712c20fSFrederick Mayle           HTTP_ADDREQ_FLAG_ADD);
292*9712c20fSFrederick Mayle     }
293*9712c20fSFrederick Mayle 
294*9712c20fSFrederick Mayle     if (timeout_ms) {
295*9712c20fSFrederick Mayle       if (!InternetSetOption(request.get(),
296*9712c20fSFrederick Mayle           INTERNET_OPTION_SEND_TIMEOUT,
297*9712c20fSFrederick Mayle           timeout_ms,
298*9712c20fSFrederick Mayle           sizeof(*timeout_ms))) {
299*9712c20fSFrederick Mayle         fwprintf(stderr, L"Could not unset send timeout, continuing...\n");
300*9712c20fSFrederick Mayle       }
301*9712c20fSFrederick Mayle 
302*9712c20fSFrederick Mayle       if (!InternetSetOption(request.get(),
303*9712c20fSFrederick Mayle           INTERNET_OPTION_RECEIVE_TIMEOUT,
304*9712c20fSFrederick Mayle           timeout_ms,
305*9712c20fSFrederick Mayle           sizeof(*timeout_ms))) {
306*9712c20fSFrederick Mayle         fwprintf(stderr, L"Could not unset receive timeout, continuing...\n");
307*9712c20fSFrederick Mayle       }
308*9712c20fSFrederick Mayle     }
309*9712c20fSFrederick Mayle 
310*9712c20fSFrederick Mayle     if (!HttpSendRequest(request.get(), NULL, 0,
311*9712c20fSFrederick Mayle         const_cast<char*>(request_body.data()),
312*9712c20fSFrederick Mayle         static_cast<DWORD>(request_body.size()))) {
313*9712c20fSFrederick Mayle       return false;
314*9712c20fSFrederick Mayle     }
315*9712c20fSFrederick Mayle 
316*9712c20fSFrederick Mayle     // The server indicates a successful upload with HTTP status 200.
317*9712c20fSFrederick Mayle     wchar_t http_status[4];
318*9712c20fSFrederick Mayle     DWORD http_status_size = sizeof(http_status);
319*9712c20fSFrederick Mayle     if (!HttpQueryInfo(request.get(), HTTP_QUERY_STATUS_CODE,
320*9712c20fSFrederick Mayle         static_cast<LPVOID>(&http_status), &http_status_size,
321*9712c20fSFrederick Mayle         0)) {
322*9712c20fSFrederick Mayle       return false;
323*9712c20fSFrederick Mayle     }
324*9712c20fSFrederick Mayle 
325*9712c20fSFrederick Mayle     int http_response = wcstol(http_status, NULL, 10);
326*9712c20fSFrederick Mayle     if (response_code) {
327*9712c20fSFrederick Mayle       *response_code = http_response;
328*9712c20fSFrederick Mayle     }
329*9712c20fSFrederick Mayle 
330*9712c20fSFrederick Mayle     bool result = (http_response == 200);
331*9712c20fSFrederick Mayle 
332*9712c20fSFrederick Mayle     if (result) {
333*9712c20fSFrederick Mayle       result = ReadResponse(request.get(), response_body);
334*9712c20fSFrederick Mayle     }
335*9712c20fSFrederick Mayle 
336*9712c20fSFrederick Mayle     return result;
337*9712c20fSFrederick Mayle   }
338*9712c20fSFrederick Mayle 
GenerateMultipartBoundary()339*9712c20fSFrederick Mayle   wstring GenerateMultipartBoundary() {
340*9712c20fSFrederick Mayle     // The boundary has 27 '-' characters followed by 16 hex digits
341*9712c20fSFrederick Mayle     static const wchar_t kBoundaryPrefix[] = L"---------------------------";
342*9712c20fSFrederick Mayle     static const int kBoundaryLength = 27 + 16 + 1;
343*9712c20fSFrederick Mayle 
344*9712c20fSFrederick Mayle     // Generate some random numbers to fill out the boundary
345*9712c20fSFrederick Mayle     int r0 = rand();
346*9712c20fSFrederick Mayle     int r1 = rand();
347*9712c20fSFrederick Mayle 
348*9712c20fSFrederick Mayle     wchar_t temp[kBoundaryLength];
349*9712c20fSFrederick Mayle     swprintf(temp, kBoundaryLength, L"%s%08X%08X", kBoundaryPrefix, r0, r1);
350*9712c20fSFrederick Mayle 
351*9712c20fSFrederick Mayle     // remove when VC++7.1 is no longer supported
352*9712c20fSFrederick Mayle     temp[kBoundaryLength - 1] = L'\0';
353*9712c20fSFrederick Mayle 
354*9712c20fSFrederick Mayle     return wstring(temp);
355*9712c20fSFrederick Mayle   }
356*9712c20fSFrederick Mayle 
GenerateMultipartPostRequestHeader(const wstring & boundary)357*9712c20fSFrederick Mayle   wstring GenerateMultipartPostRequestHeader(const wstring& boundary) {
358*9712c20fSFrederick Mayle     wstring header = L"Content-Type: multipart/form-data; boundary=";
359*9712c20fSFrederick Mayle     header += boundary;
360*9712c20fSFrederick Mayle     return header;
361*9712c20fSFrederick Mayle   }
362*9712c20fSFrederick Mayle 
AppendFileToRequestBody(const wstring & file_part_name,const wstring & filename,string * request_body,bool set_content_type=true)363*9712c20fSFrederick Mayle   bool AppendFileToRequestBody(const wstring& file_part_name,
364*9712c20fSFrederick Mayle                                const wstring& filename,
365*9712c20fSFrederick Mayle                                string* request_body,
366*9712c20fSFrederick Mayle                                bool set_content_type = true) {
367*9712c20fSFrederick Mayle     string file_part_name_utf8 = WideToUTF8(file_part_name);
368*9712c20fSFrederick Mayle     if (file_part_name_utf8.empty()) {
369*9712c20fSFrederick Mayle       return false;
370*9712c20fSFrederick Mayle     }
371*9712c20fSFrederick Mayle 
372*9712c20fSFrederick Mayle     string filename_utf8 = WideToUTF8(filename);
373*9712c20fSFrederick Mayle     if (filename_utf8.empty()) {
374*9712c20fSFrederick Mayle       return false;
375*9712c20fSFrederick Mayle     }
376*9712c20fSFrederick Mayle 
377*9712c20fSFrederick Mayle     if (set_content_type) {
378*9712c20fSFrederick Mayle       request_body->append(
379*9712c20fSFrederick Mayle           "Content-Disposition: form-data; "
380*9712c20fSFrederick Mayle           "name=\"" +
381*9712c20fSFrederick Mayle           file_part_name_utf8 +
382*9712c20fSFrederick Mayle           "\"; "
383*9712c20fSFrederick Mayle           "filename=\"" +
384*9712c20fSFrederick Mayle           filename_utf8 + "\"\r\n");
385*9712c20fSFrederick Mayle       request_body->append("Content-Type: application/octet-stream\r\n");
386*9712c20fSFrederick Mayle       request_body->append("\r\n");
387*9712c20fSFrederick Mayle     }
388*9712c20fSFrederick Mayle 
389*9712c20fSFrederick Mayle     vector<char> contents;
390*9712c20fSFrederick Mayle     if (!GetFileContents(filename, &contents)) {
391*9712c20fSFrederick Mayle       return false;
392*9712c20fSFrederick Mayle     }
393*9712c20fSFrederick Mayle 
394*9712c20fSFrederick Mayle     if (!contents.empty()) {
395*9712c20fSFrederick Mayle       request_body->append(&(contents[0]), contents.size());
396*9712c20fSFrederick Mayle     }
397*9712c20fSFrederick Mayle 
398*9712c20fSFrederick Mayle     return true;
399*9712c20fSFrederick Mayle   }
400*9712c20fSFrederick Mayle 
GenerateRequestBody(const map<wstring,wstring> & parameters,const map<wstring,wstring> & files,const wstring & boundary,string * request_body)401*9712c20fSFrederick Mayle   bool GenerateRequestBody(const map<wstring, wstring>& parameters,
402*9712c20fSFrederick Mayle       const map<wstring, wstring>& files,
403*9712c20fSFrederick Mayle       const wstring& boundary,
404*9712c20fSFrederick Mayle       string *request_body) {
405*9712c20fSFrederick Mayle     string boundary_str = WideToUTF8(boundary);
406*9712c20fSFrederick Mayle     if (boundary_str.empty()) {
407*9712c20fSFrederick Mayle       return false;
408*9712c20fSFrederick Mayle     }
409*9712c20fSFrederick Mayle 
410*9712c20fSFrederick Mayle     request_body->clear();
411*9712c20fSFrederick Mayle 
412*9712c20fSFrederick Mayle     // Append each of the parameter pairs as a form-data part
413*9712c20fSFrederick Mayle     for (map<wstring, wstring>::const_iterator pos = parameters.begin();
414*9712c20fSFrederick Mayle         pos != parameters.end(); ++pos) {
415*9712c20fSFrederick Mayle       request_body->append("--" + boundary_str + "\r\n");
416*9712c20fSFrederick Mayle       request_body->append("Content-Disposition: form-data; name=\"" +
417*9712c20fSFrederick Mayle           WideToUTF8(pos->first) + "\"\r\n\r\n" +
418*9712c20fSFrederick Mayle           WideToUTF8(pos->second) + "\r\n");
419*9712c20fSFrederick Mayle     }
420*9712c20fSFrederick Mayle 
421*9712c20fSFrederick Mayle     // Now append each upload file as a binary (octet-stream) part
422*9712c20fSFrederick Mayle     for (map<wstring, wstring>::const_iterator pos = files.begin();
423*9712c20fSFrederick Mayle         pos != files.end(); ++pos) {
424*9712c20fSFrederick Mayle       request_body->append("--" + boundary_str + "\r\n");
425*9712c20fSFrederick Mayle 
426*9712c20fSFrederick Mayle       if (!AppendFileToRequestBody(pos->first, pos->second, request_body)) {
427*9712c20fSFrederick Mayle         return false;
428*9712c20fSFrederick Mayle       }
429*9712c20fSFrederick Mayle     }
430*9712c20fSFrederick Mayle     request_body->append("--" + boundary_str + "--\r\n");
431*9712c20fSFrederick Mayle     return true;
432*9712c20fSFrederick Mayle   }
433*9712c20fSFrederick Mayle }
434*9712c20fSFrederick Mayle 
435*9712c20fSFrederick Mayle namespace google_breakpad {
SendPutRequest(const wstring & url,const wstring & path,int * timeout_ms,wstring * response_body,int * response_code)436*9712c20fSFrederick Mayle   bool HTTPUpload::SendPutRequest(
437*9712c20fSFrederick Mayle       const wstring& url,
438*9712c20fSFrederick Mayle       const wstring& path,
439*9712c20fSFrederick Mayle       int* timeout_ms,
440*9712c20fSFrederick Mayle       wstring* response_body,
441*9712c20fSFrederick Mayle       int* response_code) {
442*9712c20fSFrederick Mayle     string request_body;
443*9712c20fSFrederick Mayle     // Turn off content-type in the body. If content-type is set then binary
444*9712c20fSFrederick Mayle     // files uploaded to GCS end up with the it prepended to the file
445*9712c20fSFrederick Mayle     // contents.
446*9712c20fSFrederick Mayle     if (!AppendFileToRequestBody(L"symbol_file", path, &request_body,
447*9712c20fSFrederick Mayle                                  /*set_content_type=*/false)) {
448*9712c20fSFrederick Mayle       return false;
449*9712c20fSFrederick Mayle     }
450*9712c20fSFrederick Mayle 
451*9712c20fSFrederick Mayle     return SendRequestInner(
452*9712c20fSFrederick Mayle         url,
453*9712c20fSFrederick Mayle         L"PUT",
454*9712c20fSFrederick Mayle         L"",
455*9712c20fSFrederick Mayle         request_body,
456*9712c20fSFrederick Mayle         timeout_ms,
457*9712c20fSFrederick Mayle         response_body,
458*9712c20fSFrederick Mayle         response_code);
459*9712c20fSFrederick Mayle   }
460*9712c20fSFrederick Mayle 
SendGetRequest(const wstring & url,int * timeout_ms,wstring * response_body,int * response_code)461*9712c20fSFrederick Mayle   bool HTTPUpload::SendGetRequest(
462*9712c20fSFrederick Mayle       const wstring& url,
463*9712c20fSFrederick Mayle       int* timeout_ms,
464*9712c20fSFrederick Mayle       wstring* response_body,
465*9712c20fSFrederick Mayle       int* response_code) {
466*9712c20fSFrederick Mayle     return SendRequestInner(
467*9712c20fSFrederick Mayle         url,
468*9712c20fSFrederick Mayle         L"GET",
469*9712c20fSFrederick Mayle         L"",
470*9712c20fSFrederick Mayle         "",
471*9712c20fSFrederick Mayle         timeout_ms,
472*9712c20fSFrederick Mayle         response_body,
473*9712c20fSFrederick Mayle         response_code);
474*9712c20fSFrederick Mayle   }
475*9712c20fSFrederick Mayle 
SendMultipartPostRequest(const wstring & url,const map<wstring,wstring> & parameters,const map<wstring,wstring> & files,int * timeout_ms,wstring * response_body,int * response_code)476*9712c20fSFrederick Mayle   bool HTTPUpload::SendMultipartPostRequest(
477*9712c20fSFrederick Mayle       const wstring& url,
478*9712c20fSFrederick Mayle       const map<wstring, wstring>& parameters,
479*9712c20fSFrederick Mayle       const map<wstring, wstring>& files,
480*9712c20fSFrederick Mayle       int* timeout_ms,
481*9712c20fSFrederick Mayle       wstring* response_body,
482*9712c20fSFrederick Mayle       int* response_code) {
483*9712c20fSFrederick Mayle     // TODO(bryner): support non-ASCII parameter names
484*9712c20fSFrederick Mayle     if (!CheckParameters(parameters)) {
485*9712c20fSFrederick Mayle       return false;
486*9712c20fSFrederick Mayle     }
487*9712c20fSFrederick Mayle 
488*9712c20fSFrederick Mayle     wstring boundary = GenerateMultipartBoundary();
489*9712c20fSFrederick Mayle     wstring content_type_header = GenerateMultipartPostRequestHeader(boundary);
490*9712c20fSFrederick Mayle 
491*9712c20fSFrederick Mayle     string request_body;
492*9712c20fSFrederick Mayle     if (!GenerateRequestBody(parameters, files, boundary, &request_body)) {
493*9712c20fSFrederick Mayle       return false;
494*9712c20fSFrederick Mayle     }
495*9712c20fSFrederick Mayle 
496*9712c20fSFrederick Mayle     return SendRequestInner(
497*9712c20fSFrederick Mayle         url,
498*9712c20fSFrederick Mayle         L"POST",
499*9712c20fSFrederick Mayle         content_type_header,
500*9712c20fSFrederick Mayle         request_body,
501*9712c20fSFrederick Mayle         timeout_ms,
502*9712c20fSFrederick Mayle         response_body,
503*9712c20fSFrederick Mayle         response_code);
504*9712c20fSFrederick Mayle   }
505*9712c20fSFrederick Mayle 
SendSimplePostRequest(const wstring & url,const wstring & body,const wstring & content_type,int * timeout_ms,wstring * response_body,int * response_code)506*9712c20fSFrederick Mayle   bool HTTPUpload::SendSimplePostRequest(
507*9712c20fSFrederick Mayle       const wstring& url,
508*9712c20fSFrederick Mayle       const wstring& body,
509*9712c20fSFrederick Mayle       const wstring& content_type,
510*9712c20fSFrederick Mayle       int *timeout_ms,
511*9712c20fSFrederick Mayle       wstring *response_body,
512*9712c20fSFrederick Mayle       int *response_code) {
513*9712c20fSFrederick Mayle     return SendRequestInner(
514*9712c20fSFrederick Mayle         url,
515*9712c20fSFrederick Mayle         L"POST",
516*9712c20fSFrederick Mayle         content_type,
517*9712c20fSFrederick Mayle         WideToUTF8(body),
518*9712c20fSFrederick Mayle         timeout_ms,
519*9712c20fSFrederick Mayle         response_body,
520*9712c20fSFrederick Mayle         response_code);
521*9712c20fSFrederick Mayle   }
522*9712c20fSFrederick Mayle }  // namespace google_breakpad
523