xref: /core/desktop/source/minidump/minidump.cxx (revision bdb47881a214b659b6102c78e5e082197ecf15c7)
1 /* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */
2 /*
3  * This file is part of the LibreOffice project.
4  *
5  * This Source Code Form is subject to the terms of the Mozilla Public
6  * License, v. 2.0. If a copy of the MPL was not distributed with this
7  * file, You can obtain one at http://mozilla.org/MPL/2.0/.
8  */
9 
10 #include <desktop/minidump.hxx>
11 #include <sal/log.hxx>
12 
13 #include <map>
14 #include <fstream>
15 #include <sstream>
16 #include <string>
17 
18 #include <curl/curl.h>
19 
20 #include <systools/curlinit.hxx>
21 
22 #ifdef _WIN32
23 #include <memory>
24 #include <windows.h>
25 #endif
26 
27 const char kUserAgent[] = "Breakpad/1.0 (Linux)";
28 
readStrings(std::istream & file)29 static std::map<std::string, std::string> readStrings(std::istream& file)
30 {
31     std::map<std::string, std::string> parameters;
32 
33     // when file is not readable, the status eof would not be set
34     // better test of state is okay
35     while (file)
36     {
37         std::string line;
38         std::getline(file, line);
39         int sep = line.find('=');
40         if (sep >= 0)
41         {
42             std::string key = line.substr(0, sep);
43             std::string value = line.substr(sep + 1);
44             parameters[key] = value;
45         }
46     }
47 
48     return parameters;
49 }
50 
51 // Callback to get the response data from server.
WriteCallback(void const * ptr,size_t size,size_t nmemb,void * userp)52 static size_t WriteCallback(void const *ptr, size_t size,
53                             size_t nmemb, void *userp)
54 {
55   if (!userp)
56     return 0;
57 
58   std::string* response = static_cast<std::string *>(userp);
59   size_t real_size = size * nmemb;
60   response->append(static_cast<char const *>(ptr), real_size);
61   return real_size;
62 }
63 
getProperty(const std::string & key,std::string & value,std::map<std::string,std::string> & parameters)64 static void getProperty(const std::string& key, std::string& value,
65         std::map<std::string, std::string>& parameters)
66 {
67     auto itr = parameters.find(key);
68     if (itr != parameters.end())
69     {
70         value = itr->second;
71         parameters.erase(itr);
72     }
73 }
74 
generate_json(const std::map<std::string,std::string> & parameters)75 static std::string generate_json(const std::map<std::string, std::string>& parameters)
76 {
77     std::ostringstream stream;
78     stream << "{\n";
79     bool first = true;
80     for (auto itr = parameters.begin(), itrEnd = parameters.end(); itr != itrEnd; ++itr)
81     {
82         if (!first)
83         {
84             stream << ",\n";
85         }
86         first = false;
87         stream << "\"" << itr->first << "\": \"" << itr->second << "\"";
88     }
89     stream << "\n}";
90 
91     return stream.str();
92 }
93 
uploadContent(std::map<std::string,std::string> & parameters,std::string & response)94 static bool uploadContent(std::map<std::string, std::string>& parameters, std::string& response)
95 {
96     CURL* curl = curl_easy_init();
97     if (!curl)
98         return false;
99 
100     ::InitCurl_easy(curl);
101 
102     std::string proxy, proxy_user_pwd, ca_certificate_file, file, url, version;
103 
104     getProperty("Proxy", proxy, parameters);
105     getProperty("ProxyUserPW", proxy_user_pwd, parameters);
106     getProperty("CAFile", ca_certificate_file, parameters);
107 
108     getProperty("DumpFile", file, parameters);
109     getProperty("URL", url, parameters);
110     getProperty("Version", version, parameters);
111     if (url.empty())
112         return false;
113 
114     if (file.empty())
115         return false;
116 
117     if (version.empty())
118         return false;
119 
120     curl_easy_setopt(curl, CURLOPT_URL, url.c_str());
121     curl_easy_setopt(curl, CURLOPT_USERAGENT, kUserAgent);
122     // Set proxy information if necessary.
123     if (!proxy.empty())
124     {
125         curl_easy_setopt(curl, CURLOPT_PROXY, proxy.c_str());
126 
127         curl_easy_setopt(curl, CURLOPT_PROXYAUTH, CURLAUTH_ANYSAFE);
128 
129         if (!proxy_user_pwd.empty())
130             curl_easy_setopt(curl, CURLOPT_PROXYUSERPWD, proxy_user_pwd.c_str());
131         else
132             curl_easy_setopt(curl, CURLOPT_PROXYUSERPWD, ":");
133     }
134 
135     if (!ca_certificate_file.empty())
136         curl_easy_setopt(curl, CURLOPT_CAINFO, ca_certificate_file.c_str());
137 
138     curl_mime* mime = curl_mime_init(curl);
139     std::string additional_data = generate_json(parameters);
140     curl_mimepart* part = curl_mime_addpart(mime);
141     curl_mime_name(part, "AdditionalData");
142     curl_mime_data(part, additional_data.c_str(), CURL_ZERO_TERMINATED);
143 
144     part = curl_mime_addpart(mime);
145     curl_mime_name(part, "Version");
146     curl_mime_data(part, version.c_str(), CURL_ZERO_TERMINATED);
147 
148     part = curl_mime_addpart(mime);
149     curl_mime_name(part, "upload_file_minidump");
150     curl_mime_filedata(part, file.c_str());
151 
152     curl_easy_setopt(curl, CURLOPT_MIMEPOST, mime);
153 
154 
155     // Disable 100-continue header.
156     char buf[] = "Expect:";
157     curl_slist* headerlist = nullptr;
158     headerlist = curl_slist_append(headerlist, buf);
159     curl_easy_setopt(curl, CURLOPT_HTTPHEADER, headerlist);
160 
161     curl_easy_setopt(curl, CURLOPT_WRITEFUNCTION, WriteCallback);
162     std::string response_body;
163     curl_easy_setopt(curl, CURLOPT_WRITEDATA,
164             static_cast<void *>(&response_body));
165 
166     // Fail if 400+ is returned from the web server.
167     curl_easy_setopt(curl, CURLOPT_FAILONERROR, 1);
168 
169     CURLcode cc = curl_easy_perform(curl);
170     long response_code;
171     curl_easy_getinfo(curl, CURLINFO_RESPONSE_CODE, &response_code);
172     SAL_WARN_IF(cc != CURLE_OK, "desktop",
173             "Failed to send http request to " <<
174             url.c_str() <<
175             ", error: " <<
176             curl_easy_strerror(cc));
177 
178     if (headerlist != nullptr)
179     {
180         curl_slist_free_all(headerlist);
181     }
182 
183     response = response_body;
184 
185     if( CURLE_OK != cc )
186     {
187         if (response.empty())
188         {
189             response = curl_easy_strerror(cc);
190         }
191         return false;
192     }
193 
194     return true;
195 }
196 
197 namespace crashreport {
198 
readConfig(const std::string & iniPath,std::string * response)199 bool readConfig(const std::string& iniPath, std::string * response)
200 {
201 #if defined _WIN32
202     std::wstring iniPathW;
203     const int nChars = MultiByteToWideChar(CP_UTF8, 0, iniPath.c_str(), -1, nullptr, 0);
204     auto buf = std::make_unique<wchar_t[]>(nChars);
205     if (MultiByteToWideChar(CP_UTF8, 0, iniPath.c_str(), -1, buf.get(), nChars) != 0)
206         iniPathW = buf.get();
207 
208     std::ifstream file = iniPathW.empty() ? std::ifstream(iniPath) : std::ifstream(iniPathW);
209 #else
210     std::ifstream file(iniPath);
211 #endif
212     std::map<std::string, std::string> parameters = readStrings(file);
213 
214     // make sure that at least the mandatory parameters are in there
215     if (parameters.find("DumpFile") == parameters.end())
216     {
217         if(response != nullptr)
218             *response = "ini file needs to contain a key DumpFile!";
219         return false;
220     }
221 
222     if (parameters.find("Version") == parameters.end())
223     {
224         if (response != nullptr)
225             *response = "ini file needs to contain a key Version!";
226         return false;
227     }
228 
229     if (parameters.find("URL") == parameters.end())
230     {
231         if (response != nullptr)
232             *response = "ini file needs to contain a key URL!";
233         return false;
234     }
235 
236     if (response != nullptr)
237         return uploadContent(parameters, *response);
238 
239     return true;
240 }
241 
242 }
243 
244 /* vim:set shiftwidth=4 softtabstop=4 expandtab: */
245