Sic04/SicSetup/Sic_Setup/NSIS/Contrib/Inetc/inetc.cpp

1747 lines
54 KiB
C++

/*******************************************************
* FILE NAME: inetc.cpp
*
* Copyright (c) 2004-2015 Takhir Bedertdinov and NSIS contributors
*
* PURPOSE:
* ftp/http file download plug-in
* on the base of MS Inet API
* 4 GB limit (http support?)
*
* CHANGE HISTORY
*
* Author Date Modifications
* Takhir Bedertdinov
* Nov 11, 2004 Original
* Dec 17, 2004 Embedded edition -
* NSISdl GUI style as default
* (nsisdl.cpp code was partly used)
* Dec 17, 2004 MSI Banner style
* Feb 20, 2005 Resume download
* feature for big files and bad connections
* Mar 05, 2005 Proxy authentication
* and /POPUP caption prefix option
* Mar 25, 2005 Connect timeout option
* and FTP switched to passive mode
* Apr 18, 2005 Crack URL buffer size
* bug fixed (256->string_size)
* HTTP POST added
* Jun 06, 2005 IDOK on "Enter" key locked
* POST HTTP header added
* Jun 22, 2005 non-interaptable mode /nocancel
* and direct connect /noproxy
* Jun 29, 2005 post.php written and tested
* Jul 05, 2005 60 sec delay on WinInet detach problem
* solved (not fine, but works including
* installer exit and system reboot)
* Jul 08, 2005 'set foreground' finally removed
* Jul 26, 2005 POPUP translate option
* Aug 23, 2005 https service type in InternetConnect
* and "ignore certificate" flags
* Sep 30, 2005 https with bad certificate from old OS;
* Forbidden handling
* Dec 23, 2005 'put' entry point, new names, 12003
* ftp error handling (on ftp write permission)
* 405 http error (method not allowed)
* Mar 12, 2006 Internal authorization via InternetErrorDlg()
* and Unauthorized (401) handling.
* Jun 10, 2006 Caption text option for Resume download
* MessageBox
* Jun 24, 2006 HEAD method, silent mode clean up
* Sep 05, 2006 Center dialog code from Backland
* Sep 07, 2006 NSISdl crash fix /Backland idea/
* Sep 08, 2006 POST as dll entry point.
* Sep 21, 2006 parent dlg progr.bar style and font,
* nocancel via ws_sysmenu
* Sep 22, 2006 current lang IDCANCEL text, /canceltext
* and /useragent options
* Sep 24, 2006 .onInit improvements and defaults
* Nov 11, 2006 FTP path creation, root|current dir mgmt
* Jan 01, 2007 Global char[] cleanup, GetLastError() to
* status string on ERR_DIALOG, few MSVCRT replaces
* Jan 13, 2007 /HEADER option added
* Jan 28, 2007 _open -> CreateFile and related
* Feb 18, 2007 Speed calculating improved (pauses),
* /popup text parameter to hide URL
* Jun 07, 2007 Local file truncation added for download
* (CREATE_ALWAYS)
* Jun 11, 2007 FTP download permitted even if server rejects
* SIZE request (ProFTPD).
* Aug 11, 2007 Backland' fix for progress bar redraw/style
* issue in NSISdl display mode.
* Jan 09, 2008 {_trueparuex^}' fix - InternetSetFilePointer()
* returns -1 on error.
* /question option added for cancel question.
* Feb 15, 2008 PUT content-length file size fix
* Feb 17, 2008 char -> TCHAR replace for UNICODE option
* Feb 19, 2008 janekschwarz fix for HTTP PUT with auth
* CreateFile INVALID_HANDLE_VALUE on error fix
* Feb 20, 2008 base64 encoder update for unicode
* Feb 27, 2008 Unicode configurations added to VS 6 dsp
* Mar 20, 2008 HTTP PUT with proxy auth finally fixed
* FTP errors handling improved.
* HEAD bug fixed
* Mar 27, 2008 Details window hide/show in NSISdl mode
* Apr 10, 2008 Auth test method changed to HEAD for
* old proxy's
* Apr 30, 2008 InternetErrorDlg() ERROR_SUCESS on cancel
* click patched
* 3xx errors added to status list.
* May 20, 2008 InternetReadFile on cable disconnect patched
* May 20, 2008 Reply status "0" patch (name resolution?)
* Jul 15, 2008 HTTP 304 parsing. Incorrect size reported fix.
* Aug 21, 2009 Escape sequence convertion removed (caused
* error in signature with %2b requests)
* Marqueue progess bar style for unknown file size.
* Feb 04, 2010 Unicode POST patch - body converted to multibyte
* Jul 11, 2010 /FILE POST option added
* Nov 04, 2010 Disabled cookies and cache for cleanliness
* Feb 14, 2011 Fixed reget bug introduced in previous commit
* Feb 18, 2011 /NOCOOKIES option added
* Mar 02, 2011 User-agent buffer increased. Small memory leak fix
* Mar 23, 2011 Use caption on embedded progressbar - zenpoy
* Apr 05, 2011 reget fix - INTERNET_FLAG_RELOAD for first connect only
* Apr 27, 2011 /receivetimeout option added for big files and antivirus
* Jun 15, 2011 Stack clean up fix on cancel - zenpoy
* Oct 19, 2011 FTP PUT error parsing fix - tperquin
* Aug 19, 2013 Fix focus stealing when in silent - negativesir, JohnTHaller
* Jul 20, 2014 - 1.0.4.4 - Stuart 'Afrow UK' Welch
* /tostack & /tostackconv added
* Version information resource added
* Updated to NSIS 3.0 plugin API
* Upgraded to Visual Studio 2012
* 64-bit build added
* MSVCRT dependency removed
* Sep 04, 2015 - 1.0.5.0 - anders_k
* HTTPS connections are more secure by default
* Added /weaksecurity switch, reverts to old cert. security checks
* Sep 06, 2015 - 1.0.5.1 - anders_k
* Don't allow infinite FtpCreateDirectory tries
* Use memset intrinsic when possible to avoid VC code generation bug
* Oct 17, 2015 - 1.0.5.2 - anders_k
* Tries to set FTP mode to binary before querying the size.
* Calls FtpGetFileSize if it exists.
* Sep 24, 2018 - 1.0.5.3 - anders_k
* /tostackconv supports UTF-8 and UTF-16LE BOM sniffing and conversion.
*******************************************************/
#define _WIN32_WINNT 0x0500
#include <windows.h>
//#include <tchar.h>
#include <wininet.h>
#include <commctrl.h>
#include "pluginapi.h"
#include "resource.h"
#include <string.h> // strstr etc
#ifndef PBM_SETMARQUEE
#define PBM_SETMARQUEE (WM_USER + 10)
#define PBS_MARQUEE 0x08
#endif
#ifndef HTTP_QUERY_PROXY_AUTHORIZATION
#define HTTP_QUERY_PROXY_AUTHORIZATION 61
#endif
#ifndef SECURITY_FLAG_IGNORE_REVOCATION
#define SECURITY_FLAG_IGNORE_REVOCATION 0x00000080
#endif
#ifndef SECURITY_FLAG_IGNORE_UNKNOWN_CA
#define SECURITY_FLAG_IGNORE_UNKNOWN_CA 0x00000100
#endif
// IE 4 safety and VS 6 compatibility
typedef BOOL (__stdcall *FTP_CMD)(HINTERNET,BOOL,DWORD,LPCTSTR,DWORD,HINTERNET *);
FTP_CMD myFtpCommand;
#define PLUGIN_NAME TEXT("Inetc plug-in")
#define INETC_USERAGENT TEXT("NSIS_Inetc (Mozilla)")
#define PB_RANGE 400 // progress bar values range
#define PAUSE1_SEC 2 // transfer error indication time, for reget only
#define PAUSE2_SEC 3 // paused state time, increase this if need (60?)
#define PAUSE3_SEC 1 // pause after resume button pressed
#define NOT_AVAILABLE 0xffffffff
#define POST_HEADER TEXT("Content-Type: application/x-www-form-urlencoded")
#define PUT_HEADER TEXT("Content-Type: octet-stream\nContent-Length: %d")
#define INTERNAL_OK 0xFFEE
#define PROGRESS_MS 1000 // screen values update interval
#define DEF_QUESTION TEXT("Are you sure that you want to stop download?")
#define HOST_AUTH_HDR TEXT("Authorization: basic %s")
#define PROXY_AUTH_HDR TEXT("Proxy-authorization: basic %s")
//#define MY_WEAKSECURITY_CERT_FLAGS SECURITY_FLAG_IGNORE_UNKNOWN_CA | SECURITY_FLAG_IGNORE_REVOCATION | SECURITY_FLAG_IGNORE_CERT_DATE_INVALID | SECURITY_FLAG_IGNORE_CERT_CN_INVALID
#define MY_WEAKSECURITY_CERT_FLAGS SECURITY_FLAG_IGNORE_UNKNOWN_CA | SECURITY_FLAG_IGNORE_REVOCATION
#define MY_REDIR_FLAGS INTERNET_FLAG_IGNORE_REDIRECT_TO_HTTP | INTERNET_FLAG_IGNORE_REDIRECT_TO_HTTPS
#define MY_HTTPS_FLAGS (MY_REDIR_FLAGS | INTERNET_FLAG_SECURE)
enum STATUS_CODES {
ST_OK = 0,
ST_CONNECTING,
ST_DOWNLOAD,
ST_CANCELLED,
ST_URLOPEN,
// ST_OPENING,
ST_PAUSE,
ERR_TERMINATED,
ERR_DIALOG,
ERR_INETOPEN,
ERR_URLOPEN,
ERR_TRANSFER,
ERR_FILEOPEN,
ERR_FILEWRITE,
ERR_FILEREAD,
ERR_REGET,
ERR_CONNECT,
ERR_OPENREQUEST,
ERR_SENDREQUEST,
ERR_CRACKURL,
ERR_NOTFOUND,
ERR_THREAD,
ERR_PROXY,
ERR_FORBIDDEN,
ERR_NOTALLOWED,
ERR_REQUEST,
ERR_SERVER,
ERR_AUTH,
ERR_CREATEDIR,
ERR_PATH,
ERR_NOTMODIFIED,
ERR_REDIRECTION
};
static TCHAR szStatus[][32] = {
TEXT("OK"),TEXT("Connecting"),TEXT("Downloading"),TEXT("Cancelled"),TEXT("Connecting"), //TEXT("Opening URL")),
TEXT("Reconnect Pause"),TEXT("Terminated"),TEXT("Dialog Error"),TEXT("Open Internet Error"),
TEXT("Open URL Error"),TEXT("Transfer Error"),TEXT("File Open Error"),TEXT("File Write Error"),TEXT("File Read Error"),
TEXT("Reget Error"),TEXT("Connection Error"),TEXT("OpenRequest Error"),TEXT("SendRequest Error"),
TEXT("URL Parts Error"),TEXT("File Not Found (404)"),TEXT("CreateThread Error"),TEXT("Proxy Error (407)"),
TEXT("Access Forbidden (403)"),TEXT("Not Allowed (405)"),TEXT("Request Error"),TEXT("Server Error"),
TEXT("Unauthorized (401)"),TEXT("FtpCreateDir failed (550)"),TEXT("Error FTP path (550)"),TEXT("Not Modified"),
TEXT("Redirection")
};
HINSTANCE g_hInstance;
TCHAR fn[MAX_PATH]=TEXT(""),
*url = NULL,
*szAlias = NULL,
*szProxy = NULL,
*szHeader = NULL,
*szBanner = NULL,
*szQuestion = NULL,
szCancel[64]=TEXT(""),
szCaption[128]=TEXT(""),
szUserAgent[256]=TEXT(""),
szResume[256] = TEXT("Your internet connection seems to be not permitted or dropped out!\nPlease reconnect and click Retry to resume installation.");
CHAR *szPost = NULL,
post_fname[MAX_PATH] = "";
DWORD fSize = 0;
TCHAR *szToStack = NULL;
int status;
DWORD cnt = 0,
cntToStack = 0,
fs = 0,
timeout = 0,
receivetimeout = 0;
DWORD startTime, transfStart, openType;
bool silent, popup, resume, nocancel, noproxy, nocookies, convToStack, g_ignorecertissues;
HWND childwnd;
HWND hDlg;
bool fput = false, fhead = false;
#define Option_IgnoreCertIssues() ( g_ignorecertissues )
static FARPROC GetWininetProcAddress(LPCSTR Name)
{
return GetProcAddress(LoadLibraryA("WININET"), Name);
}
/*****************************************************
* FUNCTION NAME: sf(HWND)
* PURPOSE:
* moves HWND to top and activates it
* SPECIAL CONSIDERATIONS:
* commented because annoying
*****************************************************/
/*
void sf(HWND hw)
{
DWORD ctid = GetCurrentThreadId();
DWORD ftid = GetWindowThreadProcessId(GetForegroundWindow(), NULL);
AttachThreadInput(ftid, ctid, TRUE);
SetForegroundWindow(hw);
AttachThreadInput(ftid, ctid, FALSE);
}
*/
static TCHAR szUrl[64] = TEXT("");
static TCHAR szDownloading[64] = TEXT("Downloading %s");
static TCHAR szConnecting[64] = TEXT("Connecting ...");
static TCHAR szSecond[64] = TEXT("second");
static TCHAR szMinute[32] = TEXT("minute");
static TCHAR szHour[32] = TEXT("hour");
static TCHAR szPlural[32] = TEXT("s");
static TCHAR szProgress[128] = TEXT("%dkB (%d%%) of %dkB @ %d.%01dkB/s");
static TCHAR szRemaining[64] = TEXT(" (%d %s%s remaining)");
static TCHAR szBasic[128] = TEXT("");
static TCHAR szAuth[128] = TEXT("");
// is it possible to make it working with unicode strings?
/* Base64 encode one byte */
static TCHAR encode(unsigned char u) {
if(u < 26) return TEXT('A')+u;
if(u < 52) return TEXT('a')+(u-26);
if(u < 62) return TEXT('0')+(u-52);
if(u == 62) return TEXT('+');
return TEXT('/');
}
TCHAR *encode_base64(int size, TCHAR *src, TCHAR *dst) {
int i;
TCHAR *p;
if(!src)
return NULL;
if(!size)
size= lstrlen(src);
p = dst;
for(i=0; i<size; i+=3) {
unsigned char b1=0, b2=0, b3=0, b4=0, b5=0, b6=0, b7=0;
b1 = (unsigned char)src[i];
if(i+1<size)
b2 = (unsigned char)src[i+1];
if(i+2<size)
b3 = (unsigned char)src[i+2];
b4= b1>>2;
b5= ((b1&0x3)<<4)|(b2>>4);
b6= ((b2&0xf)<<2)|(b3>>6);
b7= b3&0x3f;
*p++= encode(b4);
*p++= encode(b5);
if(i+1<size) {
*p++= encode(b6);
} else {
*p++= TEXT('=');
}
if(i+2<size) {
*p++= encode(b7);
} else {
*p++= TEXT('=');
}
}
return dst;
}
/*****************************************************
* FUNCTION NAME: fileTransfer()
* PURPOSE:
* http/ftp file transfer itself
* for any protocol and both directions I guess
* SPECIAL CONSIDERATIONS:
*
*****************************************************/
void fileTransfer(HANDLE localFile,
HINTERNET hFile)
{
static BYTE data_buf[1024*8];
BYTE *dw;
DWORD rslt = 0;
DWORD bytesDone;
status = ST_DOWNLOAD;
while(status == ST_DOWNLOAD)
{
if(fput)
{
if(!ReadFile(localFile, data_buf, rslt = sizeof(data_buf), &bytesDone, NULL))
{
status = ERR_FILEREAD;
break;
}
if(bytesDone == 0) // EOF reached
{
status = ST_OK;
break;
}
while(bytesDone > 0)
{
dw = data_buf;
if(!InternetWriteFile(hFile, dw, bytesDone, &rslt) || rslt == 0)
{
status = ERR_TRANSFER;
break;
}
dw += rslt;
cnt += rslt;
bytesDone -= rslt;
}
}
else
{
if(!InternetReadFile(hFile, data_buf, sizeof(data_buf), &rslt))
{
status = ERR_TRANSFER;
break;
}
if(rslt == 0) // EOF reached or cable disconnect
{
// on cable disconnect returns TRUE and 0 bytes. is cnt == 0 OK (zero file size)?
// cannot check this if reply is chunked (no content-length, http 1.1)
status = (fs != NOT_AVAILABLE && cnt < fs) ? ERR_TRANSFER : ST_OK;
break;
}
if(szToStack)
{
for (DWORD i = 0; cntToStack < g_stringsize && i < rslt; i++, cntToStack++)
if (convToStack)
*((BYTE*)szToStack + cntToStack) = data_buf[i]; // Bytes
else
*(szToStack + cntToStack) = data_buf[i]; // ? to TCHARs
}
else if(!WriteFile(localFile, data_buf, rslt, &bytesDone, NULL) ||
rslt != bytesDone)
{
status = ERR_FILEWRITE;
break;
}
cnt += rslt;
}
}
}
/*****************************************************
* FUNCTION NAME: mySendRequest()
* PURPOSE:
* HttpSendRequestEx() sends headers only - for PUT
* We also can use InetWriteFile for POST body I guess
* SPECIAL CONSIDERATIONS:
*
*****************************************************/
int mySendRequest(HINTERNET hFile)
{
INTERNET_BUFFERS BufferIn = {0};
if(fput)
{
BufferIn.dwStructSize = sizeof( INTERNET_BUFFERS );
BufferIn.dwBufferTotal = fs;
return HttpSendRequestEx( hFile, &BufferIn, NULL, HSR_INITIATE, 0);
}
return HttpSendRequest(hFile, NULL, 0, szPost, fSize);
}
/*****************************************************
* FUNCTION NAME: queryStatus()
* PURPOSE:
* http status code comes before download (get) and
* after upload (put), so this is called from 2 places
* SPECIAL CONSIDERATIONS:
*
*****************************************************/
bool queryStatus(HINTERNET hFile)
{
TCHAR buf[256] = TEXT("");
DWORD rslt;
if(HttpQueryInfo(hFile, HTTP_QUERY_STATUS_CODE,
buf, &(rslt = sizeof(buf)), NULL))
{
buf[3] = 0;
if(lstrcmp(buf, TEXT("0")) == 0 || *buf == 0)
status = ERR_SENDREQUEST;
else if(lstrcmp(buf, TEXT("401")) == 0)
status = ERR_AUTH;
else if(lstrcmp(buf, TEXT("403")) == 0)
status = ERR_FORBIDDEN;
else if(lstrcmp(buf, TEXT("404")) == 0)
status = ERR_NOTFOUND;
else if(lstrcmp(buf, TEXT("407")) == 0)
status = ERR_PROXY;
else if(lstrcmp(buf, TEXT("405")) == 0)
status = ERR_NOTALLOWED;
else if(lstrcmp(buf, TEXT("304")) == 0)
status = ERR_NOTMODIFIED;
else if(*buf == TEXT('3'))
{
status = ERR_REDIRECTION;
wsprintf(szStatus[status] + lstrlen(szStatus[status]), TEXT(" (%s)"), buf);
}
else if(*buf == TEXT('4'))
{
status = ERR_REQUEST;
wsprintf(szStatus[status] + lstrlen(szStatus[status]), TEXT(" (%s)"), buf);
}
else if(*buf == TEXT('5'))
{
status = ERR_SERVER;
wsprintf(szStatus[status] + lstrlen(szStatus[status]), TEXT(" (%s)"), buf);
}
return true;
}
return false;
}
/*****************************************************
* FUNCTION NAME: openFtpFile()
* PURPOSE:
* control connection, size request, re-get lseek
* SPECIAL CONSIDERATIONS:
*
*****************************************************/
HINTERNET openFtpFile(HINTERNET hConn,
TCHAR *path)
{
TCHAR buf[256] = TEXT(""), *movp;
HINTERNET hFile;
DWORD rslt, err, gle;
bool https_req_ok = false;
/* reads connection / auth responce info and cleares 'control' buffer this way */
InternetGetLastResponseInfo(&err, buf, &(rslt = sizeof(buf)));
if(cnt == 0)
{
if(!fput) // we know local file size already
{
if (myFtpCommand)
{
/* Try to set the REPRESENTATION TYPE to I[mage] (Binary) because some servers
don't accept the SIZE command in ASCII mode */
myFtpCommand(hConn, false, FTP_TRANSFER_TYPE_ASCII, TEXT("TYPE I"), 0, &hFile);
}
/* too clever myFtpCommand returnes false on the valid TEXT("550 Not found/Not permitted" server answer,
to read answer I had to ignory returned false (!= 999999) :-(
GetLastError also possible, but MSDN description of codes is very limited */
wsprintf(buf, TEXT("SIZE %s"), path + 1);
if(myFtpCommand != NULL &&
myFtpCommand(hConn, false, FTP_TRANSFER_TYPE_ASCII, buf, 0, &hFile) != 9999 &&
memset(buf, 0, sizeof(buf)) != NULL &&
InternetGetLastResponseInfo(&err, buf, &(rslt = sizeof(buf))))
{
if(_tcsstr(buf, TEXT("213 ")))
{
fs = myatou(_tcschr(buf, TEXT(' ')) + 1);
}
/* stupid ProFTPD returns error on SIZE request. let's continue without size.
But IE knows some trick to get size from ProFTPD......
else if(mystrstr(buf, TEXT("550 TEXT("))
{
status = ERR_SIZE_NOT_PERMITTED;
return NULL;
}
*/
}
if(fs == 0)
{
fs = NOT_AVAILABLE;
}
}
}
else
{
wsprintf(buf, TEXT("REST %d"), cnt);
if(myFtpCommand == NULL ||
!myFtpCommand(hConn, false, FTP_TRANSFER_TYPE_BINARY, buf, 0, &hFile) ||
memset(buf, 0, sizeof(buf)) == NULL ||
!InternetGetLastResponseInfo(&err, buf, &(rslt = sizeof(buf))) ||
(_tcsstr(buf, TEXT("350")) == NULL && _tcsstr(buf, TEXT("110")) == NULL))
{
status = ERR_REGET;
return NULL;
}
}
if((hFile = FtpOpenFile(hConn, path + 1, fput ? GENERIC_WRITE : GENERIC_READ,
FTP_TRANSFER_TYPE_BINARY|INTERNET_FLAG_RELOAD,0)) == NULL)
{
gle = GetLastError();
*buf = 0;
InternetGetLastResponseInfo(&err, buf, &(rslt = sizeof(buf)));
// wrong path - dir may not exist or upload may be not allowed
// we use ftp://host//path (double /) to define path from FS root
if(fput && (_tcsstr(buf, TEXT("550")) != NULL || _tcsstr(buf, TEXT("553")) != NULL))
{
movp = path + 1;
if(*movp == TEXT('/')) movp++; // don't need to create root
for (UINT8 escapehatch = 0; ++escapehatch;) // Weak workaround for http://forums.winamp.com/showpost.php?p=3031692&postcount=513 bug
{
TCHAR *pbs = _tcschr(movp, TEXT('/'));
if (!pbs) break;
*pbs = TEXT('\0');
FtpCreateDirectory(hConn, path + 1);
InternetGetLastResponseInfo(&err, buf, &(rslt = sizeof(buf)));
*(movp + lstrlen(movp)) = TEXT('/');
movp = _tcschr(movp, TEXT('/')) + 1;
}
if(status != ERR_CREATEDIR &&
(hFile = FtpOpenFile(hConn, path + 1, GENERIC_WRITE,
FTP_TRANSFER_TYPE_BINARY|INTERNET_FLAG_RELOAD,0)) == NULL)
{
status = ERR_PATH;
if(InternetGetLastResponseInfo(&err, buf, &(rslt = sizeof(buf))))
lstrcpyn(szStatus[status], _tcsstr(buf, TEXT("550")), sizeof(szStatus[0]) / sizeof(TCHAR));
}
}
// may be firewall related error, let's give user time to disable it
else if(gle == 12003) // ERROR_INTERNET_EXTENDED_ERROR
{
if(_tcsstr(buf, TEXT("550")))
{
status = ERR_NOTFOUND;
lstrcpyn(szStatus[status], _tcsstr(buf, TEXT("550")), sizeof(szStatus[0]) / sizeof(TCHAR));
}
else
{
lstrcpyn(szStatus[status], buf, sizeof(szStatus[0]) / sizeof(TCHAR));
}
}
// timeout (firewall or dropped connection problem)
else if(gle == 12002) // ERROR_INTERNET_TIMEOUT
{
if(!silent)
resume = true;
status = ERR_URLOPEN;
}
}
else
InternetGetLastResponseInfo(&err, buf, &(rslt = sizeof(buf)));
if (hFile && NOT_AVAILABLE == fs)
{
FARPROC ftpgfs = GetWininetProcAddress("FtpGetFileSize"); // IE5+
if (ftpgfs)
{
DWORD shi, slo = ((DWORD(WINAPI*)(HINTERNET,DWORD*))ftpgfs)(hFile, &shi);
if (slo != -1 && !shi) fs = slo;
}
}
return hFile;
}
/*****************************************************
* FUNCTION NAME: openHttpFile()
* PURPOSE:
* file open, size request, re-get lseek
* SPECIAL CONSIDERATIONS:
*
*****************************************************/
HINTERNET openHttpFile(HINTERNET hConn, INTERNET_SCHEME nScheme, TCHAR *path)
{
TCHAR buf[256] = TEXT("");
HINTERNET hFile;
DWORD rslt, err;
bool first_attempt = true;;
// test connection for PUT, the only way to do this before sending data
// OPTIONS fails on HttpOpenRequest step for HTTPS
// but works for HEAD I guess
if(fput)// && nScheme != INTERNET_SCHEME_HTTPS)
{
// old proxy's may not support OPTIONS request, so changed to HEAD....
if((hFile = HttpOpenRequest(hConn, TEXT("HEAD"), path, NULL, NULL, NULL,
// if((hFile = HttpOpenRequest(hConn, TEXT("OPTIONS"), path, NULL, NULL, NULL,
INTERNET_FLAG_RELOAD | INTERNET_FLAG_KEEP_CONNECTION |
(nocookies ? (INTERNET_FLAG_NO_CACHE_WRITE | INTERNET_FLAG_NO_COOKIES) : 0), 0)) != NULL)
{
if(*szAuth)
{
wsprintf(buf, PROXY_AUTH_HDR, szAuth);
HttpAddRequestHeaders(hFile, buf, -1,
HTTP_ADDREQ_FLAG_ADD | HTTP_ADDREQ_FLAG_REPLACE);
}
resend_proxy1:
if(*szBasic)
{
wsprintf(buf, HOST_AUTH_HDR, szBasic);
HttpAddRequestHeaders(hFile, buf, -1,
HTTP_ADDREQ_FLAG_ADD | HTTP_ADDREQ_FLAG_REPLACE);
}
resend_auth1:
if(HttpSendRequest(hFile, NULL, 0, NULL, 0))
{
queryStatus(hFile);
// may be don't need to read all from socket, but this looks safer
while(InternetReadFile(hFile, buf, sizeof(buf), &rslt) && rslt > 0) {}
if(!silent && (status == ERR_PROXY || status == ERR_AUTH))// || status == ERR_FORBIDDEN))
{
rslt = InternetErrorDlg(hDlg, hFile,
ERROR_INTERNET_INCORRECT_PASSWORD,
FLAGS_ERROR_UI_FILTER_FOR_ERRORS |
FLAGS_ERROR_UI_FLAGS_GENERATE_DATA |
FLAGS_ERROR_UI_FLAGS_CHANGE_OPTIONS,
NULL);
if (rslt == ERROR_INTERNET_FORCE_RETRY)
{
status = ST_URLOPEN;
if(status == ERR_PROXY) goto resend_proxy1;
else goto resend_auth1;
}
else
{
status = ST_CANCELLED;
}
}
// no such file is OK for PUT. server first checks authentication
if(status == ERR_NOTFOUND || status == ERR_FORBIDDEN || status == ERR_NOTALLOWED)
{
// MessageBox(childwnd, TEXT("NOT_FOUND"), "", 0);
status = ST_URLOPEN;
}
// parameters might be updated during dialog popup
if(status == ST_URLOPEN)
{
*buf = 0;
if(HttpQueryInfo(hFile, HTTP_QUERY_AUTHORIZATION, buf, &(rslt = sizeof(buf)), NULL) && *buf)
lstrcpyn(szBasic, buf, rslt);
*buf = 0;
if(HttpQueryInfo(hFile, HTTP_QUERY_PROXY_AUTHORIZATION, buf, &(rslt = sizeof(buf)), NULL) && *buf)
lstrcpyn(szAuth, buf, rslt);
}
}
else status = ERR_SENDREQUEST;
InternetCloseHandle(hFile);
}
else status = ERR_OPENREQUEST;
}
// request itself
if(status == ST_URLOPEN)
{
DWORD secflags = nScheme == INTERNET_SCHEME_HTTPS ? MY_HTTPS_FLAGS : 0;
if (Option_IgnoreCertIssues()) secflags |= MY_WEAKSECURITY_CERT_FLAGS;
DWORD cokflags = nocookies ? (INTERNET_FLAG_NO_CACHE_WRITE | INTERNET_FLAG_NO_COOKIES) : 0;
if((hFile = HttpOpenRequest(hConn, fput ? TEXT("PUT") : (fhead ? TEXT("HEAD") : (szPost ? TEXT("POST") : NULL)),
path, NULL, NULL, NULL,
// INTERNET_FLAG_RELOAD conflicts with reget - hidden re-read from beginning has place
// INTERNET_FLAG_RESYNCHRONIZE // note - sync may not work with some http servers
// reload on first connect (and any req. except GET), just continue on resume.
// HTTP Proxy still is a problem for reget
(cnt ? 0 : INTERNET_FLAG_RELOAD) | INTERNET_FLAG_KEEP_CONNECTION | cokflags | secflags, 0)) != NULL)
{
if(*szAuth)
{
wsprintf(buf, PROXY_AUTH_HDR, szAuth);
HttpAddRequestHeaders(hFile, buf, -1,
HTTP_ADDREQ_FLAG_ADD | HTTP_ADDREQ_FLAG_REPLACE);
}
resend_proxy2:
if(szPost != NULL)
HttpAddRequestHeaders(hFile, POST_HEADER,
-1, HTTP_ADDREQ_FLAG_ADD | HTTP_ADDREQ_FLAG_REPLACE);
if(*post_fname)
HttpAddRequestHeadersA(hFile, post_fname,
-1, HTTP_ADDREQ_FLAG_ADD | HTTP_ADDREQ_FLAG_REPLACE);
if(szHeader != NULL)
HttpAddRequestHeaders(hFile, szHeader, -1,
HTTP_ADDREQ_FLAG_ADD | HTTP_ADDREQ_FLAG_REPLACE);
if(*szBasic)
{
wsprintf(buf, HOST_AUTH_HDR, szBasic);
HttpAddRequestHeaders(hFile, buf, -1,
HTTP_ADDREQ_FLAG_ADD | HTTP_ADDREQ_FLAG_REPLACE);
}
if(fput)
{
wsprintf(buf, PUT_HEADER, fs);
HttpAddRequestHeaders(hFile, buf, -1,
HTTP_ADDREQ_FLAG_ADD | HTTP_ADDREQ_FLAG_REPLACE);
}
resend_auth2:
first_attempt = true;
if(nScheme == INTERNET_SCHEME_HTTPS)
{
if(!mySendRequest(hFile))
{
InternetQueryOption (hFile, INTERNET_OPTION_SECURITY_FLAGS,
(LPVOID)&rslt, &(err = sizeof(rslt)));
rslt |= Option_IgnoreCertIssues() ? MY_WEAKSECURITY_CERT_FLAGS : 0;
InternetSetOption (hFile, INTERNET_OPTION_SECURITY_FLAGS,
&rslt, sizeof(rslt) );
}
else first_attempt = false;
}
// https Request answer may be after optional second Send only on Win98
if(!first_attempt || mySendRequest(hFile))
{
// no status for PUT - headers were sent only. And not need to get size / set position
if(!fput)
{
queryStatus(hFile);
if(!silent && (status == ERR_PROXY || status == ERR_AUTH))
{
rslt = InternetErrorDlg(hDlg, hFile,
ERROR_INTERNET_INCORRECT_PASSWORD,
FLAGS_ERROR_UI_FILTER_FOR_ERRORS |
FLAGS_ERROR_UI_FLAGS_GENERATE_DATA |
FLAGS_ERROR_UI_FLAGS_CHANGE_OPTIONS,
NULL);
if (rslt == ERROR_INTERNET_FORCE_RETRY)
{
status = ST_URLOPEN;
if(status == ERR_PROXY) goto resend_proxy2;
else goto resend_auth2;
}
else
status = ST_CANCELLED;
}
// get size / set position
if(status == ST_URLOPEN)
{
if(cnt == 0)
{
if(HttpQueryInfo(hFile, HTTP_QUERY_CONTENT_LENGTH, buf,
&(rslt = sizeof(buf)), NULL))
fs = myatou(buf);
else
fs = NOT_AVAILABLE;
}
else
{
if((int)InternetSetFilePointer(hFile, cnt, NULL, FILE_BEGIN, 0) == -1)
status = ERR_REGET;
}
}
}
}
else
{
if(!queryStatus(hFile))
status = ERR_SENDREQUEST;
}
}
else status = ERR_OPENREQUEST;
}
return hFile;
}
/*****************************************************
* FUNCTION NAME: inetTransfer()
* PURPOSE:
* http/ftp file transfer
* SPECIAL CONSIDERATIONS:
*
*****************************************************/
DWORD __stdcall inetTransfer(void *hw)
{
HINTERNET hSes, hConn, hFile;
HANDLE localFile = NULL;
HWND hDlg = (HWND)hw;
DWORD lastCnt, rslt, err;
static TCHAR hdr[2048];
TCHAR *host = (TCHAR*)LocalAlloc(LPTR, g_stringsize * sizeof(TCHAR)),
*path = (TCHAR*)LocalAlloc(LPTR, g_stringsize * sizeof(TCHAR)),
*params = (TCHAR*)LocalAlloc(LPTR, g_stringsize * sizeof(TCHAR)),
*user = (TCHAR*)LocalAlloc(LPTR, g_stringsize * sizeof(TCHAR)),
*passwd = (TCHAR*)LocalAlloc(LPTR, g_stringsize * sizeof(TCHAR));
URL_COMPONENTS uc = {sizeof(URL_COMPONENTS), NULL, 0,
(INTERNET_SCHEME)0, host, g_stringsize, 0 , user, g_stringsize,
passwd, g_stringsize, path, g_stringsize, params, g_stringsize};
if((hSes = InternetOpen(szUserAgent, openType, szProxy, NULL, 0)) != NULL)
{
if(InternetQueryOption(hSes, INTERNET_OPTION_CONNECTED_STATE, &(rslt=0),
&(lastCnt=sizeof(DWORD))) &&
(rslt & INTERNET_STATE_DISCONNECTED_BY_USER))
{
INTERNET_CONNECTED_INFO ci = {INTERNET_STATE_CONNECTED, 0};
InternetSetOption(hSes,
INTERNET_OPTION_CONNECTED_STATE, &ci, sizeof(ci));
}
if(timeout > 0)
lastCnt = InternetSetOption(hSes, INTERNET_OPTION_CONNECT_TIMEOUT, &timeout, sizeof(timeout));
if(receivetimeout > 0)
InternetSetOption(hSes, INTERNET_OPTION_RECEIVE_TIMEOUT, &receivetimeout, sizeof(receivetimeout));
// 60 sec WinInet.dll detach delay on socket time_wait fix
myFtpCommand = (FTP_CMD) GetWininetProcAddress(
#ifdef UNICODE
"FtpCommandW"
#else
"FtpCommandA"
#endif
);
while(!popstring(url) && lstrcmpi(url, TEXT("/end")) != 0)
{
// too many customers requested not to do this
// sf(hDlg);
if(popstring(fn) != 0 || lstrcmpi(url, TEXT("/end")) == 0) break;
status = ST_CONNECTING;
cnt = fs = *host = *user = *passwd = *path = *params = 0;
PostMessage(hDlg, WM_TIMER, 1, 0); // show url & fn, do it sync
if(szToStack || (localFile = CreateFile(fn, fput ? GENERIC_READ : GENERIC_WRITE, FILE_SHARE_READ,
NULL, fput ? OPEN_EXISTING : CREATE_ALWAYS, 0, NULL)) != INVALID_HANDLE_VALUE)
{
uc.dwHostNameLength = uc.dwUserNameLength = uc.dwPasswordLength =
uc.dwUrlPathLength = uc.dwExtraInfoLength = g_stringsize;
if(fput)
{
fs = GetFileSize(localFile, NULL);
}
if(InternetCrackUrl(url, 0, 0/*ICU_ESCAPE*/ , &uc))
{
// auth headers for HTTP PUT seems to be lost, preparing encoded login:password
if(*user && *passwd)
{
wsprintf(hdr, TEXT("%s:%s"), user, passwd);
// does unicode version of encoding works correct?
// are user and passwd ascii only?
encode_base64(lstrlen(hdr), hdr, szBasic);
*hdr = 0;
}
lstrcat(path, params); // BUGBUG: Could overflow path?
transfStart = GetTickCount();
do
{
// re-PUT to already deleted tmp file on http server is not possible.
// the same with POST - must re-send data to server. for 'resume' loop
if((fput && uc.nScheme != INTERNET_SCHEME_FTP) || szPost)
{
cnt = 0;
SetFilePointer(localFile, 0, NULL, FILE_BEGIN);
}
status = ST_CONNECTING;
lastCnt = cnt;
if((hConn = InternetConnect(hSes, host, uc.nPort,
lstrlen(user) > 0 ? user : NULL,
lstrlen(passwd) > 0 ? passwd : NULL,
uc.nScheme == INTERNET_SCHEME_FTP ? INTERNET_SERVICE_FTP : INTERNET_SERVICE_HTTP,
uc.nScheme == INTERNET_SCHEME_FTP ? INTERNET_FLAG_PASSIVE : 0, 0)) != NULL)
{
status = ST_URLOPEN;
hFile = uc.nScheme == INTERNET_SCHEME_FTP ?
openFtpFile(hConn, path) : openHttpFile(hConn, uc.nScheme, path);
if(status != ST_URLOPEN && hFile != NULL)
{
InternetCloseHandle(hFile);
hFile = NULL;
}
if(hFile != NULL)
{
if(fhead)
{// repeating calls clear headers..
if(HttpQueryInfo(hFile, HTTP_QUERY_RAW_HEADERS_CRLF, hdr, &(rslt=2048), NULL))
{
if(szToStack)
{
for (DWORD i = 0; cntToStack < g_stringsize && i < rslt; i++, cntToStack++)
*(szToStack + cntToStack) = hdr[i]; // ASCII to TCHAR
}
else
{
WriteFile(localFile, hdr, rslt, &lastCnt, NULL);
}
}
status = ST_OK;
}
else
{
HWND hBar = GetDlgItem(hDlg, IDC_PROGRESS1);
SendDlgItemMessage(hDlg, IDC_PROGRESS1, PBM_SETPOS, 0, 0);
SetWindowText(GetDlgItem(hDlg, IDC_STATIC5), fs == NOT_AVAILABLE ? TEXT("Not Available") : TEXT(""));
SetWindowText(GetDlgItem(hDlg, IDC_STATIC4), fs == NOT_AVAILABLE ? TEXT("Unknown") : TEXT(""));
SetWindowLong(hBar, GWL_STYLE, fs == NOT_AVAILABLE ?
(GetWindowLong(hBar, GWL_STYLE) | PBS_MARQUEE) : (GetWindowLong(hBar, GWL_STYLE) & ~PBS_MARQUEE));
SendDlgItemMessage(hDlg, IDC_PROGRESS1, PBM_SETMARQUEE, (WPARAM)(fs == NOT_AVAILABLE ? 1 : 0), (LPARAM)50 );
fileTransfer(localFile, hFile);
if(fput && uc.nScheme != INTERNET_SCHEME_FTP)
{
rslt = HttpEndRequest(hFile, NULL, 0, 0);
queryStatus(hFile);
}
}
InternetCloseHandle(hFile);
}
InternetCloseHandle(hConn);
}
else
{
status = ERR_CONNECT;
if(uc.nScheme == INTERNET_SCHEME_FTP &&
InternetGetLastResponseInfo(&err, hdr, &(rslt = sizeof(hdr))) &&
_tcsstr(hdr, TEXT("530")))
{
lstrcpyn(szStatus[status], _tcsstr(hdr, TEXT("530")), sizeof(szStatus[0]) / sizeof(TCHAR));
}
else
{
rslt = GetLastError();
if((rslt == 12003 || rslt == 12002) && !silent)
resume = true;
}
}
} while(((!fput || uc.nScheme == INTERNET_SCHEME_FTP) &&
cnt > lastCnt &&
status == ERR_TRANSFER &&
SleepEx(PAUSE1_SEC * 1000, false) == 0 &&
(status = ST_PAUSE) != ST_OK &&
SleepEx(PAUSE2_SEC * 1000, false) == 0)
|| (resume &&
status != ST_OK &&
status != ST_CANCELLED &&
status != ERR_NOTFOUND &&
ShowWindow(hDlg, SW_HIDE) != -1 &&
MessageBox(GetParent(hDlg), szResume, *szCaption ? szCaption : PLUGIN_NAME, MB_RETRYCANCEL|MB_ICONWARNING) == IDRETRY &&
(status = ST_PAUSE) != ST_OK &&
ShowWindow(hDlg, silent ? SW_HIDE : SW_SHOW) == false &&
SleepEx(PAUSE3_SEC * 1000, false) == 0));
}
else status = ERR_CRACKURL;
CloseHandle(localFile);
if(!fput && status != ST_OK && !szToStack)
{
rslt = DeleteFile(fn);
break;
}
}
else status = ERR_FILEOPEN;
}
InternetCloseHandle(hSes);
if (lstrcmpi(url, TEXT("/end"))==0)
pushstring(url);
}
else status = ERR_INETOPEN;
LocalFree(host);
LocalFree(path);
LocalFree(user);
LocalFree(passwd);
LocalFree(params);
if(IsWindow(hDlg))
PostMessage(hDlg, WM_COMMAND, MAKELONG(IDOK, INTERNAL_OK), 0);
return status;
}
/*****************************************************
* FUNCTION NAME: fsFormat()
* PURPOSE:
* formats DWORD (max 4 GB) file size for dialog, big MB
* SPECIAL CONSIDERATIONS:
*
*****************************************************/
void fsFormat(DWORD bfs, TCHAR *b)
{
if(bfs == NOT_AVAILABLE)
lstrcpy(b, TEXT("???"));
else if(bfs == 0)
lstrcpy(b, TEXT("0"));
else if(bfs < 10 * 1024)
wsprintf(b, TEXT("%u bytes"), bfs);
else if(bfs < 10 * 1024 * 1024)
wsprintf(b, TEXT("%u kB"), bfs / 1024);
else wsprintf(b, TEXT("%u MB"), (bfs / 1024 / 1024));
}
/*****************************************************
* FUNCTION NAME: progress_callback
* PURPOSE:
* old-style progress bar text updates
* SPECIAL CONSIDERATIONS:
*
*****************************************************/
void progress_callback(void)
{
static TCHAR buf[1024] = TEXT(""), b[1024] = TEXT("");
int time_sofar = max(1, (GetTickCount() - transfStart) / 1000);
int bps = cnt / time_sofar;
int remain = (cnt > 0 && fs != NOT_AVAILABLE) ? (MulDiv(time_sofar, fs, cnt) - time_sofar) : 0;
TCHAR *rtext=szSecond;
if(remain < 0) remain = 0;
if (remain >= 60)
{
remain/=60;
rtext=szMinute;
if (remain >= 60)
{
remain/=60;
rtext=szHour;
}
}
wsprintf(buf,
szProgress,
cnt/1024,
fs > 0 && fs != NOT_AVAILABLE ? MulDiv(100, cnt, fs) : 0,
fs != NOT_AVAILABLE ? fs/1024 : 0,
bps/1024,((bps*10)/1024)%10
);
if (remain) wsprintf(buf + lstrlen(buf),
szRemaining,
remain,
rtext,
remain==1?TEXT(""):szPlural
);
SetDlgItemText(hDlg, IDC_STATIC1, (cnt == 0 || status == ST_CONNECTING) ? szConnecting : buf);
if(fs > 0 && fs != NOT_AVAILABLE)
SendMessage(GetDlgItem(hDlg, IDC_PROGRESS1), PBM_SETPOS, MulDiv(cnt, PB_RANGE, fs), 0);
if (*szCaption == 0)
wsprintf(buf, szDownloading, _tcsrchr(fn, TEXT('\\')) ? _tcsrchr(fn, TEXT('\\')) + 1 : fn);
else
wsprintf(buf, TEXT("%s"),szCaption);
HWND hwndS = GetDlgItem(childwnd, 1006);
if(!silent && hwndS != NULL && IsWindow(hwndS))
{
GetWindowText(hwndS, b, sizeof(b));
if(lstrcmp(b, buf) != 0)
SetWindowText(hwndS, buf);
}
}
/*****************************************************
* FUNCTION NAME: onTimer()
* PURPOSE:
* updates text fields every second
* SPECIAL CONSIDERATIONS:
*
*****************************************************/
void onTimer(HWND hDlg)
{
TCHAR b[128];
DWORD ct = (GetTickCount() - transfStart) / 1000,
tt = (GetTickCount() - startTime) / 1000;
// dialog window caption
wsprintf(b, TEXT("%s - %s"), *szCaption ? szCaption : PLUGIN_NAME, szStatus[status]);
if(fs > 0 && fs != NOT_AVAILABLE && status == ST_DOWNLOAD)
{
wsprintf(b + lstrlen(b), TEXT(" %d%%"), MulDiv(100, cnt, fs));
}
if(szBanner == NULL) SetWindowText(hDlg, b);
// current file and url
SetDlgItemText(hDlg, IDC_STATIC1, (szAlias && *szAlias) ? szAlias : url);
SetDlgItemText(hDlg, IDC_STATIC2, /*_tcsrchr(fn, '\\') ? _tcsrchr(fn, '\\') + 1 : */fn);
// bytes done and rate
if(cnt > 0)
{
fsFormat(cnt, b);
if(ct > 1 && status == ST_DOWNLOAD)
{
lstrcat(b, TEXT(" ( "));
fsFormat(cnt / ct, b + lstrlen(b));
lstrcat(b, TEXT("/sec )"));
}
}
else *b = 0;
SetDlgItemText(hDlg, IDC_STATIC3, b);
// total download time
wsprintf(b, TEXT("%d:%02d:%02d"), tt / 3600, (tt / 60) % 60, tt % 60);
SetDlgItemText(hDlg, IDC_STATIC6, b);
// file size, time remaining, progress bar
if(fs > 0 && fs != NOT_AVAILABLE)
{
fsFormat(fs, b);
SetDlgItemText(hDlg, IDC_STATIC5, b);
SendDlgItemMessage(hDlg, IDC_PROGRESS1, PBM_SETPOS, MulDiv(cnt, PB_RANGE, fs), 0);
if(cnt > 5000)
{
ct = MulDiv(fs - cnt, ct, cnt);
wsprintf(b, TEXT("%d:%02d:%02d"), ct / 3600, (ct / 60) % 60, ct % 60);
}
else *b = 0;
SetWindowText(GetDlgItem(hDlg, IDC_STATIC4), b);
}
}
/*****************************************************
* FUNCTION NAME: centerDlg()
* PURPOSE:
* centers dlg on NSIS parent
* SPECIAL CONSIDERATIONS:
*
*****************************************************/
void centerDlg(HWND hDlg)
{
HWND hwndParent = GetParent(hDlg);
RECT nsisRect, dlgRect, waRect;
int dlgX, dlgY, dlgWidth, dlgHeight;
if(hwndParent == NULL || silent)
return;
if(popup)
GetWindowRect(hwndParent, &nsisRect);
else GetClientRect(hwndParent, &nsisRect);
GetWindowRect(hDlg, &dlgRect);
dlgWidth = dlgRect.right - dlgRect.left;
dlgHeight = dlgRect.bottom - dlgRect.top;
dlgX = (nsisRect.left + nsisRect.right - dlgWidth) / 2;
dlgY = (nsisRect.top + nsisRect.bottom - dlgHeight) / 2;
if(popup)
{
SystemParametersInfo(SPI_GETWORKAREA, 0, &waRect, 0);
if(dlgX > waRect.right - dlgWidth)
dlgX = waRect.right - dlgWidth;
if(dlgX < waRect.left) dlgX = waRect.left;
if(dlgY > waRect.bottom - dlgHeight)
dlgY = waRect.bottom - dlgHeight;
if(dlgY < waRect.top) dlgY = waRect.top;
}
else dlgY += 20;
SetWindowPos(hDlg, HWND_TOP, dlgX, dlgY, 0, 0, SWP_NOSIZE);
}
/*****************************************************
* FUNCTION NAME: onInitDlg()
* PURPOSE:
* dlg init
* SPECIAL CONSIDERATIONS:
*
*****************************************************/
void onInitDlg(HWND hDlg)
{
HFONT hFont;
HWND hPrbNew;
HWND hPrbOld;
HWND hCan = GetDlgItem(hDlg, IDCANCEL);
if(childwnd)
{
hPrbNew = GetDlgItem(hDlg, IDC_PROGRESS1);
hPrbOld = GetDlgItem(childwnd, 0x3ec);
// Backland' fix for progress bar redraw/style issue.
// Original bar may be hidden because of interfernce with other plug-ins.
LONG prbStyle = WS_VISIBLE | WS_CHILD | WS_CLIPSIBLINGS | WS_CLIPCHILDREN;
if(hPrbOld != NULL)
{
prbStyle |= GetWindowLong(hPrbOld, GWL_STYLE);
}
SetWindowLong(hPrbNew, GWL_STYLE, prbStyle);
if(!popup)
{
if((hFont = (HFONT)SendMessage(childwnd, WM_GETFONT, 0, 0)) != NULL)
{
SendDlgItemMessage(hDlg, IDC_STATIC1, WM_SETFONT, (WPARAM)hFont, 0);
SendDlgItemMessage(hDlg, IDCANCEL, WM_SETFONT, (WPARAM)hFont, 0);
}
if(*szCancel == 0)
GetWindowText(GetDlgItem(GetParent(childwnd), IDCANCEL), szCancel, sizeof(szCancel));
SetWindowText(hCan, szCancel);
SetWindowPos(hPrbNew, HWND_TOP, 0, 0, 0, 0, SWP_NOSIZE | SWP_NOMOVE);
}
}
if(nocancel)
{
if(hCan != NULL)
ShowWindow(hCan, SW_HIDE);
if(popup)
SetWindowLong(hDlg, GWL_STYLE, GetWindowLong(hDlg, GWL_STYLE) ^ WS_SYSMENU);
}
SendDlgItemMessage(hDlg, IDC_PROGRESS1, PBM_SETRANGE,
0, MAKELPARAM(0, PB_RANGE));
if(szBanner != NULL)
{
SendDlgItemMessage(hDlg, IDC_STATIC13, STM_SETICON,
(WPARAM)LoadIcon(GetModuleHandle(NULL), MAKEINTRESOURCE(103)), 0);
SetDlgItemText(hDlg, IDC_STATIC12, szBanner);
SetWindowText(hDlg, *szCaption ? szCaption : PLUGIN_NAME);
}
SetTimer(hDlg, 1, 1000, NULL);
if(*szUrl != 0)
{
SetDlgItemText(hDlg, IDC_STATIC20, szUrl);
SetDlgItemText(hDlg, IDC_STATIC21, szDownloading);
SetDlgItemText(hDlg, IDC_STATIC22, szConnecting);
SetDlgItemText(hDlg, IDC_STATIC23, szProgress);
SetDlgItemText(hDlg, IDC_STATIC24, szSecond);
SetDlgItemText(hDlg, IDC_STATIC25, szRemaining);
}
}
/*****************************************************
* FUNCTION NAME: dlgProc()
* PURPOSE:
* dlg message handling procedure
* SPECIAL CONSIDERATIONS:
* todo: better dialog design
*****************************************************/
INT_PTR CALLBACK dlgProc(HWND hDlg, UINT message, WPARAM wParam, LPARAM lParam )
{
switch(message)
{
case WM_INITDIALOG:
onInitDlg(hDlg);
centerDlg(hDlg);
return false;
case WM_PAINT:
// child dialog redraw problem. return false is important
{
HWND hS1 = GetDlgItem(hDlg, IDC_STATIC1), hC = GetDlgItem(hDlg, IDCANCEL), hP1 = GetDlgItem(hDlg, IDC_PROGRESS1);
RedrawWindow(hS1, NULL, NULL, RDW_INVALIDATE);
RedrawWindow(hC, NULL, NULL, RDW_INVALIDATE);
RedrawWindow(hP1, NULL, NULL, RDW_INVALIDATE);
UpdateWindow(hS1);
UpdateWindow(hC);
UpdateWindow(hP1);
}
return false;
case WM_TIMER:
if(!silent && IsWindow(hDlg))
{
// long connection period and paused state updates
if(status != ST_DOWNLOAD && GetTickCount() - transfStart > PROGRESS_MS)
transfStart += PROGRESS_MS;
if(popup) onTimer(hDlg); else progress_callback();
RedrawWindow(GetDlgItem(hDlg, IDC_STATIC1), NULL, NULL, RDW_INVALIDATE);
RedrawWindow(GetDlgItem(hDlg, IDCANCEL), NULL, NULL, RDW_INVALIDATE);
RedrawWindow(GetDlgItem(hDlg, IDC_PROGRESS1), NULL, NULL, RDW_INVALIDATE);
}
break;
case WM_COMMAND:
switch(LOWORD(wParam))
{
case IDCANCEL:
if(nocancel) break;
if(szQuestion &&
MessageBox(hDlg, szQuestion, *szCaption ? szCaption : PLUGIN_NAME, MB_ICONWARNING|MB_YESNO) == IDNO)
break;
status = ST_CANCELLED;
// FallThrough
case IDOK:
if(status != ST_CANCELLED && HIWORD(wParam) != INTERNAL_OK) break;
// otherwise in the silent mode next banner windows may go to background
// if(silent) sf(hDlg);
KillTimer(hDlg, 1);
DestroyWindow(hDlg);
break;
}
return false;
default:
return false;
}
return true;
}
/*****************************************************
* FUNCTION NAME: get()
* PURPOSE:
* http/https/ftp file download entry point
* SPECIAL CONSIDERATIONS:
*
*****************************************************/
extern "C"
void __declspec(dllexport) __cdecl get(HWND hwndParent,
int string_size,
TCHAR *variables,
stack_t **stacktop,
extra_parameters *extra
)
{
HANDLE hThread;
DWORD dwThreadId;
MSG msg;
TCHAR szUsername[64]=TEXT(""), // proxy params
szPassword[64]=TEXT("");
EXDLL_INIT();
// for repeating /nounload plug-un calls - global vars clean up
silent = popup = resume = nocancel = noproxy = nocookies = false;
g_ignorecertissues = false;
myFtpCommand = NULL;
openType = INTERNET_OPEN_TYPE_PRECONFIG;
status = ST_CONNECTING;
*szCaption = *szCancel = *szUserAgent = *szBasic = *szAuth = 0;
url = (TCHAR*)LocalAlloc(LPTR, string_size * sizeof(TCHAR));
if(szPost)
{
popstring(url);
#ifdef UNICODE
WideCharToMultiByte(CP_ACP, 0, url, -1, szPost, string_size, NULL, NULL);
#else
lstrcpy(szPost, url);
#endif
fSize = (DWORD)lstrlenA(szPost);
}
// global silent option
if(extra->exec_flags->silent != 0)
silent = true;
// we must take this from stack, or push url back
while(!popstring(url) && *url == TEXT('/'))
{
if(lstrcmpi(url, TEXT("/silent")) == 0)
silent = true;
else if(lstrcmpi(url, TEXT("/weaksecurity")) == 0)
g_ignorecertissues = true;
else if(lstrcmpi(url, TEXT("/caption")) == 0)
popstring(szCaption);
else if(lstrcmpi(url, TEXT("/username")) == 0)
popstring(szUsername);
else if(lstrcmpi(url, TEXT("/password")) == 0)
popstring(szPassword);
else if(lstrcmpi(url, TEXT("/nocancel")) == 0)
nocancel = true;
else if(lstrcmpi(url, TEXT("/nocookies")) == 0)
nocookies = true;
else if(lstrcmpi(url, TEXT("/noproxy")) == 0)
openType = INTERNET_OPEN_TYPE_DIRECT;
else if(lstrcmpi(url, TEXT("/popup")) == 0)
{
popup = true;
szAlias = (TCHAR*)LocalAlloc(LPTR, string_size * sizeof(TCHAR));
popstring(szAlias);
}
else if(lstrcmpi(url, TEXT("/resume")) == 0)
{
popstring(url);
if(url[0]) lstrcpy(szResume, url);
resume = true;
}
else if(lstrcmpi(url, TEXT("/translate")) == 0)
{
if(popup)
{
popstring(szUrl);
popstring(szStatus[ST_DOWNLOAD]); // Downloading
popstring(szStatus[ST_CONNECTING]); // Connecting
lstrcpy(szStatus[ST_URLOPEN], szStatus[ST_CONNECTING]);
popstring(szDownloading);// file name
popstring(szConnecting);// received
popstring(szProgress);// file size
popstring(szSecond);// remaining time
popstring(szRemaining);// total time
}
else
{
popstring(szDownloading);
popstring(szConnecting);
popstring(szSecond);
popstring(szMinute);
popstring(szHour);
popstring(szPlural);
popstring(szProgress);
popstring(szRemaining);
}
}
else if(lstrcmpi(url, TEXT("/banner")) == 0)
{
popup = true;
szBanner = (TCHAR*)LocalAlloc(LPTR, string_size * sizeof(TCHAR));
popstring(szBanner);
}
else if(lstrcmpi(url, TEXT("/canceltext")) == 0)
{
popstring(szCancel);
}
else if(lstrcmpi(url, TEXT("/question")) == 0)
{
szQuestion = (TCHAR*)LocalAlloc(LPTR, string_size * sizeof(TCHAR));
popstring(szQuestion);
if(*szQuestion == 0) lstrcpy(szQuestion, DEF_QUESTION);
}
else if(lstrcmpi(url, TEXT("/useragent")) == 0)
{
popstring(szUserAgent);
}
else if(lstrcmpi(url, TEXT("/proxy")) == 0)
{
szProxy = (TCHAR*)LocalAlloc(LPTR, string_size * sizeof(TCHAR));
popstring(szProxy);
openType = INTERNET_OPEN_TYPE_PROXY;
}
else if(lstrcmpi(url, TEXT("/connecttimeout")) == 0)
{
popstring(url);
timeout = myatou(url) * 1000;
}
else if(lstrcmpi(url, TEXT("/receivetimeout")) == 0)
{
popstring(url);
receivetimeout = myatou(url) * 1000;
}
else if(lstrcmpi(url, TEXT("/header")) == 0)
{
szHeader = (TCHAR*)LocalAlloc(LPTR, string_size * sizeof(TCHAR));
popstring(szHeader);
}
else if(!fput && ((convToStack = lstrcmpi(url, TEXT("/tostackconv")) == 0) || lstrcmpi(url, TEXT("/tostack")) == 0))
{
szToStack = (TCHAR*)LocalAlloc(LPTR, string_size * sizeof(TCHAR));
cntToStack = 0;
lstrcpy(fn, TEXT("file"));
}
else if(lstrcmpi(url, TEXT("/file")) == 0)
{
HANDLE hFile = CreateFileA(szPost, GENERIC_READ, FILE_SHARE_READ, NULL, OPEN_EXISTING, 0, NULL);
DWORD rslt;
if(hFile == INVALID_HANDLE_VALUE)
{
status = ERR_FILEOPEN;
goto cleanup;
}
if((fSize = GetFileSize(hFile, NULL)) == 0)
{
CloseHandle(hFile);
status = ERR_FILEREAD;
goto cleanup;
}
wsprintfA(post_fname, "Filename: %s",
strchr(szPost, '\\') ? strrchr(szPost, '\\') + 1 : szPost);
LocalFree(szPost);
szPost = (char*)LocalAlloc(LPTR, fSize);
if(ReadFile(hFile, szPost, fSize, &rslt, NULL) == 0 || rslt != fSize)
{
CloseHandle(hFile);
status = ERR_FILEREAD;
goto cleanup;
}
CloseHandle(hFile);
}
}
pushstring(url);
// if(*szCaption == 0) lstrcpy(szCaption, PLUGIN_NAME);
if(*szUserAgent == 0) lstrcpy(szUserAgent, INETC_USERAGENT);
if(*szPassword && *szUsername)
{
wsprintf(url, TEXT("%s:%s"), szUsername, szPassword);
encode_base64(lstrlen(url), url, szAuth);
}
// may be silent for plug-in, but not so for installer itself - let's try to define 'progress text'
if(hwndParent != NULL &&
(childwnd = FindWindowEx(hwndParent, NULL, TEXT("#32770"), NULL)) != NULL &&
!silent)
SetDlgItemText(childwnd, 1006, *szCaption ? szCaption : PLUGIN_NAME);
else InitCommonControls(); // or NSIS do this before .onInit?
// cannot embed child dialog to non-existing parent. Using 'silent' to hide it
if(childwnd == NULL && !popup) silent = true;
// let's use hidden popup dlg in the silent mode - works both on .onInit and Page
if(silent) { resume = false; popup = true; }
// google says WS_CLIPSIBLINGS helps to redraw... not in my tests...
if(!popup)
{
unsigned int wstyle = GetWindowLong(childwnd, GWL_STYLE);
wstyle |= WS_CLIPSIBLINGS;
SetWindowLong(childwnd, GWL_STYLE, wstyle);
}
startTime = GetTickCount();
if((hDlg = CreateDialog(g_hInstance,
MAKEINTRESOURCE(szBanner ? IDD_DIALOG2 : (popup ? IDD_DIALOG1 : IDD_DIALOG3)),
(popup ? hwndParent : childwnd), dlgProc)) != NULL)
{
if((hThread = CreateThread(NULL, 0, inetTransfer, (LPVOID)hDlg, 0,
&dwThreadId)) != NULL)
{
HWND hButton = GetDlgItem(childwnd, 0x403);
HWND hList = GetDlgItem(childwnd, 0x3f8);
DWORD dwStyleButton = 0;
BOOL fVisibleList = false;
if(!silent)
{
ShowWindow(hDlg, SW_NORMAL);
if(childwnd && !popup)
{
if(hButton)
{
dwStyleButton = GetWindowLong(hButton, GWL_STYLE);
EnableWindow(hButton, false);
}
if(hList)
{
fVisibleList = IsWindowVisible(hList);
ShowWindow(hList, SW_HIDE);
}
}
}
while(IsWindow(hDlg) &&
GetMessage(&msg, NULL, 0, 0) > 0)
{
if(!IsDialogMessage(hDlg, &msg) &&
!IsDialogMessage(hwndParent, &msg) &&
!TranslateMessage(&msg))
DispatchMessage(&msg);
}
if(WaitForSingleObject(hThread, 3000) == WAIT_TIMEOUT)
{
TerminateThread(hThread, 1);
status = ERR_TERMINATED;
}
CloseHandle(hThread);
if(!silent && childwnd)
{
SetDlgItemText(childwnd, 1006, TEXT(""));
if(!popup)
{
if(hButton)
SetWindowLong(hButton, GWL_STYLE, dwStyleButton);
if(hList && fVisibleList)
ShowWindow(hList, SW_SHOW);
}
// RedrawWindow(childwnd, NULL, NULL, RDW_INVALIDATE|RDW_ERASE);
}
}
else
{
status = ERR_THREAD;
DestroyWindow(hDlg);
}
}
else {
status = ERR_DIALOG;
wsprintf(szStatus[status] + lstrlen(szStatus[status]), TEXT(" (Err=%d)"), GetLastError());
}
cleanup:
// we need to clean up stack from remaining url/file pairs.
// this multiple files download head pain and may be not safe
while(!popstring(url) && lstrcmpi(url, TEXT("/end")) != 0)
{
/* nothing MessageBox(NULL, url, TEXT(""), 0);*/
}
LocalFree(url);
if(szAlias) LocalFree(szAlias);
if(szBanner) LocalFree(szAlias);
if(szQuestion) LocalFree(szQuestion);
if(szProxy) LocalFree(szProxy);
if(szPost) LocalFree(szPost);
if(szHeader) LocalFree(szHeader);
url = szProxy = szHeader = szAlias = szQuestion = NULL;
szPost = NULL;
fput = fhead = false;
if(szToStack && status == ST_OK)
{
if(cntToStack > 0 && convToStack)
{
#ifdef UNICODE
int cp = CP_ACP;
if (0xef == ((BYTE*)szToStack)[0] && 0xbb == ((BYTE*)szToStack)[1] && 0xbf == ((BYTE*)szToStack)[2]) cp = 65001; // CP_UTF8
if (0xff == ((BYTE*)szToStack)[0] && 0xfe == ((BYTE*)szToStack)[1])
{
cp = 1200; // UTF-16LE
pushstring((LPWSTR)szToStack);
}
int required = (cp == 1200) ? 0 : MultiByteToWideChar(cp, 0, (CHAR*)szToStack, string_size * sizeof(TCHAR), NULL, 0);
if(required > 0)
{
WCHAR* pszToStackNew = (WCHAR*)LocalAlloc(LPTR, sizeof(WCHAR) * (required + 1));
if(pszToStackNew)
{
if(MultiByteToWideChar(cp, 0, (CHAR*)szToStack, string_size * sizeof(TCHAR), pszToStackNew, required) > 0)
pushstring(pszToStackNew);
LocalFree(pszToStackNew);
}
}
#else
int required = WideCharToMultiByte(CP_ACP, 0, (WCHAR*)szToStack, -1, NULL, 0, NULL, NULL);
if(required > 0)
{
CHAR* pszToStackNew = (CHAR*)LocalAlloc(LPTR, required + 1);
if(pszToStackNew)
{
if(WideCharToMultiByte(CP_ACP, 0, (WCHAR*)szToStack, -1, pszToStackNew, required, NULL, NULL) > 0)
pushstring(pszToStackNew);
LocalFree(pszToStackNew);
}
}
#endif
}
else
{
pushstring(szToStack);
}
LocalFree(szToStack);
szToStack = NULL;
}
pushstring(szStatus[status]);
}
/*****************************************************
* FUNCTION NAME: put()
* PURPOSE:
* http/ftp file upload entry point
* SPECIAL CONSIDERATIONS:
* re-put not works with http, but ftp REST - may be.
*****************************************************/
extern "C"
void __declspec(dllexport) __cdecl put(HWND hwndParent,
int string_size,
TCHAR *variables,
stack_t **stacktop,
extra_parameters *extra
)
{
fput = true;
lstrcpy(szDownloading, TEXT("Uploading %s"));
lstrcpy(szStatus[2], TEXT("Uploading"));
get(hwndParent, string_size, variables, stacktop, extra);
}
/*****************************************************
* FUNCTION NAME: post()
* PURPOSE:
* http post entry point
* SPECIAL CONSIDERATIONS:
*
*****************************************************/
extern "C"
void __declspec(dllexport) __cdecl post(HWND hwndParent,
int string_size,
TCHAR *variables,
stack_t **stacktop,
extra_parameters *extra
)
{
szPost = (CHAR*)LocalAlloc(LPTR, string_size);
get(hwndParent, string_size, variables, stacktop, extra);
}
/*****************************************************
* FUNCTION NAME: head()
* PURPOSE:
* http/ftp file upload entry point
* SPECIAL CONSIDERATIONS:
* re-put not works with http, but ftp REST - may be.
*****************************************************/
extern "C"
void __declspec(dllexport) __cdecl head(HWND hwndParent,
int string_size,
TCHAR *variables,
stack_t **stacktop,
extra_parameters *extra
)
{
fhead = true;
get(hwndParent, string_size, variables, stacktop, extra);
}
/*****************************************************
* FUNCTION NAME: DllMain()
* PURPOSE:
* Dll main (initialization) entry point
* SPECIAL CONSIDERATIONS:
*
*****************************************************/
#ifdef _VC_NODEFAULTLIB
#define DllMain _DllMainCRTStartup
#endif
EXTERN_C BOOL WINAPI DllMain(HINSTANCE hinstDLL, DWORD fdwReason, LPVOID lpvReserved)
{
g_hInstance = hinstDLL;
return TRUE;
}