PageRenderTime 54ms CodeModel.GetById 31ms RepoModel.GetById 0ms app.codeStats 0ms

/toolkit/crashreporter/client/crashreporter.cpp

https://bitbucket.org/mkato/mozilla-1.9.0-win64
C++ | 612 lines | 560 code | 13 blank | 39 comment | 19 complexity | 2c1e292a92947cf905198b5fe0631352 MD5 | raw file
Possible License(s): LGPL-3.0, MIT, BSD-3-Clause, MPL-2.0-no-copyleft-exception, GPL-2.0, LGPL-2.1
  1. /* -*- Mode: C++; tab-width: 2; indent-tabs-mode: nil; c-basic-offset: 2 -*- */
  2. /* ***** BEGIN LICENSE BLOCK *****
  3. * Version: MPL 1.1/GPL 2.0/LGPL 2.1
  4. *
  5. * The contents of this file are subject to the Mozilla Public License Version
  6. * 1.1 (the "License"); you may not use this file except in compliance with
  7. * the License. You may obtain a copy of the License at
  8. * http://www.mozilla.org/MPL/
  9. *
  10. * Software distributed under the License is distributed on an "AS IS" basis,
  11. * WITHOUT WARRANTY OF ANY KIND, either express or implied. See the License
  12. * for the specific language governing rights and limitations under the
  13. * License.
  14. *
  15. * The Original Code is Mozilla Toolkit Crash Reporter
  16. *
  17. * The Initial Developer of the Original Code is
  18. * Mozilla Corporation
  19. * Portions created by the Initial Developer are Copyright (C) 2006-2007
  20. * the Initial Developer. All Rights Reserved.
  21. *
  22. * Contributor(s):
  23. * Ted Mielczarek <ted.mielczarek@gmail.com>
  24. * Dave Camp <dcamp@mozilla.com>
  25. *
  26. * Alternatively, the contents of this file may be used under the terms of
  27. * either the GNU General Public License Version 2 or later (the "GPL"), or
  28. * the GNU Lesser General Public License Version 2.1 or later (the "LGPL"),
  29. * in which case the provisions of the GPL or the LGPL are applicable instead
  30. * of those above. If you wish to allow use of your version of this file only
  31. * under the terms of either the GPL or the LGPL, and not to allow others to
  32. * use your version of this file under the terms of the MPL, indicate your
  33. * decision by deleting the provisions above and replace them with the notice
  34. * and other provisions required by the GPL or the LGPL. If you do not delete
  35. * the provisions above, a recipient may use your version of this file under
  36. * the terms of any one of the MPL, the GPL or the LGPL.
  37. *
  38. * ***** END LICENSE BLOCK ***** */
  39. #include "crashreporter.h"
  40. #ifdef _MSC_VER
  41. // Disable exception handler warnings.
  42. # pragma warning( disable : 4530 )
  43. #endif
  44. #include <fstream>
  45. #include <sstream>
  46. #include <memory>
  47. #include <time.h>
  48. #include <stdlib.h>
  49. #include <string.h>
  50. using std::string;
  51. using std::istream;
  52. using std::ifstream;
  53. using std::istringstream;
  54. using std::ostringstream;
  55. using std::ostream;
  56. using std::ofstream;
  57. using std::vector;
  58. using std::auto_ptr;
  59. namespace CrashReporter {
  60. StringTable gStrings;
  61. string gSettingsPath;
  62. int gArgc;
  63. char** gArgv;
  64. static auto_ptr<ofstream> gLogStream(NULL);
  65. static string gDumpFile;
  66. static string gExtraFile;
  67. static string kExtraDataExtension = ".extra";
  68. void UIError(const string& message)
  69. {
  70. string errorMessage;
  71. if (!gStrings[ST_CRASHREPORTERERROR].empty()) {
  72. char buf[2048];
  73. UI_SNPRINTF(buf, 2048,
  74. gStrings[ST_CRASHREPORTERERROR].c_str(),
  75. message.c_str());
  76. errorMessage = buf;
  77. } else {
  78. errorMessage = message;
  79. }
  80. UIError_impl(errorMessage);
  81. }
  82. static string Unescape(const string& str)
  83. {
  84. string ret;
  85. for (string::const_iterator iter = str.begin();
  86. iter != str.end();
  87. iter++) {
  88. if (*iter == '\\') {
  89. iter++;
  90. if (*iter == '\\'){
  91. ret.push_back('\\');
  92. } else if (*iter == 'n') {
  93. ret.push_back('\n');
  94. } else if (*iter == 't') {
  95. ret.push_back('\t');
  96. }
  97. } else {
  98. ret.push_back(*iter);
  99. }
  100. }
  101. return ret;
  102. }
  103. static string Escape(const string& str)
  104. {
  105. string ret;
  106. for (string::const_iterator iter = str.begin();
  107. iter != str.end();
  108. iter++) {
  109. if (*iter == '\\') {
  110. ret += "\\\\";
  111. } else if (*iter == '\n') {
  112. ret += "\\n";
  113. } else if (*iter == '\t') {
  114. ret += "\\t";
  115. } else {
  116. ret.push_back(*iter);
  117. }
  118. }
  119. return ret;
  120. }
  121. bool ReadStrings(istream& in, StringTable& strings, bool unescape)
  122. {
  123. string currentSection;
  124. while (!in.eof()) {
  125. string line;
  126. std::getline(in, line);
  127. int sep = line.find('=');
  128. if (sep >= 0) {
  129. string key, value;
  130. key = line.substr(0, sep);
  131. value = line.substr(sep + 1);
  132. if (unescape)
  133. value = Unescape(value);
  134. strings[key] = value;
  135. }
  136. }
  137. return true;
  138. }
  139. bool ReadStringsFromFile(const string& path,
  140. StringTable& strings,
  141. bool unescape)
  142. {
  143. ifstream* f = UIOpenRead(path);
  144. bool success = false;
  145. if (f->is_open()) {
  146. success = ReadStrings(*f, strings, unescape);
  147. f->close();
  148. }
  149. delete f;
  150. return success;
  151. }
  152. bool WriteStrings(ostream& out,
  153. const string& header,
  154. StringTable& strings,
  155. bool escape)
  156. {
  157. out << "[" << header << "]" << std::endl;
  158. for (StringTable::iterator iter = strings.begin();
  159. iter != strings.end();
  160. iter++) {
  161. out << iter->first << "=";
  162. if (escape)
  163. out << Escape(iter->second);
  164. else
  165. out << iter->second;
  166. out << std::endl;
  167. }
  168. return true;
  169. }
  170. bool WriteStringsToFile(const string& path,
  171. const string& header,
  172. StringTable& strings,
  173. bool escape)
  174. {
  175. ofstream* f = UIOpenWrite(path.c_str());
  176. bool success = false;
  177. if (f->is_open()) {
  178. success = WriteStrings(*f, header, strings, escape);
  179. f->close();
  180. }
  181. delete f;
  182. return success;
  183. }
  184. void LogMessage(const std::string& message)
  185. {
  186. if (gLogStream.get()) {
  187. char date[64];
  188. time_t tm;
  189. time(&tm);
  190. if (strftime(date, sizeof(date) - 1, "%c", localtime(&tm)) == 0)
  191. date[0] = '\0';
  192. (*gLogStream) << "[" << date << "] " << message << std::endl;
  193. }
  194. }
  195. static void OpenLogFile()
  196. {
  197. string logPath = gSettingsPath + UI_DIR_SEPARATOR + "submit.log";
  198. gLogStream.reset(UIOpenWrite(logPath.c_str(), true));
  199. }
  200. static bool ReadConfig()
  201. {
  202. string iniPath;
  203. if (!UIGetIniPath(iniPath))
  204. return false;
  205. if (!ReadStringsFromFile(iniPath, gStrings, true))
  206. return false;
  207. // See if we have a string override file, if so process it
  208. char* overrideEnv = getenv("MOZ_CRASHREPORTER_STRINGS_OVERRIDE");
  209. if (overrideEnv && *overrideEnv && UIFileExists(overrideEnv))
  210. ReadStringsFromFile(overrideEnv, gStrings, true);
  211. return true;
  212. }
  213. static string GetExtraDataFilename(const string& dumpfile)
  214. {
  215. string filename(dumpfile);
  216. int dot = filename.rfind('.');
  217. if (dot < 0)
  218. return "";
  219. filename.replace(dot, filename.length() - dot, kExtraDataExtension);
  220. return filename;
  221. }
  222. static string Basename(const string& file)
  223. {
  224. int slashIndex = file.rfind(UI_DIR_SEPARATOR);
  225. if (slashIndex >= 0)
  226. return file.substr(slashIndex + 1);
  227. else
  228. return file;
  229. }
  230. static bool MoveCrashData(const string& toDir,
  231. string& dumpfile,
  232. string& extrafile)
  233. {
  234. if (!UIEnsurePathExists(toDir)) {
  235. UIError(gStrings[ST_ERROR_CREATEDUMPDIR]);
  236. return false;
  237. }
  238. string newDump = toDir + UI_DIR_SEPARATOR + Basename(dumpfile);
  239. string newExtra = toDir + UI_DIR_SEPARATOR + Basename(extrafile);
  240. if (!UIMoveFile(dumpfile, newDump)) {
  241. UIError(gStrings[ST_ERROR_DUMPFILEMOVE]);
  242. return false;
  243. }
  244. if (!UIMoveFile(extrafile, newExtra)) {
  245. UIError(gStrings[ST_ERROR_EXTRAFILEMOVE]);
  246. return false;
  247. }
  248. dumpfile = newDump;
  249. extrafile = newExtra;
  250. return true;
  251. }
  252. static bool AddSubmittedReport(const string& serverResponse)
  253. {
  254. StringTable responseItems;
  255. istringstream in(serverResponse);
  256. ReadStrings(in, responseItems, false);
  257. if (responseItems.find("StopSendingReportsFor") != responseItems.end()) {
  258. // server wants to tell us to stop sending reports for a certain version
  259. string reportPath =
  260. gSettingsPath + UI_DIR_SEPARATOR + "EndOfLife" +
  261. responseItems["StopSendingReportsFor"];
  262. ofstream* reportFile = UIOpenWrite(reportPath);
  263. if (reportFile->is_open()) {
  264. // don't really care about the contents
  265. *reportFile << 1 << "\n";
  266. reportFile->close();
  267. }
  268. delete reportFile;
  269. }
  270. if (responseItems.find("CrashID") == responseItems.end())
  271. return false;
  272. string submittedDir =
  273. gSettingsPath + UI_DIR_SEPARATOR + "submitted";
  274. if (!UIEnsurePathExists(submittedDir)) {
  275. return false;
  276. }
  277. string path = submittedDir + UI_DIR_SEPARATOR +
  278. responseItems["CrashID"] + ".txt";
  279. ofstream* file = UIOpenWrite(path);
  280. if (!file->is_open()) {
  281. delete file;
  282. return false;
  283. }
  284. char buf[1024];
  285. UI_SNPRINTF(buf, 1024,
  286. gStrings["CrashID"].c_str(),
  287. responseItems["CrashID"].c_str());
  288. *file << buf << "\n";
  289. if (responseItems.find("ViewURL") != responseItems.end()) {
  290. UI_SNPRINTF(buf, 1024,
  291. gStrings["CrashDetailsURL"].c_str(),
  292. responseItems["ViewURL"].c_str());
  293. *file << buf << "\n";
  294. }
  295. file->close();
  296. delete file;
  297. return true;
  298. }
  299. void DeleteDump()
  300. {
  301. const char* noDelete = getenv("MOZ_CRASHREPORTER_NO_DELETE_DUMP");
  302. if (!noDelete || *noDelete == '\0') {
  303. if (!gDumpFile.empty())
  304. UIDeleteFile(gDumpFile);
  305. if (!gExtraFile.empty())
  306. UIDeleteFile(gExtraFile);
  307. }
  308. }
  309. bool SendCompleted(bool success, const string& serverResponse)
  310. {
  311. if (success) {
  312. DeleteDump();
  313. return AddSubmittedReport(serverResponse);
  314. }
  315. return true;
  316. }
  317. bool ShouldEnableSending()
  318. {
  319. srand(time(0));
  320. return ((rand() % 100) < MOZ_CRASHREPORTER_ENABLE_PERCENT);
  321. }
  322. } // namespace CrashReporter
  323. using namespace CrashReporter;
  324. void RewriteStrings(StringTable& queryParameters)
  325. {
  326. // rewrite some UI strings with the values from the query parameters
  327. string product = queryParameters["ProductName"];
  328. string vendor = queryParameters["Vendor"];
  329. if (vendor.empty()) {
  330. // Assume Mozilla if no vendor is specified
  331. vendor = "Mozilla";
  332. }
  333. char buf[4096];
  334. UI_SNPRINTF(buf, sizeof(buf),
  335. gStrings[ST_CRASHREPORTERVENDORTITLE].c_str(),
  336. vendor.c_str());
  337. gStrings[ST_CRASHREPORTERTITLE] = buf;
  338. string str = gStrings[ST_CRASHREPORTERPRODUCTERROR];
  339. // Only do the replacement here if the string has two
  340. // format specifiers to start. Otherwise
  341. // we assume it has the product name hardcoded.
  342. string::size_type pos = str.find("%s");
  343. if (pos != string::npos)
  344. pos = str.find("%s", pos+2);
  345. if (pos != string::npos) {
  346. // Leave a format specifier for UIError to fill in
  347. UI_SNPRINTF(buf, sizeof(buf),
  348. gStrings[ST_CRASHREPORTERPRODUCTERROR].c_str(),
  349. product.c_str(),
  350. "%s");
  351. gStrings[ST_CRASHREPORTERERROR] = buf;
  352. }
  353. else {
  354. // product name is hardcoded
  355. gStrings[ST_CRASHREPORTERERROR] = str;
  356. }
  357. UI_SNPRINTF(buf, sizeof(buf),
  358. gStrings[ST_CRASHREPORTERDESCRIPTION].c_str(),
  359. product.c_str());
  360. gStrings[ST_CRASHREPORTERDESCRIPTION] = buf;
  361. UI_SNPRINTF(buf, sizeof(buf),
  362. gStrings[ST_CHECKSUBMIT].c_str(),
  363. vendor.c_str());
  364. gStrings[ST_CHECKSUBMIT] = buf;
  365. UI_SNPRINTF(buf, sizeof(buf),
  366. gStrings[ST_RESTART].c_str(),
  367. product.c_str());
  368. gStrings[ST_RESTART] = buf;
  369. UI_SNPRINTF(buf, sizeof(buf),
  370. gStrings[ST_QUIT].c_str(),
  371. product.c_str());
  372. gStrings[ST_QUIT] = buf;
  373. UI_SNPRINTF(buf, sizeof(buf),
  374. gStrings[ST_ERROR_ENDOFLIFE].c_str(),
  375. product.c_str());
  376. gStrings[ST_ERROR_ENDOFLIFE] = buf;
  377. }
  378. bool CheckEndOfLifed(string version)
  379. {
  380. string reportPath =
  381. gSettingsPath + UI_DIR_SEPARATOR + "EndOfLife" + version;
  382. return UIFileExists(reportPath);
  383. }
  384. int main(int argc, char** argv)
  385. {
  386. gArgc = argc;
  387. gArgv = argv;
  388. if (!ReadConfig()) {
  389. UIError("Couldn't read configuration.");
  390. return 0;
  391. }
  392. if (!UIInit())
  393. return 0;
  394. if (argc > 1) {
  395. gDumpFile = argv[1];
  396. }
  397. if (gDumpFile.empty()) {
  398. // no dump file specified, run the default UI
  399. UIShowDefaultUI();
  400. } else {
  401. gExtraFile = GetExtraDataFilename(gDumpFile);
  402. if (gExtraFile.empty()) {
  403. UIError(gStrings[ST_ERROR_BADARGUMENTS]);
  404. return 0;
  405. }
  406. if (!UIFileExists(gExtraFile)) {
  407. UIError(gStrings[ST_ERROR_EXTRAFILEEXISTS]);
  408. return 0;
  409. }
  410. StringTable queryParameters;
  411. if (!ReadStringsFromFile(gExtraFile, queryParameters, true)) {
  412. UIError(gStrings[ST_ERROR_EXTRAFILEREAD]);
  413. return 0;
  414. }
  415. if (queryParameters.find("ProductName") == queryParameters.end()) {
  416. UIError(gStrings[ST_ERROR_NOPRODUCTNAME]);
  417. return 0;
  418. }
  419. // There is enough information in the extra file to rewrite strings
  420. // to be product specific
  421. RewriteStrings(queryParameters);
  422. if (queryParameters.find("ServerURL") == queryParameters.end()) {
  423. UIError(gStrings[ST_ERROR_NOSERVERURL]);
  424. return 0;
  425. }
  426. // Hopefully the settings path exists in the environment. Try that before
  427. // asking the platform-specific code to guess.
  428. #ifdef XP_WIN32
  429. static const wchar_t kDataDirKey[] = L"MOZ_CRASHREPORTER_DATA_DIRECTORY";
  430. const wchar_t *settingsPath = _wgetenv(kDataDirKey);
  431. if (settingsPath && *settingsPath) {
  432. gSettingsPath = WideToUTF8(settingsPath);
  433. }
  434. #else
  435. static const char kDataDirKey[] = "MOZ_CRASHREPORTER_DATA_DIRECTORY";
  436. const char *settingsPath = getenv(kDataDirKey);
  437. if (settingsPath && *settingsPath) {
  438. gSettingsPath = settingsPath;
  439. }
  440. #endif
  441. else {
  442. string product = queryParameters["ProductName"];
  443. string vendor = queryParameters["Vendor"];
  444. if (!UIGetSettingsPath(vendor, product, gSettingsPath)) {
  445. gSettingsPath.clear();
  446. }
  447. }
  448. if (gSettingsPath.empty() || !UIEnsurePathExists(gSettingsPath)) {
  449. UIError(gStrings[ST_ERROR_NOSETTINGSPATH]);
  450. return 0;
  451. }
  452. OpenLogFile();
  453. if (!UIFileExists(gDumpFile)) {
  454. UIError(gStrings[ST_ERROR_DUMPFILEEXISTS]);
  455. return 0;
  456. }
  457. string pendingDir = gSettingsPath + UI_DIR_SEPARATOR + "pending";
  458. if (!MoveCrashData(pendingDir, gDumpFile, gExtraFile)) {
  459. return 0;
  460. }
  461. string sendURL = queryParameters["ServerURL"];
  462. // we don't need to actually send this
  463. queryParameters.erase("ServerURL");
  464. // re-set XUL_APP_FILE for xulrunner wrapped apps
  465. const char *appfile = getenv("MOZ_CRASHREPORTER_RESTART_XUL_APP_FILE");
  466. if (appfile && *appfile) {
  467. const char prefix[] = "XUL_APP_FILE=";
  468. char *env = (char*) malloc(strlen(appfile)+strlen(prefix));
  469. if (!env) {
  470. UIError("Out of memory");
  471. return 0;
  472. }
  473. strcpy(env, prefix);
  474. strcat(env, appfile);
  475. putenv(env);
  476. free(env);
  477. }
  478. vector<string> restartArgs;
  479. ostringstream paramName;
  480. int i = 0;
  481. paramName << "MOZ_CRASHREPORTER_RESTART_ARG_" << i++;
  482. const char *param = getenv(paramName.str().c_str());
  483. while (param && *param) {
  484. restartArgs.push_back(param);
  485. paramName.str("");
  486. paramName << "MOZ_CRASHREPORTER_RESTART_ARG_" << i++;
  487. param = getenv(paramName.str().c_str());
  488. };
  489. // allow override of the server url via environment variable
  490. //XXX: remove this in the far future when our robot
  491. // masters force everyone to use XULRunner
  492. char* urlEnv = getenv("MOZ_CRASHREPORTER_URL");
  493. if (urlEnv && *urlEnv) {
  494. sendURL = urlEnv;
  495. }
  496. // see if this version has been end-of-lifed
  497. if (queryParameters.find("Version") != queryParameters.end() &&
  498. CheckEndOfLifed(queryParameters["Version"])) {
  499. UIError(gStrings[ST_ERROR_ENDOFLIFE]);
  500. DeleteDump();
  501. return 0;
  502. }
  503. if (!UIShowCrashUI(gDumpFile, queryParameters, sendURL, restartArgs))
  504. DeleteDump();
  505. }
  506. UIShutdown();
  507. return 0;
  508. }
  509. #if defined(XP_WIN) && !defined(__GNUC__)
  510. // We need WinMain in order to not be a console app. This function is unused
  511. // if we are a console application.
  512. int WINAPI wWinMain( HINSTANCE, HINSTANCE, LPWSTR args, int )
  513. {
  514. char** argv = static_cast<char**>(malloc(__argc * sizeof(char*)));
  515. for (int i = 0; i < __argc; i++) {
  516. argv[i] = strdup(WideToUTF8(__wargv[i]).c_str());
  517. }
  518. // Do the real work.
  519. return main(__argc, argv);
  520. }
  521. #endif