updater.cpp

Go to the documentation of this file.
00001 
00032 //
00033 // Usage: updater -url <url> [-name <window_title>] [-program <program_name>] [-silent]
00034 //
00035 
00036 #include "linden_common.h"
00037 
00038 #include <windows.h>
00039 #include <wininet.h>
00040 
00041 #define BUFSIZE 8192
00042 
00043 int  gTotalBytesRead = 0;
00044 DWORD gTotalBytes = -1;
00045 HWND gWindow = NULL;
00046 WCHAR gProgress[256];
00047 char* gUpdateURL;
00048 char* gProgramName;
00049 char* gProductName;
00050 bool gIsSilent;
00051 
00052 #if _DEBUG
00053 FILE* logfile = 0;
00054 #endif
00055 
00056 char* wchars_to_utf8chars(WCHAR* in_chars)
00057 {
00058         int tlen = 0;
00059         WCHAR* twc = in_chars;
00060         while (*twc++ != 0)
00061         {
00062                 tlen++;
00063         }
00064         char* outchars = new char[tlen];
00065         char* res = outchars;
00066         for (int i=0; i<tlen; i++)
00067         {
00068                 int cur_char = (int)(*in_chars++);
00069                 if (cur_char < 0x80)
00070                 {
00071                         *outchars++ = (char)cur_char;
00072                 }
00073                 else
00074                 {
00075                         *outchars++ = '?';
00076                 }
00077         }
00078         *outchars = 0;
00079         return res;
00080 }
00081 
00082 int WINAPI get_url_into_file(WCHAR *uri, char *path, int *cancelled)
00083 {
00084         int success = FALSE;
00085         *cancelled = FALSE;
00086 
00087         HINTERNET hinet, hdownload;
00088         char data[BUFSIZE];             /* Flawfinder: ignore */
00089         unsigned long bytes_read;
00090 
00091 #if _DEBUG
00092         fprintf(logfile,"Opening '%s'\n",path);
00093         fflush(logfile);
00094 #endif  
00095 
00096         FILE* fp = fopen(path, "wb");           /* Flawfinder: ignore */
00097 
00098         if (!fp)
00099         {
00100 #if _DEBUG
00101                 fprintf(logfile,"Failed to open '%s'\n",path);
00102                 fflush(logfile);
00103 #endif  
00104                 return success;
00105         }
00106         
00107 #if _DEBUG
00108         fprintf(logfile,"Calling InternetOpen\n");
00109         fflush(logfile);
00110 #endif  
00111         // Init wininet subsystem
00112         hinet = InternetOpen(L"LindenUpdater", INTERNET_OPEN_TYPE_PRECONFIG, NULL, NULL, 0);
00113         if (hinet == NULL)
00114         {
00115                 return success;
00116         }
00117 
00118 #if _DEBUG
00119         fprintf(logfile,"Calling InternetOpenUrl: %s\n",wchars_to_utf8chars(uri));
00120         fflush(logfile);
00121 #endif  
00122         hdownload = InternetOpenUrl(hinet, uri, NULL, 0, INTERNET_FLAG_NEED_FILE, NULL);
00123         if (hdownload == NULL)
00124         {
00125 #if _DEBUG
00126                 DWORD err = GetLastError();
00127                 fprintf(logfile,"InternetOpenUrl Failed: %d\n",err);
00128                 fflush(logfile);
00129 #endif  
00130                 return success;
00131         }
00132 
00133         DWORD sizeof_total_bytes = sizeof(gTotalBytes);
00134         HttpQueryInfo(hdownload, HTTP_QUERY_CONTENT_LENGTH | HTTP_QUERY_FLAG_NUMBER, &gTotalBytes, &sizeof_total_bytes, NULL);
00135         
00136         DWORD total_bytes = 0;
00137         success = InternetQueryDataAvailable(hdownload, &total_bytes, 0, 0);
00138         if (success == FALSE)
00139         {
00140 #if _DEBUG
00141                 DWORD err = GetLastError();
00142                 fprintf(logfile,"InternetQueryDataAvailable Failed: %d bytes Err:%d\n",total_bytes,err);
00143                 fflush(logfile);
00144 #endif  
00145                 return success;
00146         }
00147 
00148         success = FALSE;
00149         while(!success && !(*cancelled))
00150         {
00151                 MSG msg;
00152 
00153 #if _DEBUG
00154                 fprintf(logfile,"Calling InternetReadFile\n");
00155                 fflush(logfile);
00156 #endif  
00157                 if (!InternetReadFile(hdownload, data, BUFSIZE, &bytes_read))
00158                 {
00159 #if _DEBUG
00160                         fprintf(logfile,"InternetReadFile Failed.\n");
00161                         fflush(logfile);
00162 #endif
00163                         // ...an error occurred
00164                         return FALSE;
00165                 }
00166 
00167 #if _DEBUG
00168                 if (!bytes_read)
00169                 {
00170                         fprintf(logfile,"InternetReadFile Read 0 bytes.\n");
00171                         fflush(logfile);
00172                 }
00173 #endif
00174 
00175 #if _DEBUG
00176                 fprintf(logfile,"Reading Data, bytes_read = %d\n",bytes_read);
00177                 fflush(logfile);
00178 #endif  
00179                 
00180                 if (bytes_read == 0)
00181                 {
00182                         // If InternetFileRead returns TRUE AND bytes_read == 0
00183                         // we've successfully downloaded the entire file
00184                         wsprintf(gProgress, L"Download complete.");
00185                         success = TRUE;
00186                 }
00187                 else
00188                 {
00189                         // write what we've got, then continue
00190                         fwrite(data, sizeof(char), bytes_read, fp);
00191 
00192                         gTotalBytesRead += int(bytes_read);
00193 
00194                         if (gTotalBytes != -1)
00195                                 wsprintf(gProgress, L"Downloaded: %d%%", 100 * gTotalBytesRead / gTotalBytes);
00196                         else
00197                                 wsprintf(gProgress, L"Downloaded: %dK", gTotalBytesRead / 1024);
00198 
00199                 }
00200 
00201 #if _DEBUG
00202                 fprintf(logfile,"Calling InvalidateRect\n");
00203                 fflush(logfile);
00204 #endif  
00205                 
00206                 // Mark the window as needing redraw (of the whole thing)
00207                 InvalidateRect(gWindow, NULL, TRUE);
00208 
00209                 // Do the redraw
00210 #if _DEBUG
00211                 fprintf(logfile,"Calling UpdateWindow\n");
00212                 fflush(logfile);
00213 #endif  
00214                 UpdateWindow(gWindow);
00215 
00216 #if _DEBUG
00217                 fprintf(logfile,"Calling PeekMessage\n");
00218                 fflush(logfile);
00219 #endif  
00220                 if (PeekMessage(&msg, NULL, 0, 0, PM_REMOVE))
00221                 {
00222                         TranslateMessage(&msg);
00223                         DispatchMessage(&msg);
00224 
00225                         if (msg.message == WM_QUIT)
00226                         {
00227                                 // bail out, user cancelled
00228                                 *cancelled = TRUE;
00229                         }
00230                 }
00231         }
00232 
00233 #if _DEBUG
00234         fprintf(logfile,"Calling InternetCloseHandle\n");
00235         fclose(logfile);
00236 #endif
00237         
00238         fclose(fp);
00239         InternetCloseHandle(hdownload);
00240         InternetCloseHandle(hinet);
00241 
00242         return success;
00243 }
00244 
00245 LRESULT CALLBACK WinProc(HWND hwnd, UINT message, WPARAM wparam, LPARAM lparam)
00246 {
00247         HDC hdc;                        // Drawing context
00248         PAINTSTRUCT ps;
00249 
00250         switch(message)
00251         {
00252         case WM_PAINT:
00253                 {
00254                         hdc = BeginPaint(hwnd, &ps);
00255 
00256                         RECT rect;
00257                         GetClientRect(hwnd, &rect);
00258                         DrawText(hdc, gProgress, -1, &rect, 
00259                                 DT_SINGLELINE | DT_CENTER | DT_VCENTER);
00260 
00261                         EndPaint(hwnd, &ps);
00262                         return 0;
00263                 }
00264         case WM_CLOSE:
00265         case WM_DESTROY:
00266                 // Get out of full screen
00267                 // full_screen_mode(false);
00268                 PostQuitMessage(0);
00269                 return 0;
00270         }
00271         return DefWindowProc(hwnd, message, wparam, lparam);
00272 }
00273 
00274 #define win_class_name L"FullScreen"
00275 
00276 int parse_args(int argc, char **argv)
00277 {
00278         int j;
00279 
00280         for (j = 1; j < argc; j++) 
00281         {
00282                 if ((!strcmp(argv[j], "-name")) && (++j < argc)) 
00283                 {
00284                         gProductName = argv[j];
00285                 }
00286                 else if ((!strcmp(argv[j], "-url")) && (++j < argc)) 
00287                 {
00288                         gUpdateURL = argv[j];
00289                 }
00290                 else if ((!strcmp(argv[j], "-program")) && (++j < argc)) 
00291                 {
00292                         gProgramName = argv[j];
00293                 }
00294                 else if (!strcmp(argv[j], "-silent"))
00295                 {
00296                         gIsSilent = true;
00297                 }
00298         }
00299 
00300         // If nothing was set, let the caller know.
00301         if (!gProductName && !gProgramName && !gIsSilent && !gUpdateURL)
00302         {
00303                 return 1;
00304         }
00305         return 0;
00306 }
00307         
00308 int WINAPI
00309 WinMain (HINSTANCE hInstance, HINSTANCE hPrevInstance, LPSTR lpCmdLine, int nShowCmd)
00310 {
00311         // Parse the command line.
00312         LPSTR cmd_line_including_exe_name = GetCommandLineA();
00313 
00314         const int MAX_ARGS = 100;
00315         int argc = 0;
00316         char* argv[MAX_ARGS];           /* Flawfinder: ignore */
00317 
00318 #if _DEBUG
00319         logfile = _wfopen(TEXT("updater.log"),TEXT("wt"));
00320         fprintf(logfile,"Parsing command arguments\n");
00321         fflush(logfile);
00322 #endif
00323         
00324         char *token = NULL;
00325         if( cmd_line_including_exe_name[0] == '\"' )
00326         {
00327                 // Exe name is enclosed in quotes
00328                 token = strtok( cmd_line_including_exe_name, "\"" );
00329                 argv[argc++] = token;
00330                 token = strtok( NULL, " \t," );
00331         }
00332         else
00333         {
00334                 // Exe name is not enclosed in quotes
00335                 token = strtok( cmd_line_including_exe_name, " \t," );
00336         }
00337 
00338         while( (token != NULL) && (argc < MAX_ARGS) )
00339         {
00340                 argv[argc++] = token;
00341                 /* Get next token: */
00342                 if (*(token + strlen(token) + 1) == '\"')               /* Flawfinder: ignore */
00343                 {
00344                         token = strtok( NULL, "\"");
00345                 }
00346                 else
00347                 {
00348                         token = strtok( NULL, " \t," );
00349                 }
00350         }
00351 
00352         gUpdateURL = NULL;
00353         gProgramName = NULL;
00354         gProductName = NULL;
00355         gIsSilent = false;
00356 
00358         //
00359         // Process command line arguments
00360         //
00361 
00362 #if _DEBUG
00363         fprintf(logfile,"Processing command arguments\n");
00364         fflush(logfile);
00365 #endif
00366         
00367         //
00368         // Parse the command line arguments
00369         //
00370         int parse_args_result = parse_args(argc, argv);
00371         WCHAR window_title[2048];
00372         if (gProductName)
00373         {
00374                 mbstowcs(window_title, gProductName, 2048);
00375                 wcscat(window_title, L" Updater");              /* Flawfinder: ignore */
00376         }
00377         else
00378         {
00379                 mbstowcs(window_title, "Second Life Updater", 2048);
00380         }
00381         
00382         WNDCLASSEX wndclassex = { 0 };
00383         DEVMODE dev_mode = { 0 };
00384         char update_exec_path[MAX_PATH];                /* Flawfinder: ignore */
00385         char *ptr;
00386 
00387         const int WINDOW_WIDTH = 250;
00388         const int WINDOW_HEIGHT = 100;
00389 
00390         wsprintf(gProgress, L"Connecting...");
00391 
00392         /* Init the WNDCLASSEX */
00393         wndclassex.cbSize = sizeof(WNDCLASSEX);
00394         wndclassex.style = CS_HREDRAW | CS_VREDRAW;
00395         wndclassex.hInstance = hInstance;
00396         wndclassex.lpfnWndProc = WinProc;
00397         wndclassex.hbrBackground = (HBRUSH) GetStockObject(WHITE_BRUSH);
00398         wndclassex.lpszClassName = win_class_name;
00399         
00400         RegisterClassEx(&wndclassex);
00401         
00402         // Get the size of the screen
00403         EnumDisplaySettings(NULL, ENUM_CURRENT_SETTINGS, &dev_mode);
00404         
00405         gWindow = CreateWindowEx(NULL, win_class_name, 
00406                 window_title,
00407                 WS_OVERLAPPEDWINDOW, 
00408                 CW_USEDEFAULT, 
00409                 CW_USEDEFAULT, 
00410                 WINDOW_WIDTH, 
00411                 WINDOW_HEIGHT,
00412                 NULL, NULL, hInstance, NULL);
00413 
00414         ShowWindow(gWindow, nShowCmd);
00415         UpdateWindow(gWindow);
00416 
00417         if (parse_args_result)
00418         {
00419                 MessageBox(gWindow, 
00420                                 L"Usage: updater -url <url> [-name <window_title>] [-program <program_name>] [-silent]",
00421                                 L"Usage", MB_OK);
00422                 return parse_args_result;
00423         }
00424 
00425         // Did we get a userserver to work with?
00426         if (!gUpdateURL)
00427         {
00428                 MessageBox(gWindow, L"Please specify the download url from the command line",
00429                         L"Error", MB_OK);
00430                 return 1;
00431         }
00432 
00433         // Can't feed GetTempPath into GetTempFile directly
00434         if (0 == GetTempPathA(MAX_PATH - 14, update_exec_path))
00435         {
00436                 MessageBox(gWindow, L"Problem with GetTempPath()",
00437                         L"Error", MB_OK);
00438                 return 1;
00439         }
00440         if (0 == GetTempFileNameA(update_exec_path, NULL, 0, update_exec_path))
00441         {
00442                 MessageBox(gWindow, L"Problem with GetTempFileName()",
00443                         L"Error", MB_OK);
00444                 return 1;
00445         }
00446         // Hack hack hack
00447         ptr = strrchr(update_exec_path, '.');
00448         *(ptr + 1) = 'e';
00449         *(ptr + 2) = 'x';
00450         *(ptr + 3) = 'e';
00451         *(ptr + 4) = 0;
00452 
00453         WCHAR update_uri[4096];
00454         mbstowcs(update_uri, gUpdateURL, 4096);
00455 
00456         int success;
00457         int cancelled;
00458 
00459         // Actually do the download
00460 #if _DEBUG
00461         fprintf(logfile,"Calling get_url_into_file\n");
00462         fflush(logfile);
00463 #endif  
00464         success = get_url_into_file(update_uri, update_exec_path, &cancelled);
00465 
00466         // WinInet can't tell us if we got a 404 or not.  Therefor, we check
00467         // for the size of the downloaded file, and assume that our installer
00468         // will always be greater than 1MB.
00469         if (gTotalBytesRead < (1024 * 1024) && ! cancelled)
00470         {
00471                 MessageBox(gWindow,
00472                         L"The Second Life auto-update has failed.\n"
00473                         L"The problem may be caused by other software installed \n"
00474                         L"on your computer, such as a firewall.\n"
00475                         L"Please visit http://secondlife.com/download/ \n"
00476                         L"to download the latest version of Second Life.\n",
00477                         NULL, MB_OK);
00478                 return 1;
00479         }
00480 
00481         if (cancelled)
00482         {
00483                 // silently exit
00484                 return 0;
00485         }
00486 
00487         if (!success)
00488         {
00489                 MessageBox(gWindow, 
00490                         L"Second Life download failed.\n"
00491                         L"Please try again later.", 
00492                         NULL, MB_OK);
00493                 return 1;
00494         }
00495 
00496         // Construct some parameters.
00497         char params[2048];              /* Flawfinder: ignore */
00498         if (gIsSilent && gProgramName)
00499         {
00500                 _snprintf(params, sizeof(params), "/S /P=\"%s\"", gProgramName);                /* Flawfinder: ignore */
00501                 params[2047] = '\0';
00502         }
00503         else if (gProgramName)
00504         {
00505                 _snprintf(params, sizeof(params), "/P=\"%s\"", gProgramName);           /* Flawfinder: ignore */
00506                 params[2047] = '\0';
00507         }
00508         else if (gIsSilent)
00509         {
00510                 sprintf(params, "/S");          /* Flawfinder: ignore */
00511         }
00512         else
00513         {
00514                 params[0] = '\0';
00515         }
00516                 
00517         if (32 >= (int) ShellExecuteA(gWindow, "open", update_exec_path, params, 
00518                 "C:\\", SW_SHOWDEFAULT))
00519         {
00520                 // No shit: less than or equal to 32 means failure
00521                 MessageBox(gWindow, L"ShellExecute failed.  Please try again later.", NULL, MB_OK);
00522                 return 1;
00523         }
00524 
00525         if (gIsSilent && gProductName)
00526         {
00527                 WCHAR message[2048];
00528                 WCHAR wproduct[2048];
00529                 mbstowcs(wproduct, gProductName, 2048);
00530 
00531                 wsprintf(message, 
00532                                 L"Updating %s.  %s will automatically start once the update is complete.  This may take a minute...",
00533                                 wproduct, wproduct);
00534                                 
00535                 MessageBox(gWindow, message, L"Download Complete", MB_OK);
00536         }
00537 
00538         return 0;
00539 }

Generated on Fri May 16 08:34:33 2008 for SecondLife by  doxygen 1.5.5