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

Generated on Thu Jul 1 06:09:59 2010 for Second Life Viewer by  doxygen 1.4.7