LibreOffice Module desktop (master)  1
minidump.cxx
Go to the documentation of this file.
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 #ifdef _WIN32
21 #include <memory>
22 #include <windows.h>
23 #endif
24 
25 const char kUserAgent[] = "Breakpad/1.0 (Linux)";
26 
27 static std::map<std::string, std::string> readStrings(std::istream& file)
28 {
29  std::map<std::string, std::string> parameters;
30 
31  // when file is not readable, the status eof would not be set
32  // better test of state is okay
33  while (file)
34  {
35  std::string line;
36  std::getline(file, line);
37  int sep = line.find('=');
38  if (sep >= 0)
39  {
40  std::string key = line.substr(0, sep);
41  std::string value = line.substr(sep + 1);
42  parameters[key] = value;
43  }
44  }
45 
46  return parameters;
47 }
48 
49 // Callback to get the response data from server.
50 static size_t WriteCallback(void const *ptr, size_t size,
51  size_t nmemb, void *userp)
52 {
53  if (!userp)
54  return 0;
55 
56  std::string* response = static_cast<std::string *>(userp);
57  size_t real_size = size * nmemb;
58  response->append(static_cast<char const *>(ptr), real_size);
59  return real_size;
60 }
61 
62 static void getProperty(const std::string& key, std::string& value,
63  std::map<std::string, std::string>& parameters)
64 {
65  auto itr = parameters.find(key);
66  if (itr != parameters.end())
67  {
68  value = itr->second;
69  parameters.erase(itr);
70  }
71 }
72 
73 static std::string generate_json(const std::map<std::string, std::string>& parameters)
74 {
75  std::ostringstream stream;
76  stream << "{\n";
77  bool first = true;
78  for (auto itr = parameters.begin(), itrEnd = parameters.end(); itr != itrEnd; ++itr)
79  {
80  if (!first)
81  {
82  stream << ",\n";
83  }
84  first = false;
85  stream << "\"" << itr->first << "\": \"" << itr->second << "\"";
86  }
87  stream << "\n}";
88 
89  return stream.str();
90 }
91 
92 static bool uploadContent(std::map<std::string, std::string>& parameters, std::string& response)
93 {
94  CURL* curl = curl_easy_init();
95  if (!curl)
96  return false;
97 
98  std::string proxy, proxy_user_pwd, ca_certificate_file, file, url, version;
99 
100  getProperty("Proxy", proxy, parameters);
101  getProperty("ProxyUserPW", proxy_user_pwd, parameters);
102  getProperty("CAFile", ca_certificate_file, parameters);
103 
104  getProperty("DumpFile", file, parameters);
105  getProperty("URL", url, parameters);
106  getProperty("Version", version, parameters);
107  if (url.empty())
108  return false;
109 
110  if (file.empty())
111  return false;
112 
113  if (version.empty())
114  return false;
115 
116  curl_easy_setopt(curl, CURLOPT_URL, url.c_str());
117  curl_easy_setopt(curl, CURLOPT_USERAGENT, kUserAgent);
118  curl_easy_setopt(curl, CURLOPT_SSL_VERIFYPEER, false);
119  // Set proxy information if necessary.
120  if (!proxy.empty())
121  {
122  curl_easy_setopt(curl, CURLOPT_PROXY, proxy.c_str());
123 
124  curl_easy_setopt(curl, CURLOPT_PROXYAUTH, CURLAUTH_ANYSAFE);
125 
126  if (!proxy_user_pwd.empty())
127  curl_easy_setopt(curl, CURLOPT_PROXYUSERPWD, proxy_user_pwd.c_str());
128  else
129  curl_easy_setopt(curl, CURLOPT_PROXYUSERPWD, ":");
130  }
131 
132  if (!ca_certificate_file.empty())
133  curl_easy_setopt(curl, CURLOPT_CAINFO, ca_certificate_file.c_str());
134 
135  curl_httppost* formpost = nullptr;
136  curl_httppost* lastptr = nullptr;
137  std::string additional_data = generate_json(parameters);
138  curl_formadd(&formpost, &lastptr,
139  CURLFORM_COPYNAME, "AdditionalData",
140  CURLFORM_COPYCONTENTS, additional_data.c_str(),
141  CURLFORM_END);
142 
143  curl_formadd(&formpost, &lastptr,
144  CURLFORM_COPYNAME, "Version",
145  CURLFORM_COPYCONTENTS, version.c_str(),
146  CURLFORM_END);
147 
148  std::string response_body;
149  long response_code;
150  curl_formadd(&formpost, &lastptr,
151  CURLFORM_COPYNAME, "upload_file_minidump",
152  CURLFORM_FILE, file.c_str(),
153  CURLFORM_END);
154 
155  curl_easy_setopt(curl, CURLOPT_HTTPPOST, formpost);
156 
157 
158  // Disable 100-continue header.
159  char buf[] = "Expect:";
160  curl_slist* headerlist = nullptr;
161  headerlist = curl_slist_append(headerlist, buf);
162  curl_easy_setopt(curl, CURLOPT_HTTPHEADER, headerlist);
163 
164  curl_easy_setopt(curl, CURLOPT_WRITEFUNCTION, WriteCallback);
165  curl_easy_setopt(curl, CURLOPT_WRITEDATA,
166  static_cast<void *>(&response_body));
167 
168  // Fail if 400+ is returned from the web server.
169  curl_easy_setopt(curl, CURLOPT_FAILONERROR, 1);
170 
171  CURLcode cc = curl_easy_perform(curl);
172  curl_easy_getinfo(curl, CURLINFO_RESPONSE_CODE, &response_code);
173  SAL_WARN_IF(cc != CURLE_OK, "desktop",
174  "Failed to send http request to " <<
175  url.c_str() <<
176  ", error: " <<
177  curl_easy_strerror(cc));
178 
179  if (formpost != nullptr)
180  {
181  curl_formfree(formpost);
182  }
183  if (headerlist != nullptr)
184  {
185  curl_slist_free_all(headerlist);
186  }
187 
188  response = response_body;
189 
190  if( CURLE_OK != cc )
191  return false;
192 
193  return true;
194 }
195 
196 namespace crashreport {
197 
198 bool readConfig(const std::string& iniPath, std::string * response)
199 {
200 #if defined _WIN32
201  std::wstring iniPathW;
202  const int nChars = MultiByteToWideChar(CP_UTF8, 0, iniPath.c_str(), -1, nullptr, 0);
203  auto buf = std::make_unique<wchar_t[]>(nChars);
204  if (MultiByteToWideChar(CP_UTF8, 0, iniPath.c_str(), -1, buf.get(), nChars) != 0)
205  iniPathW = buf.get();
206 
207  std::ifstream file = iniPathW.empty() ? std::ifstream(iniPath) : std::ifstream(iniPathW);
208 #else
209  std::ifstream file(iniPath);
210 #endif
211  std::map<std::string, std::string> parameters = readStrings(file);
212 
213  // make sure that at least the mandatory parameters are in there
214  if (parameters.find("DumpFile") == parameters.end())
215  {
216  if(response != nullptr)
217  *response = "ini file needs to contain a key DumpFile!";
218  return false;
219  }
220 
221  if (parameters.find("Version") == parameters.end())
222  {
223  if (response != nullptr)
224  *response = "ini file needs to contain a key Version!";
225  return false;
226  }
227 
228  if (parameters.find("URL") == parameters.end())
229  {
230  if (response != nullptr)
231  *response = "ini file needs to contain a key URL!";
232  return false;
233  }
234 
235  if (response != nullptr)
236  return uploadContent(parameters, *response);
237 
238  return true;
239 }
240 
241 }
242 
243 /* vim:set shiftwidth=4 softtabstop=4 expandtab: */
tuple line
static size_t WriteCallback(void const *ptr, size_t size, size_t nmemb, void *userp)
Definition: minidump.cxx:50
static void getProperty(const std::string &key, std::string &value, std::map< std::string, std::string > &parameters)
Definition: minidump.cxx:62
static std::string generate_json(const std::map< std::string, std::string > &parameters)
Definition: minidump.cxx:73
const char kUserAgent[]
Definition: minidump.cxx:25
bool readConfig(const std::string &iniPath, std::string *response)
Read+Send, Test and send info from the Dump.ini .
Definition: minidump.cxx:198
static bool uploadContent(std::map< std::string, std::string > &parameters, std::string &response)
Definition: minidump.cxx:92
static std::map< std::string, std::string > readStrings(std::istream &file)
Definition: minidump.cxx:27
Reference< XOutputStream > stream
#define SAL_WARN_IF(condition, area, stream)
Any value
constexpr OUStringLiteral first