summaryrefslogtreecommitdiff
path: root/indra/win_crash_logger/win_crash_logger.cpp
diff options
context:
space:
mode:
Diffstat (limited to 'indra/win_crash_logger/win_crash_logger.cpp')
-rw-r--r--indra/win_crash_logger/win_crash_logger.cpp915
1 files changed, 915 insertions, 0 deletions
diff --git a/indra/win_crash_logger/win_crash_logger.cpp b/indra/win_crash_logger/win_crash_logger.cpp
new file mode 100644
index 0000000000..e9198e8ff9
--- /dev/null
+++ b/indra/win_crash_logger/win_crash_logger.cpp
@@ -0,0 +1,915 @@
+/**
+ * @file win_crash_logger.cpp
+ * @brief Windows crash logger implementation
+ *
+ * Copyright (c) 2003-$CurrentYear$, Linden Research, Inc.
+ * $License$
+ */
+
+// win_crash_logger.cpp : Defines the entry point for the application.
+//
+
+#include "linden_common.h"
+#include "llcontrol.h"
+#include "stdafx.h"
+#include "resource.h"
+
+#include <stdio.h>
+#include <stdlib.h>
+#include <direct.h>
+#include <time.h>
+#include <sys/types.h>
+#include <sys/stat.h>
+#include <wininet.h>
+
+#include "indra_constants.h" // CRASH_BEHAVIOR_ASK, CRASH_SETTING_NAME
+#include "llerror.h"
+#include "lltimer.h"
+#include "lldir.h"
+
+#include "llstring.h"
+#include "lldxhardware.h"
+
+LLControlGroup gCrashSettings; // saved at end of session
+
+// Constants
+#define MAX_LOADSTRING 100
+const char* const SETTINGS_FILE_HEADER = "version";
+const S32 SETTINGS_FILE_VERSION = 101;
+
+// Functions
+LRESULT CALLBACK WndProc(HWND, UINT, WPARAM, LPARAM);
+bool handle_button_click(WORD button_id);
+S32 load_crash_behavior_setting();
+bool save_crash_behavior_setting(S32 crash_behavior);
+void send_crash_report();
+void write_debug(const char *str);
+void write_debug(std::string& str);
+
+// Global Variables:
+HINSTANCE hInst= NULL; // current instance
+TCHAR szTitle[MAX_LOADSTRING]; // The title bar text
+TCHAR szWindowClass[MAX_LOADSTRING]; // The title bar text
+
+LLString gUserText; // User's description of the problem
+time_t gStartTime = 0;
+HWND gHwndReport = NULL; // Send/Don't Send dialog
+HWND gHwndProgress = NULL; // Progress window
+HCURSOR gCursorArrow = NULL;
+HCURSOR gCursorWait = NULL;
+BOOL gFirstDialog = TRUE; // Are we currently handling the Send/Don't Send dialog?
+BOOL gCrashInPreviousExec = FALSE;
+FILE *gDebugFile = NULL;
+LLString gUserserver;
+WCHAR gProductName[512];
+
+//
+// Implementation
+//
+
+// Include product name in the window caption.
+void ProcessCaption(HWND hWnd)
+{
+ TCHAR templateText[1024];
+ TCHAR finalText[2048];
+ GetWindowText(hWnd, templateText, sizeof(templateText));
+ swprintf(finalText, templateText, gProductName);
+ SetWindowText(hWnd, finalText);
+}
+
+
+// Include product name in the diaog item text.
+void ProcessDlgItemText(HWND hWnd, int nIDDlgItem)
+{
+ TCHAR templateText[1024];
+ TCHAR finalText[2048];
+ GetDlgItemText(hWnd, nIDDlgItem, templateText, sizeof(templateText));
+ swprintf(finalText, templateText, gProductName);
+ SetDlgItemText(hWnd, nIDDlgItem, finalText);
+}
+
+int APIENTRY WinMain(HINSTANCE hInstance,
+ HINSTANCE hPrevInstance,
+ LPSTR lpCmdLine,
+ int nCmdShow)
+{
+ llinfos << "Starting crash reporter" << llendl;
+ // We assume that all the logs we're looking for reside on the current drive
+ gDirUtilp->initAppDirs("SecondLife");
+
+ // Default to the product name "Second Life" (this is overridden by the -name argument)
+ swprintf(gProductName, L"Second Life");
+
+ gCrashSettings.declareS32(CRASH_BEHAVIOR_SETTING, CRASH_BEHAVIOR_ASK, "Controls behavior when viewer crashes "
+ "(0 = ask before sending crash report, 1 = always send crash report, 2 = never send crash report)");
+
+ llinfos << "Loading crash behavior setting" << llendl;
+ S32 crash_behavior = load_crash_behavior_setting();
+
+ // In Win32, we need to generate argc and argv ourselves...
+ // Note: GetCommandLine() returns a potentially return a LPTSTR
+ // which can resolve to a LPWSTR (unicode string).
+ // (That's why it's different from lpCmdLine which is a LPSTR.)
+ // We don't currently do unicode, so call the non-unicode version
+ // directly.
+ llinfos << "Processing command line" << llendl;
+ LPSTR cmd_line_including_exe_name = GetCommandLineA();
+
+ const S32 MAX_ARGS = 100;
+ int argc = 0;
+ char *argv[MAX_ARGS];
+
+ char *token = NULL;
+ if( cmd_line_including_exe_name[0] == '\"' )
+ {
+ // Exe name is enclosed in quotes
+ token = strtok( cmd_line_including_exe_name, "\"" );
+ argv[argc++] = token;
+ token = strtok( NULL, " \t," );
+ }
+ else
+ {
+ // Exe name is not enclosed in quotes
+ token = strtok( cmd_line_including_exe_name, " \t," );
+ }
+
+ while( (token != NULL) && (argc < MAX_ARGS) )
+ {
+ argv[argc++] = token;
+ /* Get next token: */
+ if (*(token + strlen(token) + 1) == '\"')
+ {
+ token = strtok( NULL, "\"");
+ }
+ else
+ {
+ token = strtok( NULL, " \t," );
+ }
+ }
+
+ S32 i;
+ for (i=0; i<argc; i++)
+ {
+ if(!strcmp(argv[i], "-previous"))
+ {
+ llinfos << "Previous execution did not remove SecondLife.exec_marker" << llendl;
+ gCrashInPreviousExec = TRUE;
+ }
+
+ if(!strcmp(argv[i], "-dialog"))
+ {
+ llinfos << "Show the user dialog" << llendl;
+ crash_behavior = CRASH_BEHAVIOR_ASK;
+ }
+
+ if(!strcmp(argv[i], "-user"))
+ {
+ if ((i + 1) < argc)
+ {
+ i++;
+ gUserserver = argv[i];
+ llinfos << "Got userserver " << gUserserver << llendl;
+ }
+ }
+
+ if(!strcmp(argv[i], "-name"))
+ {
+ if ((i + 1) < argc)
+ {
+ i++;
+
+ mbstowcs(gProductName, argv[i], sizeof(gProductName));
+ llinfos << "Got product name " << argv[i] << llendl;
+ }
+ }
+ }
+
+ // If user doesn't want to send, bail out
+ if (crash_behavior == CRASH_BEHAVIOR_NEVER_SEND)
+ {
+ llinfos << "Crash behavior is never_send, quitting" << llendl;
+ return 0;
+ }
+
+ // Get the current time
+ time(&gStartTime);
+
+ llinfos << "Loading dialogs" << llendl;
+
+ // Store instance handle in our global variable
+ hInst = hInstance;
+
+ // Initialize global strings
+ LoadString(hInstance, IDS_APP_TITLE, szTitle, MAX_LOADSTRING);
+ LoadString(hInstance, IDC_WIN_CRASH_LOGGER, szWindowClass, MAX_LOADSTRING);
+
+ gCursorArrow = LoadCursor(NULL, IDC_ARROW);
+ gCursorWait = LoadCursor(NULL, IDC_WAIT);
+
+ // Register a window class that will be used by our dialogs
+ WNDCLASS wndclass;
+ wndclass.style = CS_HREDRAW | CS_VREDRAW;
+ wndclass.lpfnWndProc = WndProc;
+ wndclass.cbClsExtra = 0;
+ wndclass.cbWndExtra = DLGWINDOWEXTRA; // Required, since this is used for dialogs!
+ wndclass.hInstance = hInst;
+ wndclass.hIcon = LoadIcon(hInst, MAKEINTRESOURCE( IDI_WIN_CRASH_LOGGER ) );
+ wndclass.hCursor = gCursorArrow;
+ wndclass.hbrBackground = (HBRUSH) (COLOR_BTNFACE + 1);
+ wndclass.lpszMenuName = NULL;
+ wndclass.lpszClassName = szWindowClass;
+ RegisterClass( &wndclass );
+
+ // Note: parent hwnd is 0 (the desktop). No dlg proc. See Petzold (5th ed) HexCalc example, Chapter 11, p529
+ // win_crash_logger.rc has been edited by hand.
+ // Dialogs defined with CLASS "WIN_CRASH_LOGGER" (must be same as szWindowClass)
+
+ gHwndProgress = CreateDialog(hInst, MAKEINTRESOURCE(IDD_PROGRESS), 0, NULL);
+ ProcessCaption(gHwndProgress);
+ ShowWindow(gHwndProgress, SW_HIDE );
+
+ if (crash_behavior == CRASH_BEHAVIOR_ALWAYS_SEND)
+ {
+ ShowWindow(gHwndProgress, SW_SHOW );
+ send_crash_report();
+ return 0;
+ }
+
+ if (crash_behavior == CRASH_BEHAVIOR_ASK)
+ {
+ gHwndReport = CreateDialog(hInst, MAKEINTRESOURCE(IDD_REPORT), 0, NULL);
+
+ // Include the product name in the caption and various dialog items.
+ ProcessCaption(gHwndReport);
+ ProcessDlgItemText(gHwndReport, IDC_STATIC_WHATINFO);
+ ProcessDlgItemText(gHwndReport, IDC_STATIC_MOTIVATION);
+
+ // Update the header to include whether or not we crashed on the last run.
+ WCHAR header[2048];
+ if (gCrashInPreviousExec)
+ {
+ swprintf(header, L"%s appears to have crashed or frozen the last time it ran.", gProductName);
+ }
+ else
+ {
+ swprintf(header, L"%s appears to have crashed.", gProductName);
+ }
+ SetDlgItemText(gHwndReport, IDC_STATIC_HEADER, header);
+ ShowWindow(gHwndReport, SW_SHOW );
+
+ MSG msg;
+ while (GetMessage(&msg, NULL, 0, 0))
+ {
+ TranslateMessage(&msg);
+ DispatchMessage(&msg);
+ }
+ return msg.wParam;
+ }
+ else
+ {
+ llwarns << "Unknown crash behavior " << crash_behavior << llendl;
+ return 1;
+ }
+}
+
+
+LRESULT CALLBACK WndProc( HWND hwnd, UINT message, WPARAM wParam, LPARAM lParam )
+{
+ switch( message )
+ {
+ case WM_CREATE:
+ return 0;
+
+ case WM_COMMAND:
+ if( gFirstDialog )
+ {
+ WORD button_id = LOWORD(wParam);
+ bool handled = handle_button_click(button_id);
+ if (handled)
+ {
+ return 0;
+ }
+ }
+ break;
+
+ case WM_DESTROY:
+ // Closing the window cancels
+ PostQuitMessage(0);
+ return 0;
+ }
+
+ return DefWindowProc(hwnd, message, wParam, lParam);
+}
+
+
+bool handle_button_click(WORD button_id)
+{
+ // Is this something other than Send or Don't Send?
+ if (button_id != IDOK
+ && button_id != IDCANCEL)
+ {
+ return false;
+ }
+
+ // See if "do this next time" is checked and save state
+ S32 crash_behavior = CRASH_BEHAVIOR_ASK;
+ LRESULT result = SendDlgItemMessage(gHwndReport, IDC_CHECK_AUTO, BM_GETCHECK, 0, 0);
+ if (result == BST_CHECKED)
+ {
+ if (button_id == IDOK)
+ {
+ crash_behavior = CRASH_BEHAVIOR_ALWAYS_SEND;
+ }
+ else if (button_id == IDCANCEL)
+ {
+ crash_behavior = CRASH_BEHAVIOR_NEVER_SEND;
+ }
+ }
+ bool success = save_crash_behavior_setting(crash_behavior);
+ if (!success)
+ {
+ llwarns << "Failed to save crash settings" << llendl;
+ }
+
+ // We're done with this dialog.
+ gFirstDialog = FALSE;
+
+ // Send the crash report if requested
+ if (button_id == IDOK)
+ {
+ // Don't let users type anything. They believe the reports
+ // get read by humans, and get angry when we don't respond. JC
+ //WCHAR wbuffer[20000];
+ //GetDlgItemText(gHwndReport, // handle to dialog box
+ // IDC_EDIT1, // control identifier
+ // wbuffer, // pointer to buffer for text
+ // 20000 // maximum size of string
+ // );
+ //gUserText = wstring_to_utf8str(utf16str_to_wstring(wbuffer)).c_str();
+ //llinfos << gUserText << llendl;
+
+ // Activate and show the window.
+ ShowWindow(gHwndProgress, SW_SHOW);
+ // Try doing this second to make the progress window go frontmost.
+ ShowWindow(gHwndReport, SW_HIDE);
+
+ send_crash_report();
+ }
+
+ // Quit the app
+ PostQuitMessage(0);
+
+ return true;
+}
+
+
+class LLFileEncoder
+{
+public:
+ LLFileEncoder(const char *formname, const char *filename);
+
+ BOOL isValid() const { return mIsValid; }
+ LLString encodeURL(const S32 max_length = 0);
+public:
+ BOOL mIsValid;
+ LLString mFilename;
+ LLString mFormname;
+ S32 mBufLength;
+ U8 *mBuf;
+};
+
+LLString encode_string(const char *formname, const LLString &str);
+
+void update_messages()
+{
+ MSG msg;
+ while (PeekMessage(&msg, NULL, 0, 0, PM_REMOVE))
+ {
+ if (msg.message == WM_QUIT)
+ {
+ exit(0);
+ }
+ TranslateMessage(&msg);
+ DispatchMessage(&msg);
+ }
+}
+
+void sleep_and_pump_messages( U32 seconds )
+{
+ const U32 CYCLES_PER_SECOND = 10;
+ U32 cycles = seconds * CYCLES_PER_SECOND;
+ while( cycles-- )
+ {
+ update_messages();
+ ms_sleep(1000 / CYCLES_PER_SECOND);
+ }
+}
+
+
+void show_progress(const char* message)
+{
+ std::wstring msg = wstring_to_utf16str(utf8str_to_wstring(message));
+ if (gHwndProgress)
+ {
+ SendDlgItemMessage(gHwndProgress, // handle to destination window
+ IDC_LOG,
+ WM_SETTEXT, // message to send
+ FALSE, // undo option
+ (LPARAM)msg.c_str());
+ }
+}
+
+
+void send_crash_report()
+{
+ update_messages();
+ show_progress("Starting up...");
+ update_messages();
+
+ const S32 SL_MAX_SIZE = 100000; // Maximum size of the Second Life log file.
+
+ update_messages();
+
+ // Lots of silly variable, replicated for each log file.
+ std::string db_file_name; // debug.log
+ std::string sl_file_name; // SecondLife.log
+ std::string md_file_name; // minidump (SecondLife.dmp) file name
+ std::string st_file_name; // stats.log file
+ std::string si_file_name; // settings.ini file
+ std::string ml_file_name; // message.log file
+
+ LLFileEncoder *db_filep = NULL;
+ LLFileEncoder *sl_filep = NULL;
+ LLFileEncoder *st_filep = NULL;
+ LLFileEncoder *md_filep = NULL;
+ LLFileEncoder *si_filep = NULL;
+ LLFileEncoder *ml_filep = NULL;
+
+ // DX hardware probe blocks, so we can't cancel during it
+ SetCursor(gCursorWait);
+
+ // Need to do hardware detection before we grab the files, otherwise we don't send the debug log updates
+ // to the server (including the list of hardware).
+ update_messages();
+ show_progress("Detecting hardware, please wait...");
+ update_messages();
+ gDXHardware.setWriteDebugFunc(write_debug);
+ gDXHardware.getInfo(FALSE);
+ update_messages();
+ gDXHardware.dumpDevices();
+ update_messages();
+ fclose(gDebugFile);
+ gDebugFile = NULL;
+
+ // At this point we're responsive enough the user could click the close button
+ SetCursor(gCursorArrow);
+
+ ///////////////////////////////////
+ //
+ // We do the parsing for the debug_info file first, as that will
+ // give us the location of the SecondLife.log file.
+ //
+
+ // Figure out the filename of the debug log
+ db_file_name = gDirUtilp->getExpandedFilename(LL_PATH_LOGS,"debug_info.log");
+ db_filep = new LLFileEncoder("DB", db_file_name.c_str());
+
+ // Get the filename of the SecondLife.log file
+ char tmp_sl_name[256];
+ tmp_sl_name[0] = '\0';
+
+ update_messages();
+ show_progress("Looking for files...");
+ update_messages();
+
+ // Look for it in the debug_info.log file
+ if (db_filep->isValid())
+ {
+ sscanf((const char *)db_filep->mBuf, "SL Log: %[^\r\n]", tmp_sl_name);
+ }
+ else
+ {
+ delete db_filep;
+ db_filep = NULL;
+ }
+
+ if (gCrashInPreviousExec)
+ {
+ // If we froze, the crash log this time around isn't useful. Use the
+ // old one.
+ sl_file_name = gDirUtilp->getExpandedFilename(LL_PATH_LOGS,"SecondLife.old");
+ }
+ else if (tmp_sl_name[0])
+ {
+ // If debug_info.log gives us a valid log filename, use that.
+ sl_file_name = tmp_sl_name;
+ llinfos << "Using log file from debug log " << sl_file_name << llendl;
+ }
+ else
+ {
+ // Figure out the filename of the default second life log
+ sl_file_name = gDirUtilp->getExpandedFilename(LL_PATH_LOGS,"SecondLife.log");
+ }
+
+ // Now we get the SecondLife.log file if it's there
+ sl_filep = new LLFileEncoder("SL", sl_file_name.c_str());
+ if (!sl_filep->isValid())
+ {
+ delete sl_filep;
+ sl_filep = NULL;
+ }
+
+ update_messages();
+ show_progress("Looking for stats file...");
+ update_messages();
+
+ st_file_name = gDirUtilp->getExpandedFilename(LL_PATH_LOGS,"stats.log");
+ st_filep = new LLFileEncoder("ST", st_file_name.c_str());
+ if (!st_filep->isValid())
+ {
+ delete st_filep;
+ st_filep = NULL;
+ }
+
+ si_file_name = gDirUtilp->getExpandedFilename(LL_PATH_USER_SETTINGS,"settings.ini");
+ si_filep = new LLFileEncoder("SI", si_file_name.c_str());
+ if (!si_filep->isValid())
+ {
+ delete si_filep;
+ si_filep = NULL;
+ }
+
+ // Now we get the minidump
+ md_file_name = gDirUtilp->getExpandedFilename(LL_PATH_LOGS,"SecondLife.dmp");
+ md_filep = new LLFileEncoder("MD", md_file_name.c_str());
+ if (!md_filep->isValid())
+ {
+ delete md_filep;
+ md_filep = NULL;
+ }
+
+ // Now we get the message log
+ ml_file_name = gDirUtilp->getExpandedFilename(LL_PATH_LOGS,"message.log");
+ ml_filep = new LLFileEncoder("ML", ml_file_name.c_str());
+ if (!ml_filep->isValid())
+ {
+ delete ml_filep;
+ ml_filep = NULL;
+ }
+
+ LLString post_data;
+ LLString tmp_url_buf;
+
+ // Append the userserver
+ tmp_url_buf = encode_string("USER", gUserserver);
+ post_data += tmp_url_buf;
+ llinfos << "PostData:" << post_data << llendl;
+
+ if (gCrashInPreviousExec)
+ {
+ post_data.append(1, '&');
+ tmp_url_buf = encode_string("EF", "Y");
+ post_data += tmp_url_buf;
+ }
+
+ update_messages();
+ show_progress("Encoding data");
+ update_messages();
+ if (db_filep)
+ {
+ post_data.append(1, '&');
+ tmp_url_buf = db_filep->encodeURL();
+ post_data += tmp_url_buf;
+ llinfos << "Sending DB log file" << llendl;
+ }
+ else
+ {
+ llinfos << "Not sending DB log file" << llendl;
+ }
+ show_progress("Encoding data.");
+ update_messages();
+
+ if (sl_filep)
+ {
+ post_data.append(1, '&');
+ tmp_url_buf = sl_filep->encodeURL(SL_MAX_SIZE);
+ post_data += tmp_url_buf;
+ llinfos << "Sending SL log file" << llendl;
+ }
+ else
+ {
+ llinfos << "Not sending SL log file" << llendl;
+ }
+ show_progress("Encoding data..");
+ update_messages();
+
+ if (st_filep)
+ {
+ post_data.append(1, '&');
+ tmp_url_buf = st_filep->encodeURL(SL_MAX_SIZE);
+ post_data += tmp_url_buf;
+ llinfos << "Sending stats log file" << llendl;
+ }
+ else
+ {
+ llinfos << "Not sending stats log file" << llendl;
+ }
+ show_progress("Encoding data...");
+ update_messages();
+
+ if (md_filep)
+ {
+ post_data.append(1, '&');
+ tmp_url_buf = md_filep->encodeURL();
+ post_data += tmp_url_buf;
+ llinfos << "Sending minidump log file" << llendl;
+ }
+ else
+ {
+ llinfos << "Not sending minidump log file" << llendl;
+ }
+ show_progress("Encoding data....");
+ update_messages();
+
+ if (si_filep)
+ {
+ post_data.append(1, '&');
+ tmp_url_buf = si_filep->encodeURL();
+ post_data += tmp_url_buf;
+ llinfos << "Sending settings log file" << llendl;
+ }
+ else
+ {
+ llinfos << "Not sending settings.ini file" << llendl;
+ }
+ show_progress("Encoding data....");
+ update_messages();
+
+ if (ml_filep)
+ {
+ post_data.append(1, '&');
+ tmp_url_buf = ml_filep->encodeURL(SL_MAX_SIZE);
+ post_data += tmp_url_buf;
+ llinfos << "Sending message log file" << llendl;
+ }
+ else
+ {
+ llinfos << "Not sending message.log file" << llendl;
+ }
+ show_progress("Encoding data....");
+ update_messages();
+
+ if (gUserText.size())
+ {
+ post_data.append(1, '&');
+ tmp_url_buf = encode_string("UN", gUserText);
+ post_data += tmp_url_buf;
+ }
+
+ delete db_filep;
+ db_filep = NULL;
+ delete sl_filep;
+ sl_filep = NULL;
+ delete md_filep;
+ md_filep = NULL;
+
+ // Post data to web server
+ const S32 BUFSIZE = 65536;
+ HINTERNET hinet, hsession, hrequest;
+ char data[BUFSIZE];
+ unsigned long bytes_read;
+
+ llinfos << "Connecting to crash report server" << llendl;
+ update_messages();
+ show_progress("Connecting to server...");
+ update_messages();
+
+ // Init wininet subsystem
+ hinet = InternetOpen(L"LindenCrashReporter", INTERNET_OPEN_TYPE_PRECONFIG, NULL, NULL, 0);
+ if (hinet == NULL)
+ {
+ llinfos << "Couldn't open connection" << llendl;
+ sleep_and_pump_messages( 5 );
+// return FALSE;
+ }
+
+ hsession = InternetConnect(hinet,
+ L"secondlife.com",
+ INTERNET_DEFAULT_HTTP_PORT,
+ NULL,
+ NULL,
+ INTERNET_SERVICE_HTTP,
+ NULL,
+ NULL);
+
+ if (!hsession)
+ {
+ llinfos << "Couldn't talk to crash report server" << llendl;
+ }
+
+ hrequest = HttpOpenRequest(hsession, L"POST", L"/cgi-bin/viewer_crash_reporter2", NULL, L"", NULL, 0, 0);
+ if (!hrequest)
+ {
+ llinfos << "Couldn't open crash report URL!" << llendl;
+ }
+
+ llinfos << "Transmitting data" << llendl;
+ llinfos << "Bytes: " << (post_data.size()) << llendl;
+
+ update_messages();
+ show_progress("Transmitting data...");
+ update_messages();
+
+ BOOL ok = HttpSendRequest(hrequest, NULL, 0, (void *)(post_data.c_str()), post_data.size());
+ if (!ok)
+ {
+ llinfos << "Error posting data!" << llendl;
+ sleep_and_pump_messages( 5 );
+ }
+
+ llinfos << "Response from crash report server:" << llendl;
+ do
+ {
+ if (InternetReadFile(hrequest, data, BUFSIZE, &bytes_read))
+ {
+ if (bytes_read == 0)
+ {
+ // If InternetFileRead returns TRUE AND bytes_read == 0
+ // we've successfully downloaded the entire file
+ break;
+ }
+ else
+ {
+ data[bytes_read] = 0;
+ llinfos << data << llendl;
+ }
+ }
+ else
+ {
+ llinfos << "Couldn't read file!" << llendl;
+ sleep_and_pump_messages( 5 );
+// return FALSE;
+ }
+ } while(TRUE);
+
+ InternetCloseHandle(hrequest);
+ InternetCloseHandle(hsession);
+ InternetCloseHandle(hinet);
+ update_messages();
+ show_progress("Done.");
+ sleep_and_pump_messages( 3 );
+// return TRUE;
+}
+
+LLFileEncoder::LLFileEncoder(const char *form_name, const char *filename)
+{
+ mFormname = form_name;
+ mFilename = filename;
+ mIsValid = FALSE;
+ mBuf = NULL;
+
+ int res;
+
+ llstat stat_data;
+ res = LLFile::stat(mFilename.c_str(), &stat_data);
+ if (res)
+ {
+ llwarns << "File " << mFilename << " is missing!" << llendl;
+ return;
+ }
+
+ FILE *fp = NULL;
+ S32 buf_size = 0;
+ S32 count = 0;
+ while (count < 5)
+ {
+ buf_size = stat_data.st_size;
+ fp = LLFile::fopen(mFilename.c_str(), "rb");
+ if (!fp)
+ {
+ llwarns << "Can't open file " << mFilename << ", wait for a second" << llendl;
+ // Couldn't open the file, wait a bit and try again
+ count++;
+ ms_sleep(1000);
+ }
+ else
+ {
+ break;
+ }
+ }
+ if (!fp)
+ {
+ return;
+ }
+ U8 *buf = new U8[buf_size + 1];
+ fread(buf, 1, buf_size, fp);
+ fclose(fp);
+
+ mBuf = buf;
+ mBufLength = buf_size;
+
+ mIsValid = TRUE;
+}
+
+LLString LLFileEncoder::encodeURL(const S32 max_length)
+{
+ LLString result = mFormname;
+ result.append(1, '=');
+
+ S32 i = 0;
+
+ if (max_length)
+ {
+ if (mBufLength > max_length)
+ {
+ i = mBufLength - max_length;
+ }
+ }
+
+ S32 url_buf_size = 3*mBufLength + 1;
+ char *url_buf = new char[url_buf_size];
+
+ S32 cur_pos = 0;
+ for (; i < mBufLength; i++)
+ {
+ S32 byte_val = mBuf[i];
+ sprintf(url_buf + cur_pos, "%%%02x", byte_val);
+ cur_pos += 3;
+ }
+ url_buf[i*3] = 0;
+
+ result.append(url_buf);
+ delete[] url_buf;
+ return result;
+}
+
+LLString encode_string(const char *formname, const LLString &str)
+{
+ LLString result = formname;
+ result.append(1, '=');
+ // Not using LLString because of bad performance issues
+ S32 buf_size = str.size();
+ S32 url_buf_size = 3*str.size() + 1;
+ char *url_buf = new char[url_buf_size];
+
+ S32 cur_pos = 0;
+ S32 i;
+ for (i = 0; i < buf_size; i++)
+ {
+ sprintf(url_buf + cur_pos, "%%%02x", str[i]);
+ cur_pos += 3;
+ }
+ url_buf[i*3] = 0;
+
+ result.append(url_buf);
+ delete[] url_buf;
+ return result;
+}
+
+void write_debug(const char *str)
+{
+ if (!gDebugFile)
+ {
+ std::string debug_filename = gDirUtilp->getExpandedFilename(LL_PATH_LOGS,"debug_info.log");
+ llinfos << "Opening debug file " << debug_filename << llendl;
+ gDebugFile = LLFile::fopen(debug_filename.c_str(), "a+");
+ if (!gDebugFile)
+ {
+ fprintf(stderr, "Couldn't open %s: debug log to stderr instead.\n", debug_filename.c_str());
+ gDebugFile = stderr;
+ }
+ }
+ fprintf(gDebugFile, str);
+ fflush(gDebugFile);
+}
+
+void write_debug(std::string& str)
+{
+ write_debug(str.c_str());
+}
+
+S32 load_crash_behavior_setting()
+{
+ std::string filename = gDirUtilp->getExpandedFilename(LL_PATH_USER_SETTINGS, CRASH_SETTINGS_FILE);
+
+ gCrashSettings.loadFromFile(filename);
+
+ S32 value = gCrashSettings.getS32(CRASH_BEHAVIOR_SETTING);
+
+ if (value < CRASH_BEHAVIOR_ASK || CRASH_BEHAVIOR_NEVER_SEND < value) return CRASH_BEHAVIOR_ASK;
+
+ return value;
+}
+
+bool save_crash_behavior_setting(S32 crash_behavior)
+{
+ if (crash_behavior < CRASH_BEHAVIOR_ASK) return false;
+ if (crash_behavior > CRASH_BEHAVIOR_NEVER_SEND) return false;
+
+ gCrashSettings.setS32(CRASH_BEHAVIOR_SETTING, crash_behavior);
+ std::string filename = gDirUtilp->getExpandedFilename(LL_PATH_USER_SETTINGS, CRASH_SETTINGS_FILE);
+
+ gCrashSettings.saveToFile(filename, FALSE);
+
+ return true;
+}