Skip to content

Commit

Permalink
First pass at new update installer.
Browse files Browse the repository at this point in the history
[#291]
  • Loading branch information
Martin Robinson committed Mar 1, 2010
1 parent d73a234 commit 4495c64
Show file tree
Hide file tree
Showing 7 changed files with 848 additions and 498 deletions.
346 changes: 346 additions & 0 deletions installation/net_installer/win32/common.cpp
@@ -0,0 +1,346 @@
/**
* Appcelerator Titanium - licensed under the Apache Public License 2
* see LICENSE in the root folder for details on the license.
* Copyright (c) 2009-2010 Appcelerator, Inc. All Rights Reserved.
*/
#include <kroll/utils/utils.h>
#include <windows.h>
#include <Wininet.h>
#include <cmath>
#include <sstream>
#include <string>

using namespace KrollUtils;
using KrollUtils::Application;
using KrollUtils::SharedApplication;
using KrollUtils::KComponentType;
using std::wstring;
using std::string;

// These functions must be defined in the installer implementation.
extern HWND GetInstallerHWND();
extern bool Progress(SharedDependency dependency, int percent);

void ShowError(const wstring& wmsg)
{
MessageBoxW(GetDesktopWindow(), wmsg.c_str(), L"Installation Failed",
MB_OK | MB_SYSTEMMODAL | MB_ICONEXCLAMATION);
}

void ShowError(const string& msg)
{
wstring wmsg(UTF8ToWide(msg));
ShowError(wmsg);
}

static wstring GetTempFilePathForDependency(SharedDependency dependency)
{
string filename;
switch (dependency->type)
{
case MODULE:
filename = "module-";
break;
case RUNTIME:
filename = "runtime-";
break;
case MOBILESDK:
filename = "mobilesdk-";
break;
case APP_UPDATE:
filename = "appupdate-";
break;
case SDK:
filename = "sdk-";
break;
}
filename.append(dependency->name);
filename.append("-");
filename.append(dependency->version);
filename.append(".zip");
static string tempdir;
if (tempdir.empty())
{
tempdir.assign(FileUtils::GetTempDirectory());
FileUtils::CreateDirectory(tempdir);
}

return UTF8ToWide(FileUtils::Join(tempdir.c_str(), filename.c_str(), 0));
}

static HINTERNET netHandle = 0;
static HINTERNET GetNetConnection()
{
if (netHandle)
return netHandle;

netHandle = InternetOpenW(
L"Mozilla/5.0 (compatible; Titanium_Downloader/0.1; Win32)",
INTERNET_OPEN_TYPE_PRECONFIG, 0, 0, 0);

if (!netHandle)
{
string error(Win32Utils::QuickFormatMessage(GetLastError()));
error = string("Could not open Internet connection: ") + error;
ShowError(error);
}

return netHandle;
}

void ShutdownNetConnection()
{
if (!netHandle)
return;

InternetCloseHandle(netHandle);
}

static void ShowLastDownloadEror()
{
DWORD bufferSize = 1024, error;
wchar_t staticErrorBuffer[1024];
wchar_t* errorBuffer = staticErrorBuffer;
BOOL success = InternetGetLastResponseInfo(&error, errorBuffer, &bufferSize);

if (!success && GetLastError() == ERROR_INSUFFICIENT_BUFFER)
{
errorBuffer = new wchar_t[bufferSize];
success = InternetGetLastResponseInfo(&error, errorBuffer, &bufferSize);
}

std::wstring errorString(L"Download failed: Unknown error");
if (success)
errorString = std::wstring(L"Download failed:") + errorBuffer;

ShowError(::WideToSystem(errorString));

if (errorBuffer != staticErrorBuffer)
delete [] errorBuffer;
}

bool DownloadDependency(SharedApplication app, SharedDependency dependency)
{
HINTERNET hINet = GetNetConnection();
if (!hINet)
{
ShowError("Could not establish an Internet connection.");
return false;
}

// If ths URL is a file on the local file system, then this dependency
// is packaged with the application ala the SDK Installer. In that case
// just pretend that we downloaded successfully. InstallDependency will
// take care of unpacking it properly.
wstring url(UTF8ToWide(app->GetURLForDependency(dependency)));
if (FileUtils::IsFile(url))
{
return true;
}

wstring outFilename(GetTempFilePathForDependency(dependency));
WCHAR szDecodedUrl[INTERNET_MAX_URL_LENGTH];
DWORD cchDecodedUrl = INTERNET_MAX_URL_LENGTH;
WCHAR szDomainName[INTERNET_MAX_URL_LENGTH];

// parse the URL
HRESULT hr = CoInternetParseUrl(url.c_str(), PARSE_DECODE,
URL_ENCODING_NONE, szDecodedUrl, INTERNET_MAX_URL_LENGTH,
&cchDecodedUrl, 0);
if (hr != S_OK)
{
string error = Win32Utils::QuickFormatMessage(GetLastError());
error = string("Could not decode URL: ") + error;
ShowError(error);
return false;
}

// figure out the domain/hostname
hr = CoInternetParseUrl(szDecodedUrl, PARSE_DOMAIN,
0, szDomainName, INTERNET_MAX_URL_LENGTH, &cchDecodedUrl, 0);
if (hr != S_OK)
{
string error = Win32Utils::QuickFormatMessage(GetLastError());
error = string("Could not parse domain: ") + error;
ShowError(error);
return false;
}

// start the HTTP fetch
HINTERNET hConnection = InternetConnectW(hINet, szDomainName,
80, L" ", L" ", INTERNET_SERVICE_HTTP, 0, 0 );
if (!hConnection)
{
string error = Win32Utils::QuickFormatMessage(GetLastError());
error = string("Could not start connection: ") + error;
ShowError(error);
return false;
}

wstring wurl(szDecodedUrl);
wstring path = wurl.substr(wurl.find(szDomainName)+wcslen(szDomainName));
HINTERNET hRequest = HttpOpenRequestW(hConnection, L"GET", path.c_str(),
0, 0, 0,
INTERNET_FLAG_IGNORE_CERT_CN_INVALID | // Disregard TLS certificate errors.
INTERNET_FLAG_IGNORE_CERT_DATE_INVALID |
INTERNET_FLAG_KEEP_CONNECTION | // Needed for NTLM authentication.
INTERNET_FLAG_NO_CACHE_WRITE | INTERNET_FLAG_RELOAD | // Always get the latest.
INTERNET_FLAG_NO_COOKIES, 0);

resend:
HttpSendRequest(hRequest, 0, 0, 0, 0);

DWORD dwErrorCode = hRequest ? ERROR_SUCCESS : GetLastError();
if (InternetErrorDlg(GetInstallerHWND(), hRequest, dwErrorCode,
FLAGS_ERROR_UI_FILTER_FOR_ERRORS |
FLAGS_ERROR_UI_FLAGS_CHANGE_OPTIONS |
FLAGS_ERROR_UI_FLAGS_GENERATE_DATA,
0) == ERROR_INTERNET_FORCE_RETRY)
goto resend;

CHAR buffer[2048];
DWORD bytesRead;
DWORD contentLength = 0;
DWORD statusCode = 0;
DWORD size = sizeof(contentLength);
BOOL success = HttpQueryInfo(hRequest,
HTTP_QUERY_STATUS_CODE | HTTP_QUERY_FLAG_NUMBER,
(LPDWORD) &statusCode, (LPDWORD) &size, 0);
if (!success || statusCode != 200)
{
string error = Win32Utils::QuickFormatMessage(GetLastError());
if (success)
{
std::ostringstream str;
str << "Invalid HTTP Status Code (" << statusCode << ")";
error = str.str();
}
error = string("Could not query info: ") + error;
ShowError(error);
return false;
}

success = HttpQueryInfo(hRequest,
HTTP_QUERY_CONTENT_LENGTH | HTTP_QUERY_FLAG_NUMBER,
(LPDWORD)&contentLength, (LPDWORD)&size, 0);
if (!success)
{
string error = Win32Utils::QuickFormatMessage(GetLastError());
error = string("Could not determine content length: ") + error;
ShowError(error);
return false;
}

// now stream the resulting HTTP into a file
HANDLE file = CreateFileW(outFilename.c_str(), GENERIC_WRITE,
0, 0, CREATE_ALWAYS, FILE_ATTRIBUTE_NORMAL, 0);
if (file == INVALID_HANDLE_VALUE)
{
string error = Win32Utils::QuickFormatMessage(GetLastError());
error = string("Could not open output file (") + WideToUTF8(outFilename) +
string("): ") + error;
ShowError(error);
return false;
}

// Keep reading from InternetReadFile as long as it's successful and the number
// of bytes read is greater than zero.
bool showError = true;
DWORD total = 0;
while ((success = InternetReadFile(hRequest, buffer, 2047, &bytesRead)) && bytesRead > 0)
{
// Be sure to Write the entire buffer into to the file.
DWORD bytesWritten = 0;
while (bytesWritten < bytesRead)
{
if (!WriteFile(file, buffer + bytesWritten,
bytesRead - bytesWritten, &bytesWritten, 0))
{
showError = success = false;
string error = Win32Utils::QuickFormatMessage(GetLastError());
error = string("Could write data to output file (") + WideToUTF8(outFilename) +
string("): ") + error;
ShowError(error);
break;
}
}

total += bytesRead;
printf("total: %i contentLength: %i\n", total, contentLength);
if (!Progress(dependency, (double)total/(double)contentLength*100))
{
showError = success = false;
break;
}
}

if (!success)
{
if (showError)
ShowLastDownloadEror();

CancelIo(file);
CloseHandle(file);
DeleteFileW(outFilename.c_str());
}
else
{
CloseHandle(file);
}

InternetCloseHandle(hRequest);
return success;
}

static bool UnzipProgressCallback(char* message, int current, int total, void* data)
{
SharedDependency* dep = (SharedDependency*) data;
int percent = total == 0 ? 0 : floor(((double)current/(double)total)*100);
return Progress(*dep, percent);
}

bool InstallDependency(SharedApplication app, SharedDependency dependency)
{
string destination(FileUtils::GetSystemRuntimeHomeDirectory());
if (dependency->type == MODULE)
{
destination = FileUtils::Join(destination.c_str(),
"modules", OS_NAME, dependency->name.c_str(),
dependency->version.c_str(), 0);
}
else if (dependency->type == RUNTIME)
{
destination = FileUtils::Join(destination.c_str(),
"runtime", OS_NAME, dependency->version.c_str(), 0);
}
else if (dependency->type == SDK || dependency->type == MOBILESDK)
{
// The SDKs unzip directly into the component installation path.
}
else if (dependency->type == APP_UPDATE)
{
// Application updates need to be unzipped into the application
// installation directory.
destination = app->path;
}
else
{
return false;
}

// Recursively create directories and then unzip the temporary
// download into that directory, calling the progress callback
// the entire time.
FileUtils::CreateDirectory(destination, true);

// First check if this dependency is packaged in the 'dist' folder
// ala the SDK installer. In that case, we didn't actual
string zipFile(app->GetURLForDependency(dependency));
if (!FileUtils::IsFile(zipFile))
zipFile = WideToUTF8(GetTempFilePathForDependency(dependency));

return FileUtils::Unzip(zipFile, destination, &UnzipProgressCallback, &dependency);
}



10 changes: 10 additions & 0 deletions installation/net_installer/win32/common.h
@@ -0,0 +1,10 @@
/**
* Appcelerator Titanium - licensed under the Apache Public License 2
* see LICENSE in the root folder for details on the license.
* Copyright (c) 2009-2010 Appcelerator, Inc. All Rights Reserved.
*/
void ShowError(const std::string& msg);
void ShowError(const std::wstring& wmsg);
void ShutdownNetConnection();
bool DownloadDependency(SharedApplication app, SharedDependency dependency);
bool InstallDependency(SharedApplication app, SharedDependency dependency);

0 comments on commit 4495c64

Please sign in to comment.