forked from appcelerator-archive/titanium_desktop
Commit
This commit does not belong to any branch on this repository, and may belong to a fork outside of the repository.
[#291]
- Loading branch information
Martin Robinson
committed
Mar 1, 2010
1 parent
d73a234
commit 4495c64
Showing
7 changed files
with
848 additions
and
498 deletions.
There are no files selected for viewing
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -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); | ||
} | ||
|
||
|
||
|
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -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); |
Oops, something went wrong.