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/crashreport.hxx>
11 #include <rtl/bootstrap.hxx>
12 #include <osl/file.hxx>
13 #include <comphelper/processfactory.hxx>
14 #include <ucbhelper/proxydecider.hxx>
15 #include <unotools/bootstrap.hxx>
16 #include <o3tl/char16_t2wchar_t.hxx>
17 #include <desktop/minidump.hxx>
18
19 #include <config_version.h>
20 #include <config_folders.h>
21
22 #include <string>
23
24
25 #if HAVE_FEATURE_BREAKPAD
26
27 #include <fstream>
28 #if defined( UNX ) && !defined MACOSX && !defined IOS && !defined ANDROID
29 #include <client/linux/handler/exception_handler.h>
30 #elif defined WNT
31 #if defined __clang__
32 #pragma clang diagnostic push
33 #pragma clang diagnostic ignored "-Wmicrosoft-enum-value"
34 #endif
35 #include <client/windows/handler/exception_handler.h>
36 #if defined __clang__
37 #pragma clang diagnostic pop
38 #endif
39 #include <locale>
40 #include <codecvt>
41 #endif
42
43 osl::Mutex CrashReporter::maMutex;
44 std::unique_ptr<google_breakpad::ExceptionHandler> CrashReporter::mpExceptionHandler;
45 bool CrashReporter::mbInit = false;
46 CrashReporter::vmaKeyValues CrashReporter::maKeyValues;
47
48
49 #if defined( UNX ) && !defined MACOSX && !defined IOS && !defined ANDROID
dumpCallback(const google_breakpad::MinidumpDescriptor & descriptor,void *,bool succeeded)50 static bool dumpCallback(const google_breakpad::MinidumpDescriptor& descriptor, void* /*context*/, bool succeeded)
51 {
52 CrashReporter::addKeyValue("DumpFile", OStringToOUString(descriptor.path(), RTL_TEXTENCODING_UTF8), CrashReporter::Write);
53 SAL_WARN("desktop", "minidump generated: " << descriptor.path());
54
55 return succeeded;
56 }
57 #elif defined WNT
dumpCallback(const wchar_t * path,const wchar_t * id,void *,EXCEPTION_POINTERS *,MDRawAssertionInfo *,bool succeeded)58 static bool dumpCallback(const wchar_t* path, const wchar_t* id,
59 void* /*context*/, EXCEPTION_POINTERS* /*exinfo*/,
60 MDRawAssertionInfo* /*assertion*/,
61 bool succeeded)
62 {
63 // TODO: moggi: can we avoid this conversion
64 #ifdef _MSC_VER
65 #pragma warning (disable: 4996)
66 #endif
67 std::wstring_convert<std::codecvt_utf8<wchar_t>> conv1;
68 std::string aPath = conv1.to_bytes(std::wstring(path)) + conv1.to_bytes(std::wstring(id)) + ".dmp";
69 CrashReporter::addKeyValue("DumpFile", OStringToOUString(aPath.c_str(), RTL_TEXTENCODING_UTF8), CrashReporter::AddItem);
70 CrashReporter::addKeyValue("GDIHandles", OUString::number(::GetGuiResources(::GetCurrentProcess(), GR_GDIOBJECTS)), CrashReporter::Write);
71 SAL_WARN("desktop", "minidump generated: " << aPath);
72 return succeeded;
73 }
74 #endif
75
76
writeToFile(std::ios_base::openmode Openmode)77 void CrashReporter::writeToFile(std::ios_base::openmode Openmode)
78 {
79 std::ofstream ini_file(getIniFileName(), Openmode);
80
81 for (auto& keyValue : maKeyValues)
82 {
83 ini_file << OUStringToOString(keyValue.first, RTL_TEXTENCODING_UTF8) << "=";
84 ini_file << OUStringToOString(keyValue.second, RTL_TEXTENCODING_UTF8) << "\n";
85 }
86
87 maKeyValues.clear();
88 ini_file.close();
89 }
90
addKeyValue(const OUString & rKey,const OUString & rValue,tAddKeyHandling AddKeyHandling)91 void CrashReporter::addKeyValue(const OUString& rKey, const OUString& rValue, tAddKeyHandling AddKeyHandling)
92 {
93 osl::MutexGuard aGuard(maMutex);
94
95 if (IsDumpEnable())
96 {
97 if (!rKey.isEmpty())
98 maKeyValues.push_back(mpair(rKey, rValue));
99
100 if (AddKeyHandling != AddItem)
101 {
102 if (mbInit)
103 writeToFile(std::ios_base::app);
104 else if (AddKeyHandling == Create)
105 writeCommonInfo();
106 }
107 }
108 }
109
writeCommonInfo()110 void CrashReporter::writeCommonInfo()
111 {
112 ucbhelper::InternetProxyDecider proxy_decider(::comphelper::getProcessComponentContext());
113
114 const OUString protocol = "https";
115 const OUString url = "crashreport.libreoffice.org";
116 const sal_Int32 port = 443;
117
118 const ucbhelper::InternetProxyServer proxy_server = proxy_decider.getProxy(protocol, url, port);
119
120 // save the new Keys
121 vmaKeyValues atlast = maKeyValues;
122 // clear the keys, the following Keys should be at the begin
123 maKeyValues.clear();
124
125 // limit the amount of code that needs to be executed before the crash reporting
126 addKeyValue("ProductName", "LibreOffice", AddItem);
127 addKeyValue("Version", LIBO_VERSION_DOTTED, AddItem);
128 addKeyValue("BuildID", utl::Bootstrap::getBuildIdData(""), AddItem);
129 addKeyValue("URL", protocol + "://" + url + "/submit/", AddItem);
130
131 if (proxy_server.aName != OUString())
132 {
133 addKeyValue("Proxy", proxy_server.aName + ":" + OUString::number(proxy_server.nPort), AddItem);
134 }
135
136 // write the new keys at the end
137 maKeyValues.insert(maKeyValues.end(), atlast.begin(), atlast.end());
138
139 mbInit = true;
140
141 writeToFile(std::ios_base::trunc);
142
143 updateMinidumpLocation();
144 }
145
146
147 namespace {
148
getCrashDirectory()149 OUString getCrashDirectory()
150 {
151 OUString aCrashURL;
152 rtl::Bootstrap::get("CrashDirectory", aCrashURL);
153 // Need to convert to URL in case of user-defined path
154 osl::FileBase::getFileURLFromSystemPath(aCrashURL, aCrashURL);
155
156 if (aCrashURL.isEmpty()) { // Fall back to user profile
157 aCrashURL = "${$BRAND_BASE_DIR/" LIBO_ETC_FOLDER "/" SAL_CONFIGFILE("bootstrap") ":UserInstallation}/crash/";
158 rtl::Bootstrap::expandMacros(aCrashURL);
159 }
160
161 if (!aCrashURL.endsWith("/"))
162 aCrashURL += "/";
163
164 osl::Directory::create(aCrashURL);
165 OUString aCrashPath;
166 osl::FileBase::getSystemPathFromFileURL(aCrashURL, aCrashPath);
167 return aCrashPath;
168 }
169
170 }
171
updateMinidumpLocation()172 void CrashReporter::updateMinidumpLocation()
173 {
174 #if defined( UNX ) && !defined MACOSX && !defined IOS && !defined ANDROID
175 OUString aURL = getCrashDirectory();
176 OString aOStringUrl = OUStringToOString(aURL, RTL_TEXTENCODING_UTF8);
177 google_breakpad::MinidumpDescriptor descriptor(aOStringUrl.getStr());
178 mpExceptionHandler->set_minidump_descriptor(descriptor);
179 #elif defined WNT
180 OUString aURL = getCrashDirectory();
181 mpExceptionHandler->set_dump_path(o3tl::toW(aURL.getStr()));
182 #endif
183 }
184
crashReportInfoExists()185 bool CrashReporter::crashReportInfoExists()
186 {
187 static bool first = true;
188 static bool InfoExist = false;
189
190 if (first)
191 {
192 first = false;
193 InfoExist = crashreport::readConfig(CrashReporter::getIniFileName(), nullptr);
194 }
195
196 return InfoExist;
197 }
198
readSendConfig(std::string & response)199 bool CrashReporter::readSendConfig(std::string& response)
200 {
201 return crashreport::readConfig(CrashReporter::getIniFileName(), &response);
202 }
203
installExceptionHandler()204 void CrashReporter::installExceptionHandler()
205 {
206 if (!IsDumpEnable())
207 return;
208 #if defined( UNX ) && !defined MACOSX && !defined IOS && !defined ANDROID
209 google_breakpad::MinidumpDescriptor descriptor("/tmp");
210 mpExceptionHandler = std::make_unique<google_breakpad::ExceptionHandler>(descriptor, nullptr, dumpCallback, nullptr, true, -1);
211 #elif defined WNT
212 mpExceptionHandler = std::make_unique<google_breakpad::ExceptionHandler>(L".", nullptr, dumpCallback, nullptr, google_breakpad::ExceptionHandler::HANDLER_ALL);
213 #endif
214 }
215
removeExceptionHandler()216 void CrashReporter::removeExceptionHandler()
217 {
218 mpExceptionHandler.reset();
219 }
220
221
222
IsDumpEnable()223 bool CrashReporter::IsDumpEnable()
224 {
225 OUString sToken;
226 OString sEnvVar(std::getenv("CRASH_DUMP_ENABLE"));
227 bool bEnable = true; // default, always on
228 // read configuration item 'CrashDumpEnable' -> bool on/off
229 if (rtl::Bootstrap::get("CrashDumpEnable", sToken) && sEnvVar.isEmpty())
230 {
231 bEnable = sToken.toBoolean();
232 }
233
234 return bEnable;
235 }
236
237
getIniFileName()238 std::string CrashReporter::getIniFileName()
239 {
240 OUString url = getCrashDirectory() + "dump.ini";
241 OString aUrl = OUStringToOString(url, RTL_TEXTENCODING_UTF8);
242 std::string aRet(aUrl.getStr());
243 return aRet;
244 }
245
246
247 #endif //HAVE_FEATURE_BREAKPAD
248
249 /* vim:set shiftwidth=4 softtabstop=4 expandtab: */
250