├── .gitignore ├── LICENSE.md ├── Makefile.mingw ├── NSxfer.Readme.txt ├── NSxfer.sln ├── NSxfer.vcxproj ├── NSxfer.vcxproj.filters ├── NSxfer.vcxproj.user ├── README.md ├── SConscript ├── Test ├── NSxfer-Test-build.bat ├── NSxfer-Test.nsi ├── build64.bat ├── buildA.bat ├── buildW.bat └── cleanup.bat ├── TestDebug ├── NSxfer-Debug.nsi ├── build64.bat ├── buildA.bat ├── buildW.bat └── cleanup.bat ├── _build_Debug.cmd ├── _build_Release.cmd ├── _build_Release_CL.cmd ├── _build_Release_mingw.bat ├── _cleanup.bat ├── _get_nsis_sdk.py ├── _make_package.bat ├── gui.c ├── gui.h ├── main.c ├── main.h ├── queue.c ├── queue.h ├── resource.h ├── resource.rc ├── thread.c ├── thread.h ├── todo.txt ├── tools └── ITaskbarList.h ├── utils.c └── utils.h /.gitignore: -------------------------------------------------------------------------------- 1 | 2 | /nsis 3 | 4 | /Test/*.exe 5 | /Test/_* 6 | 7 | /TestDebug/*.exe 8 | /TestDebug/_* 9 | 10 | /NSxfer*.7z 11 | 12 | Debug-* 13 | Release-* 14 | ipch 15 | .vs 16 | 17 | *.opensdf 18 | *.sdf 19 | *.suo 20 | *.user 21 | *.aps 22 | -------------------------------------------------------------------------------- /LICENSE.md: -------------------------------------------------------------------------------- 1 | 2 | The zlib/libpng License Copyright (c) 2014-2023, [Marius Negrutiu](mailto:marius.negrutiu@protonmail.com) 3 | 4 | This software is provided 'as-is', without any express or implied warranty. 5 | In no event will the authors be held liable for any damages arising from the use of this software. 6 | 7 | Permission is granted to anyone to use this software for any purpose, including commercial applications, 8 | and to alter it and redistribute it freely, subject to the following restrictions: 9 | 10 | 1. The origin of this software must not be misrepresented; you must not claim that you wrote the original software. 11 | If you use this software in a product, an acknowledgment in the product documentation would be appreciated but is not required. 12 | 13 | 2. Altered source versions must be plainly marked as such, and must not be misrepresented as being the original software. 14 | 15 | 3. This notice may not be removed or altered from any source distribution. 16 | -------------------------------------------------------------------------------- /Makefile.mingw: -------------------------------------------------------------------------------- 1 | 2 | # ------------------------------- 3 | # Mandatory definitions 4 | # ------------------------------- 5 | # ARCH=X86|x64 6 | # CHAR=ANSI|Unicode 7 | # OUTDIR= 8 | # ------------------------------- 9 | # Optional definitions 10 | # ------------------------------- 11 | # CUSTOM_CFLAGS 12 | # CUSTOM_LDFLAGS 13 | # CUSTOM_RCFLAGS 14 | 15 | 16 | PROJECT = NSxfer 17 | BIN = $(PROJECT).dll 18 | OBJ = pluginapi.o gui.o main.o queue.o thread.o utils.o resource.res 19 | INC = -I. 20 | LIB = -lkernel32 -luser32 -ladvapi32 -lshlwapi -lwininet -lole32 -luuid -lversion 21 | 22 | _OBJ = $(patsubst %,$(OUTDIR)/%,$(OBJ)) 23 | _BIN = $(patsubst %,$(OUTDIR)/%,$(BIN)) 24 | 25 | DEF = $(OUTDIR)/lib$(PROJECT).def 26 | STATIC = $(OUTDIR)/lib$(PROJECT).a 27 | 28 | # 29 | # https://gcc.gnu.org/onlinedocs/gcc/Option-Summary.html 30 | # http://linux.die.net/man/1/gcc 31 | # http://linux.die.net/man/1/ld 32 | # 33 | 34 | # ARCH 35 | ifeq ($(ARCH), X64) 36 | CFLAGS += -DNDEBUG 37 | LDFLAGS += -Wl,--high-entropy-va -Wl,-e'DllMain' 38 | RCFLAGS += -F pe-x86-64 39 | else 40 | CFLAGS += -march=pentium2 -DNDEBUG 41 | LDFLAGS += -Wl,-e'_DllMain' 42 | RCFLAGS += -F pe-i386 43 | endif 44 | 45 | # CHAR 46 | ifeq ($(CHAR), ANSI) 47 | CFLAGS += -DMBCS -D_MBCS 48 | LDFLAGS += 49 | else 50 | CFLAGS += -municode -DUNICODE -D_UNICODE 51 | LDFLAGS += 52 | endif 53 | 54 | # https://sourceforge.net/p/mingw-w64/wiki2/gnu%20printf/ 55 | CFLAGS += -D__USE_MINGW_ANSI_STDIO=0 56 | 57 | CFLAGS += \ 58 | -mdll \ 59 | -nostdlib \ 60 | -s \ 61 | -Os \ 62 | -fPIE \ 63 | -ffunction-sections \ 64 | -fdata-sections \ 65 | -fno-unwind-tables \ 66 | -fno-asynchronous-unwind-tables \ 67 | -Wall \ 68 | -Wno-unused-function \ 69 | -Wno-unused-variable \ 70 | -Wno-unused-but-set-variable \ 71 | $(INC) \ 72 | $(CUSTOM_CFLAGS) 73 | 74 | LDFLAGS += \ 75 | $(CFLAGS) \ 76 | -Wl,--gc-sections \ 77 | -Wl,--no-seh \ 78 | -Wl,--nxcompat \ 79 | -Wl,--dynamicbase \ 80 | -Wl,--enable-auto-image-base \ 81 | -Wl,--enable-stdcall-fixup \ 82 | -Wl,--output-def,$(DEF) \ 83 | -Wl,--out-implib,$(STATIC) \ 84 | $(LIB) \ 85 | $(CUSTOM_LDFLAGS) 86 | 87 | RCFLAGS += \ 88 | $(CUSTOM_RCFLAGS) 89 | 90 | 91 | .PHONY: clean all-before nsis-sdk all all-after 92 | 93 | clean: 94 | @echo. 95 | if exist $(OUTDIR) rd /S /Q $(OUTDIR) 96 | 97 | all: all-before nsis-sdk $(_BIN) all-after 98 | 99 | all-before: 100 | if not exist $(OUTDIR) mkdir $(OUTDIR) 101 | 102 | nsis-sdk: 103 | REM ----- NSIS SDK ------------------------------------------------------------ 104 | call py -3 _get_nsis_sdk.py 105 | 106 | # Link 107 | $(_BIN): $(_OBJ) 108 | @echo. 109 | gcc $(_OBJ) -o $(_BIN) $(LDFLAGS) 110 | 111 | # Compile .c 112 | $(OUTDIR)/%.o: %.c 113 | gcc $(CFLAGS) -o $@ -c $< 114 | 115 | $(OUTDIR)/%.o: nsis/pluginapi.c 116 | gcc $(CFLAGS) -o $@ -c $< 117 | 118 | # Compile .rc 119 | $(OUTDIR)/%.res: %.rc 120 | windres -o $@ -i $< -O coff --input-format=rc $(RCFLAGS) 121 | -------------------------------------------------------------------------------- /NSxfer.Readme.txt: -------------------------------------------------------------------------------- 1 | 2 | NSxfer - NSIS WinINet Plugin 3 | https://github.com/negrutiu/nsis-nsxfer 4 | marius.negrutiu@protonmail.com 5 | _________________________________________________________________________________________________ 6 | 7 | Features: 8 | - Multi-threaded: download multiple files in parallel 9 | - Asynchronous: start a download now, check its status later 10 | - Aggressive: multiple attempts to connect, multiple attempts to reconnect, resume interrupted transfers, etc 11 | - NSIS aware: download files at any installation stage (from .onInit, from Sections, from custom pages, silent installers, etc) 12 | - Informative: plenty of useful information is available for each download (size, speed, HTTP status, HTTP headers, etc) 13 | - Supports all relevant HTTP methods (GET, POST, HEAD, etc) 14 | - Supports custom HTTP headers and data 15 | - Supports proxy servers (both authenticated and open) 16 | - Supports files larger than 4GB 17 | - Can download remote content to RAM instead of a file 18 | - Many more... 19 | 20 | Examples: 21 | # Quick transfer, single file 22 | NSxfer::Transfer /URL "http://MyServer/MyFile" /LOCAL "$TEMP\MyFile" /MODE Page /ABORT "Abort Title" "Are you sure?" /END 23 | Pop $0 ; Transfer status ("OK" for success) 24 | DetailPrint "Status: $0" 25 | 26 | # Download to memory 27 | NSxfer::Transfer /URL "https://wikipedia.org" /LOCAL memory /RETURNID /END 28 | Pop $0 ; Request ID 29 | NSxfer::Query /ID $0 /ERRORTEXT /CONTENT /SPEED /END 30 | Pop $1 ; Transfer status ("OK" for success) 31 | Pop $2 ; Remote-Content from memory 32 | Pop $3 ; Transfer speed (i.e. "50 MB/s") 33 | 34 | # Parallel transfer, multiple files 35 | ; Start the downloads 36 | NSxfer::Request /URL "http://MyServer1/MyFile1" /LOCAL "$TEMP\MyFile1" /END 37 | Pop $1 ; Request ID1 38 | NSxfer::Request /URL "http://MyServer2/MyFile2" /LOCAL "$TEMP\MyFile2" /END 39 | Pop $2 ; Request ID2 40 | NSxfer::Request /URL "http://MyServer3/MyFile3" /LOCAL "$TEMP\MyFile3" /END 41 | Pop $3 ; Request ID3 42 | 43 | ; ...do other useful stuff... 44 | 45 | ; Wait for all downloads to complete 46 | NSxfer::Wait /MODE Page /ABORT "Abort" "Are you sure?" /END 47 | 48 | ; Query status (1) 49 | NSxfer::Query /ID $1 /ERRORCODE /END 50 | Pop $0 ; Error code (HTTP status 200-299 for success) 51 | DetailPrint "Status1: $0" 52 | ; Query status (2) 53 | NSxfer::Query /ID $2 /ERRORCODE /END 54 | Pop $0 ; Error code (HTTP status 200-299 for success) 55 | DetailPrint "Status2: $0" 56 | ; Query status (3) 57 | NSxfer::Query /ID $3 /ERRORCODE /END 58 | Pop $0 ; Error code (HTTP status 200-299 for success) 59 | DetailPrint "Status3: $0" 60 | 61 | ; ...you got the idea... 62 | _________________________________________________________________________________________________ 63 | 64 | NSxfer::Transfer [/PRIORITY prio] [/DEPEND id] 65 | [/METHOD GET|POST] 66 | /URL url 67 | [/LOCAL file|MEMORY|NONE] 68 | [/HEADERS hdr] 69 | [/DATA data | /DATAFILE file] 70 | [/TIMEOUTCONNECT msec] [/TIMEOUTRECONNECT msec] 71 | [/OPTCONNECTRETRIES count] [/OPTCONNECTTIMEOUT msec] [/OPTRECEIVETIMEOUT msec] [/OPTSENDTIMEOUT msec] 72 | [/PROXY server] [/PROXYUSER user] [/PROXYPASS pass] 73 | [/REFERER url] 74 | [/INTERNETFLAGS flags] [/SECURITYFLAGS flags] 75 | [/MODE SILENT|POPUP|PAGE] 76 | [/TITLEHWND Wnd] [/STATUSHWND Wnd] [/PROGRESSHWND Wnd] 77 | [/TITLETEXT Text MultiText] [/STATUSTEXT Text Multitext] 78 | [/ABORT Title Message] 79 | [/RETURNID] 80 | /END 81 | 82 | Remarks: 83 | Make a (single) HTTP request, wait for it to complete and return the status code. 84 | This routine is recommended for single transfers. 85 | For multiple, parallel transfers, you need to use lower level functions (Request(1) + Request(2) + ... + Request(n) + [...] + Wait() + Query()) 86 | 87 | Parameters: 88 | [...] | Check out "Request" parameters! 89 | [...] | Check out "Wait" parameters! 90 | /END | Must conclude the parameter list, otherwise the NSIS stack will be emptied... 91 | 92 | Return: 93 | [Stack] "OK" for successful transfers, or an error string (HTTP or Win32) otherwise. 94 | If `/RETURNID` is specified, the unique transfer ID is returned instead of the transfer status. Use Query(id) to get additional transfer information. 95 | _________________________________________________________________________________________________ 96 | 97 | NSxfer::Request [/PRIORITY prio] [/DEPEND id] 98 | [/METHOD GET|POST] 99 | /URL url 100 | [/LOCAL file|MEMORY|NONE] 101 | [/HEADERS hdr] 102 | [/DATA data | /DATAFILE file] 103 | [/TIMEOUTCONNECT msec] [/TIMEOUTRECONNECT msec] 104 | [/OPTCONNECTRETRIES count] [/OPTCONNECTTIMEOUT msec] [/OPTRECEIVETIMEOUT msec] [/OPTSENDTIMEOUT msec] 105 | [/PROXY server] [/PROXYUSER user] [/PROXYPASS pass] 106 | [/REFERER url] 107 | [/INTERNETFLAGS flags] [/SECURITYFLAGS flags] 108 | /END 109 | 110 | Remarks: 111 | Add a transfer request to the queue. 112 | The transfer will start as soon as a worker thread becomes available. 113 | This command will not display any GUI. 114 | 115 | Parameters: 116 | /PRIORITY | Priority in queue. Lower value mean higher priority (default: 1000) 117 | /DEPEND id | Another request that this one depends on. The new request will wait in queue until the other is completed (default: 0, no dependency) 118 | /METHOD GET|POST|HEAD|... | HTTP Method (default: GET) 119 | /URL url | HTTP or HTTPS... 120 | /LOCAL file|MEMORY|NONE | Local destination. Can be either a local file, a memory buffer, or none (none: make the HTTP request, but don't transfer the content). MEMORY downloads will only retrieve the first NSIS_MAX_STRLEN bytes (usually 4K or 8K) (default: NONE) 121 | /HEADERS hdr | Additional HTTP headers, delimited by CRLF (\r\n) 122 | /DATA data | Additional data to be sent as part of the HTTP request 123 | /DATAFILE file | Additional data to be sent as part of the HTTP request (read from the specified file) 124 | /TIMEOUTCONNECT msec | Keep trying to connect for "msec" milliseconds (default: 0, no retries) 125 | /TIMEOUTRECONNECT msec | Keep trying to reconnect for "msec" milliseconds, if the connection drops while downloading (default: 0, no reconnecting) 126 | /OPTCONNECTRETRIES count | InternetSetOption( Session, INTERNET_OPTION_CONNECT_RETRIES ). Relevant only for remote hosts with multiple IPs! 127 | /OPTCONNECTTIMEOUT msec | InternetSetOption( Session, INTERNET_OPTION_CONNECT_TIMEOUT ) 128 | /OPTRECEIVETIMEOUT msec | InternetSetOption( Request, INTERNET_OPTION_RECEIVE_TIMEOUT ) 129 | /OPTSENDTIMEOUT msec | InternetSetOption( Request, INTERNET_OPTION_SEND_TIMEOUT ) 130 | /PROXY server | CERN type proxies (ex: "http=http://my_proxy:my_port"). SOCKS proxies are supported if Internet Explorer is installed 131 | /PROXYUSER user | Optional username for authenticated proxies 132 | /PROXYPASS pass | Optional password for authenticated proxies 133 | /REFERER url | Optional referer URL, passed to InternetOpenRequest 134 | /INTERNETFLAGS flags | Combination of INTERNET_FLAG_XXX passed to InternetOpenRequest (default: 0x84082200, INTERNET_FLAG_NO_CACHE_WRITE|INTERNET_FLAG_IGNORE_CERT_DATE_INVALID|INTERNET_FLAG_NO_COOKIES|INTERNET_FLAG_NO_UI|INTERNET_FLAG_RELOAD) 135 | /SECURITYFLAGS flags | Combination of SECURITY_FLAG_XXX passed to InternetSetOption(INTERNET_OPTION_SECURITY_FLAGS) (default: 0x2080, SECURITY_FLAG_IGNORE_REVOCATION|SECURITY_FLAG_IGNORE_CERT_DATE_INVALID) 136 | /END | Must conclude the parameter list, otherwise the NSIS stack will be emptied... 137 | 138 | Return: 139 | [Stack] An unique request ID. You'll need it later, to query the status 140 | 141 | Examples: 142 | 143 | ; Download to local file 144 | NSxfer::Request /URL "http://live.sysinternals.com/Files/SysinternalsSuite.zip" /LOCAL "$EXEDIR\SysinternalsSuite.zip" /TIMEOUTCONNECT 60000 /TIMEOUTRECONNECT 300000 /END 145 | Pop $0 ; Request ID 146 | 147 | ; Download to memory, highest priority 148 | NSxfer::Request /PRIORITY 1 /URL "http://live.sysinternals.com/Files/SysinternalsSuite.zip" /LOCAL MEMORY /END 149 | Pop $0 ; Request ID 150 | 151 | ; Send the HTTP request but don't download the content 152 | NSxfer::Request /URL "http://mydomain.com:800?param1=va1¶m2=val2" /LOCAL NONE /END 153 | Pop $0 ; Request ID 154 | 155 | ; POST a form 156 | NSxfer::Request /METHOD POST /URL "http://httpbin.org/post" /LOCAL "Post.txt" /HEADERS "Content-Type: application/x-www-form-urlencoded" /DATA "user=MY+NAME&pass=MY+PASSWORD" /END 157 | Pop R0 ; Request ID 158 | _________________________________________________________________________________________________ 159 | 160 | NSxfer::Wait [/ID id] [/PRIORITY prio] ; Selection parameters 161 | [/MODE SILENT|POPUP|PAGE] 162 | [/TITLEHWND Wnd] [/STATUSHWND Wnd] [/PROGRESSHWND Wnd] 163 | [/TITLETEXT Text MultiText] [/STATUSTEXT Text Multitext] 164 | [/ABORT Title Message] 165 | /END 166 | 167 | Remarks: 168 | Wait (by ID or PRIORITY) for one or multiple transfers to complete 169 | 170 | Waiting modes: 171 | - SILENT | No GUI is displayed, the call returns when transfer(s) complete(s) 172 | - POPUP | Progress is displayed on a popup window 173 | - PAGE | Progress is displayed on an installer page (InstFiles or custom). This mode is available only when the installer is visible. 174 | 175 | SILENT and POPUP modes are available at all times, including silent installers. 176 | PAGE mode requires that the installer is visible, and a page is currently active. In silent installers, PAGE mode is automatically converted to SILENT mode. 177 | 178 | All displayed texts can be customized. 179 | The plugin will choose between "Text" and "MultiText", based on whether waiting for one or multiple transfers 180 | All texts may contain keywords that are automatically replaced at runtime. 181 | 182 | Transfer-specific keywords: 183 | {ID} | The request ID returned by NSxfer::Request 184 | {STATUS} | "Waiting", "Downloading" or "Completed" (English only) 185 | {WININETSTATUS} | The last status reported by the InternetCallback (http://msdn.microsoft.com/EN-US/library/windows/desktop/aa385121(v=vs.85).aspx) 186 | {METHOD} | The HTTP method in use (GET, POST, HEAD, etc.) 187 | {URL} | Full URI 188 | {IP} | Server's IP address, or an empty string 189 | {PROXY} | The proxy server, or an empty string 190 | {LOCAL} | The local destination (file path|Memory|None) 191 | {LOCALFILEDIR} | The file directory (no name), extracted from the local path 192 | {LOCALFILENAME} | The file name (no path), extracted from the local path 193 | {FILESIZE} | The remote content length, nicely formatted (ex: "1,3 GB") 194 | {FILESIZEBYTES} | The remote content length (can exceed 4GB (32-bit boundary)) 195 | {RECVSIZE} | The amount of bytes received, nicely formatted (ex: "1,3 GB") 196 | {RECVSIZEBYTES} | The amount of bytes received 197 | {PERCENT} | Percent (0-100) 198 | {SPEED} | Transfer speed (nicely formatted, ex: "1,4 MB/s") 199 | {SPEEDBYTES} | Transfer speed (bytes/second) 200 | {TIMESTART} | The timestamp when the transfer has started (ex: 2014/10/18 08:24) 201 | {TIMEELAPSED} | Amount of time since the transfer has started (ex: 3 min, 2 sec) 202 | {TIMEREMAINING} | Estimated amount of time until the transfer will complete (ex: 10 min, 35 sec) 203 | 204 | Global keywords: 205 | {TOTALCOUNT} | The overall number of requests (waiting + downloading + completed) 206 | {TOTALWAITING} | The number of waiting requests 207 | {TOTALACTIVE} | The number of downloading + completed requests 208 | {TOTALDOWNLOADING} | The number of requests in progress 209 | {TOTALCOMPLETED} | The number of completed requests 210 | {TOTALRECVSIZE} | The amount of bytes received, nicely formatted (ex: "1,3 GB") 211 | {TOTALRECVSIZEBYTES} | The amount of bytes received 212 | {TOTALSPEED} | The combined speed of transfers in progress (nicely formatted, ex: "1,4 MB/s") 213 | {TOTALSPEEDBYTES} | The combined speed of transfers in progress (bytes/second) 214 | {TOTALTHREADS} | Worker thread count 215 | {ORIGINALTITLE} | The original title text 216 | {ORIGINALSTATUS} | The original status text 217 | {PLUGINNAME} | Plugin name ("NSxfer") 218 | {PLUGINVERSION} | Plugin version 219 | {ANIMLINE} | The classic \|/- animation 220 | {ANIMDOTS} | The classic ./../... animation 221 | 222 | Parameters: 223 | /ID id | Wait for a specific transfer. If not specified, we're waiting for all transfers 224 | /PRIORITY prio | Wait for transfers with specific priority. If not specified we're waiting for all transfers 225 | /MODE | Wait mode 226 | /TITLEHWND | Custom control to display the Title text 227 | /STATUSHWND | Custom control to display the Status text 228 | /PROGRESSHWND | Custom progress control 229 | /TITLETEXT Text MultiText | The Title text. "Text" is displayed when waiting for a single transfer, whereas "MultiText" is displayed when waiting for multiple transfers. Both texts may contain keywords 230 | /STATUSTEXT Text MultiText | The Status text. "Text" is displayed when waiting for a single transfer, whereas "MultiText" is displayed when waiting for multiple transfers. Both texts may contain keywords 231 | /ABORT Title Message | The transfer can be aborted. If Message is not empty, a confirmation box (Yes/No) will be diaplayed. If Message is empty, the transfer will abort quietly. By default abortion is disabled 232 | /END | Must conclude the parameter list, otherwise the NSIS stack will be emptied... 233 | 234 | Return: 235 | [Stack] Result code. Ignore it! 236 | 237 | Examples: 238 | ; Wait for requests with priority 2000 (Popup mode) 239 | NSxfer::Wait \ 240 | /PRIORITY 2000 /MODE POPUP \ 241 | /STATUSTEXT \ 242 | "Received {RECVSIZE}/{FILESIZE} @ {SPEED}, ETA: {TIMEREMAINING}$\n{URL}" \ 243 | "Downloading {TOTALACTIVE}/{TOTALCOUNT} files. Received {TOTALRECVSIZE} @ {TOTALSPEED}" \ 244 | /TITLETEXT \ 245 | "{PERCENT}% - Downloading..." \ 246 | "Downloading {TOTALCOUNT} files..." \ 247 | /ABORT "Abort" "Are your sure?" 248 | Pop $0 249 | _________________________________________________________________________________________________ 250 | 251 | NSxfer::Enumerate [/STATUS waiting|downloading|completed] 252 | [/PRIORITY prio] 253 | /END 254 | 255 | Remarks: 256 | Enumerate transfer requests from the queue (by STATUS and/or PRIORITY). 257 | Be aware that completed requests remain in queue. They can be filtered out using /STATUS 258 | 259 | Parameters: 260 | /STATUS | Enumerate requests with specific status (Waiting, Downloading, Completed). If not specified, all requests will be enumerated 261 | /PRIORITY prio | Enumerate requests with specific priority. If not specified, all requests will be enumerated 262 | /END | Must conclude the parameter list, otherwise the NSIS stack will be emptied... 263 | 264 | Return: 265 | [Stack] Request count (N) 266 | [Stack] ID1 267 | [Stack] ID2 268 | [Stack] ... 269 | [Stack] IDn 270 | 271 | Example: 272 | ; Enumerate all completed requests 273 | NSxfer::Enumerate /STATUS "Completed" /END 274 | Pop $1 ; Count 275 | ${For} $0 1 $1 276 | Pop $2 277 | DetailPrint "[$0] Request ID = $2" 278 | ${Next} 279 | _________________________________________________________________________________________________ 280 | 281 | NSxfer::Query /ID id 282 | [/PRIORITY] [/DEPEND] 283 | [/STATUS] [/WININETSTATUS] 284 | [/METHOD] [/URL] [/IP] [/PROXY] [/LOCAL] 285 | [/SENTHEADERS] [/RECVHEADERS] 286 | [/FILESIZE] [/RECVSIZE] [/PERCENT] [/SPEEDBYTES] [/SPEED] 287 | [/CONTENT] [/DATA] 288 | [/TIMEWAITING] [/TIMEDOWNLOADING] 289 | [/CONNECTIONDROPS] 290 | [/ERRORCODE] [/ERRORTEXT] 291 | /END 292 | 293 | Remarks: 294 | Query information about a specific transfer request. 295 | 296 | Parameters: 297 | /ID id | The request ID returned by NSxfer::Request 298 | ----------- | ------------------ 299 | /PRIORITY | Request priority 300 | /DEPEND | Dependency ID 301 | /STATUS | Request status (downloading|waiting|completed) 302 | /WININETSTATUS | The last status reported by the InternetCallback (http://msdn.microsoft.com/EN-US/library/windows/desktop/aa385121(v=vs.85).aspx) 303 | /METHOD | The HTTP method in use (GET, POST, HEAD, etc.) 304 | /URL | Full URL 305 | /IP | Server's IP address, or an empty string 306 | /PROXY | The proxy server, or an empty string 307 | /LOCAL | The local destination (file|MEMORY|NONE) 308 | /SENTHEADERS | The raw HTTP headers sent to the server 309 | /RECVHEADERS | The raw HTTP headers received from the server 310 | /FILESIZE | The remote content length (can exceed 4GB (32-bit boundary)) 311 | /RECVSIZE | The amount of bytes received so far 312 | /PERCENT | Percent (0-100) 313 | /SPEED | Transfer speed (nicely formatted, ex: "1,4 MB/s") 314 | /SPEEDBYTES | Transfer speed (bytes/second) 315 | /CONTENT | Retrieves the remote content (max NSIS_MAX_STRLEN) downloaded to memory. Non-printable characters will be replaced with '.' 316 | /DATA | Retrieves the data sent to the server (max NSIS_MAX_STRLEN). Non-printable characters will be replaced with '.' 317 | /TIMEWAITING | Amount of time (milliseconds) spent waiting in queue 318 | /TIMEDOWNLOADING | Amount of time (milliseconds) spent transferring 319 | /CONNECTIONDROPS | Number of times the connection dropped out during the transfer 320 | /ERRORCODE | Win32 error code, or HTTP status code 321 | /ERRORTEXT | The error explained 322 | /END | Must conclude the parameter list, otherwise the NSIS stack will be emptied... 323 | 324 | Return: 325 | A value for each parameter, in the same order 326 | [Stack] Value1 327 | [Stack] Value2 328 | [Stack] ... 329 | [Stack] ValueN 330 | 331 | Example: 332 | NSxfer::Query /ID $varID /STATUS /URL /PERCENT /SPEED /END 333 | Pop $0 ;Status 334 | Pop $1 ;URL 335 | Pop $2 ;Percent 336 | Pop $3 ;Speed 337 | DetailPrint "Status:$0, URL:$1, Percent:$2, Speed:$3" 338 | _________________________________________________________________________________________________ 339 | 340 | NSxfer::Set [/ID id] [/PRIORITY prio] ; Selection parameters 341 | [/SETPRIORITY prio] 342 | [/SETDEPEND id] 343 | [/ABORT] 344 | [/REMOVE] 345 | /END 346 | 347 | Remarks: 348 | Modify one or multiple transfer requests (by ID or PRIORITY) 349 | When aborting a file transfer the received content is not removed (from disk). It's possible to resume the transfer later. 350 | The caller is responsible for cleaning up unfinished/aborted downloads. 351 | 352 | Parameters: 353 | /ID id | Modify specific request. If not specified, all requests will be modified 354 | /PRIORITY prio | Modify requests with specific priority. If not specified, all requests will be modified 355 | /SETPRIORITY prio | New priority 356 | /SETDEPEND id | New dependency 357 | /ABORT | Abort specified transfers 358 | /REMOVE | Remove specified transfers from queue 359 | /END | Must conclude the parameter list, otherwise the NSIS stack will be emptied... 360 | 361 | Return: 362 | [Stack] Result code. Ignore it! 363 | 364 | Examples: 365 | ; Abort all transfers 366 | NSxfer::Set /ABORT /END 367 | 368 | ; Abort all transfer with priority 3000 369 | NSxfer::Set /PRIORITY 3000 /ABORT /END 370 | 371 | ; Abort a specific transfer 372 | NSxfer::Set /ID 666 /ABORT /END 373 | 374 | ; Modify priority 3000 to 5000 375 | NSxfer::Set /PRIORITY 3000 /SETPRIORITY 5000 /END 376 | 377 | ; Modify one request's dependency 378 | NSxfer::Set /ID 666 /SETDEPEND 1 /END 379 | 380 | ; Remove one request's dependency (by setting it to 0) 381 | NSxfer::Set /ID 666 /SETDEPEND 0 /END 382 | 383 | ; Abort all and clear the queue 384 | NSxfer::Set /REMOVE /END 385 | _________________________________________________________________________________________________ 386 | 387 | NSxfer::QueryGlobal [/TOTALCOUNT] [/TOTALWAITING] [/TOTALDOWNLOADING] [/TOTALCOMPLETED] 388 | [/TOTALSPEED] [/TOTALSPEEDBYTES] 389 | [/TOTALTHREADS] 390 | [/PLUGINNAME] [/PLUGINVERSION] 391 | [/USERAGENT] 392 | /END 393 | 394 | Remarks: 395 | Query global information. 396 | 397 | Parameters: 398 | /TOTALCOUNT | The overall number of requests (waiting + downloading + completed) 399 | /TOTALWAITING | The number of waiting requests 400 | /TOTALDOWNLOADING | The number of requests in progress 401 | /TOTALCOMPLETED | The number of completed requests 402 | /TOTALRECVSIZE | The amount of bytes received, nicely formatted (ex: "1,3 GB") 403 | /TOTALRECVSIZEBYTES | The amount of bytes received 404 | /TOTALSPEED | The combined speed of requests in progress (nicely formatted, ex: "1,4 MB/s") 405 | /TOTALSPEEDBYTES | The combined speed of requests in progress (bytes/second) 406 | /TOTALTHREADS | Worker thread count 407 | /PLUGINNAME | "NSxfer" 408 | /PLUGINVERSION | Version string such as "1.2014.11.16" 409 | /USERAGENT | User agent (ex: "Mozilla/5.0 (Windows; WOW64) xfer/1.0") 410 | /END | Must conclude the parameter list, otherwise the NSIS stack will be emptied... 411 | 412 | Return: 413 | A value for each parameter, in the same order 414 | [Stack] Value1 415 | [Stack] Value2 416 | [Stack] ... 417 | [Stack] ValueN 418 | 419 | Example: 420 | NSxfer::QueryGlobal /TOTALCOUNT /TOTALCOMPLETED /TOTALDOWNLOADING /TOTALSPEED /TOTALTHREADS /END 421 | Pop $R0 ; Total 422 | Pop $R1 ; Completed 423 | Pop $R2 ; Downloading 424 | Pop $R3 ; Speed 425 | Pop $R4 ; Worker threads 426 | 427 | DetailPrint "Transferring $R1+$R2/$R0 files at $R3 using $R4 worker threads" 428 | _________________________________________________________________________________________________ 429 | 430 | -------------------------------------------------------------------------------- /NSxfer.sln: -------------------------------------------------------------------------------- 1 |  2 | Microsoft Visual Studio Solution File, Format Version 12.00 3 | # Visual Studio 14 4 | VisualStudioVersion = 14.0.24720.0 5 | MinimumVisualStudioVersion = 10.0.40219.1 6 | Project("{8BC9CEB8-8B4A-11D0-8D11-00A0C91BC942}") = "NSxfer", "NSxfer.vcxproj", "{FE51110F-82BE-423E-800C-F5951C998D22}" 7 | EndProject 8 | Global 9 | GlobalSection(SolutionConfigurationPlatforms) = preSolution 10 | Debug-x86-ansi|Win32 = Debug-x86-ansi|Win32 11 | Debug-x86-unicode|Win32 = Debug-x86-unicode|Win32 12 | Release-nocrt-x86-ansi|Win32 = Release-nocrt-x86-ansi|Win32 13 | Release-staticcrt-x86-ansi|Win32 = Release-staticcrt-x86-ansi|Win32 14 | Release-nocrt-x86-unicode|Win32 = Release-nocrt-x86-unicode|Win32 15 | Release-staticcrt-x86-unicode|Win32 = Release-staticcrt-x86-unicode|Win32 16 | EndGlobalSection 17 | GlobalSection(ProjectConfigurationPlatforms) = postSolution 18 | {FE51110F-82BE-423E-800C-F5951C998D22}.Debug-x86-ansi|Win32.ActiveCfg = Debug-x86-ansi|Win32 19 | {FE51110F-82BE-423E-800C-F5951C998D22}.Debug-x86-ansi|Win32.Build.0 = Debug-x86-ansi|Win32 20 | {FE51110F-82BE-423E-800C-F5951C998D22}.Debug-x86-unicode|Win32.ActiveCfg = Debug-x86-unicode|Win32 21 | {FE51110F-82BE-423E-800C-F5951C998D22}.Debug-x86-unicode|Win32.Build.0 = Debug-x86-unicode|Win32 22 | {FE51110F-82BE-423E-800C-F5951C998D22}.Release-nocrt-x86-ansi|Win32.ActiveCfg = Release-nocrt-x86-ansi|Win32 23 | {FE51110F-82BE-423E-800C-F5951C998D22}.Release-nocrt-x86-ansi|Win32.Build.0 = Release-nocrt-x86-ansi|Win32 24 | {FE51110F-82BE-423E-800C-F5951C998D22}.Release-staticcrt-x86-ansi|Win32.ActiveCfg = Release-staticcrt-x86-ansi|Win32 25 | {FE51110F-82BE-423E-800C-F5951C998D22}.Release-staticcrt-x86-ansi|Win32.Build.0 = Release-staticcrt-x86-ansi|Win32 26 | {FE51110F-82BE-423E-800C-F5951C998D22}.Release-nocrt-x86-unicode|Win32.ActiveCfg = Release-nocrt-x86-unicode|Win32 27 | {FE51110F-82BE-423E-800C-F5951C998D22}.Release-nocrt-x86-unicode|Win32.Build.0 = Release-nocrt-x86-unicode|Win32 28 | {FE51110F-82BE-423E-800C-F5951C998D22}.Release-nocrt-x86-unicode|Win32.Deploy.0 = Release-nocrt-x86-unicode|Win32 29 | {FE51110F-82BE-423E-800C-F5951C998D22}.Release-staticcrt-x86-unicode|Win32.ActiveCfg = Release-staticcrt-x86-unicode|Win32 30 | {FE51110F-82BE-423E-800C-F5951C998D22}.Release-staticcrt-x86-unicode|Win32.Build.0 = Release-staticcrt-x86-unicode|Win32 31 | EndGlobalSection 32 | GlobalSection(SolutionProperties) = preSolution 33 | HideSolutionNode = FALSE 34 | EndGlobalSection 35 | EndGlobal 36 | -------------------------------------------------------------------------------- /NSxfer.vcxproj: -------------------------------------------------------------------------------- 1 |  2 | 3 | 4 | 5 | Debug-x86-ansi 6 | Win32 7 | 8 | 9 | Debug-x86-unicode 10 | Win32 11 | 12 | 13 | Release-staticcrt-x86-ansi 14 | Win32 15 | 16 | 17 | Release-nocrt-x86-ansi 18 | Win32 19 | 20 | 21 | Release-staticcrt-x86-unicode 22 | Win32 23 | 24 | 25 | Release-nocrt-x86-unicode 26 | Win32 27 | 28 | 29 | 30 | {FE51110F-82BE-423E-800C-F5951C998D22} 31 | Win32Proj 32 | NSxfer 33 | 10.0 34 | 35 | 36 | 37 | DynamicLibrary 38 | true 39 | MultiByte 40 | v143 41 | 42 | 43 | DynamicLibrary 44 | true 45 | Unicode 46 | v143 47 | 48 | 49 | DynamicLibrary 50 | false 51 | Unicode 52 | true 53 | v143 54 | 55 | 56 | DynamicLibrary 57 | false 58 | true 59 | MultiByte 60 | v143 61 | 62 | 63 | DynamicLibrary 64 | false 65 | MultiByte 66 | true 67 | v143 68 | 69 | 70 | DynamicLibrary 71 | false 72 | Unicode 73 | true 74 | v143 75 | 76 | 77 | 78 | 79 | 80 | 81 | 82 | 83 | 84 | 85 | 86 | 87 | 88 | 89 | 90 | 91 | 92 | 93 | 94 | 95 | 96 | 97 | 98 | 99 | $(SolutionDir)$(Configuration)\temp\ 100 | 101 | 102 | $(SolutionDir)$(Configuration)\temp\ 103 | 104 | 105 | false 106 | $(SolutionDir)$(Configuration)\temp\ 107 | false 108 | 109 | 110 | $(SolutionDir)$(Configuration)\temp\ 111 | false 112 | false 113 | 114 | 115 | $(SolutionDir)$(Configuration)\temp\ 116 | false 117 | false 118 | 119 | 120 | $(SolutionDir)$(Configuration)\temp\ 121 | false 122 | false 123 | 124 | 125 | 126 | 127 | 128 | Level3 129 | WIN32;_DEBUG;_WINDOWS;_USRDLL;%(PreprocessorDefinitions) 130 | $(ProjectDir);%(AdditionalIncludeDirectories) 131 | 132 | 133 | Windows 134 | wininet.lib;shlwapi.lib;version.lib;%(AdditionalDependencies) 135 | 136 | 137 | call py -3 _get_nsis_sdk.py 138 | 139 | 140 | 141 | 142 | 143 | 144 | Level3 145 | WIN32;_DEBUG;_WINDOWS;_USRDLL;%(PreprocessorDefinitions) 146 | $(ProjectDir);%(AdditionalIncludeDirectories) 147 | 148 | 149 | Windows 150 | wininet.lib;shlwapi.lib;version.lib;%(AdditionalDependencies) 151 | 152 | 153 | call py -3 _get_nsis_sdk.py 154 | 155 | 156 | 157 | 158 | Level3 159 | 160 | 161 | WIN32;NDEBUG;_WINDOWS;_USRDLL;%(PreprocessorDefinitions) 162 | StreamingSIMDExtensions 163 | false 164 | Size 165 | $(ProjectDir);%(AdditionalIncludeDirectories) 166 | 167 | 168 | Windows 169 | true 170 | true 171 | wininet.lib;shlwapi.lib;version.lib;%(AdditionalDependencies) 172 | true 173 | DllMain 174 | 175 | 176 | call py -3 _get_nsis_sdk.py 177 | 178 | 179 | 180 | 181 | Level3 182 | 183 | 184 | WIN32;NDEBUG;_WINDOWS;_USRDLL;%(PreprocessorDefinitions) 185 | StreamingSIMDExtensions 186 | MultiThreaded 187 | Size 188 | $(ProjectDir);%(AdditionalIncludeDirectories) 189 | 190 | 191 | Windows 192 | true 193 | wininet.lib;shlwapi.lib;version.lib;%(AdditionalDependencies) 194 | true 195 | 196 | 197 | call py -3 _get_nsis_sdk.py 198 | 199 | 200 | 201 | 202 | Level3 203 | 204 | 205 | WIN32;NDEBUG;_WINDOWS;_USRDLL;%(PreprocessorDefinitions) 206 | StreamingSIMDExtensions 207 | false 208 | Size 209 | $(ProjectDir);%(AdditionalIncludeDirectories) 210 | 211 | 212 | Windows 213 | true 214 | true 215 | wininet.lib;shlwapi.lib;version.lib;%(AdditionalDependencies) 216 | true 217 | DllMain 218 | 219 | 220 | call py -3 _get_nsis_sdk.py 221 | 222 | 223 | 224 | 225 | Level3 226 | 227 | 228 | WIN32;NDEBUG;_WINDOWS;_USRDLL;%(PreprocessorDefinitions) 229 | StreamingSIMDExtensions 230 | MultiThreaded 231 | Size 232 | $(ProjectDir);%(AdditionalIncludeDirectories) 233 | 234 | 235 | Windows 236 | true 237 | true 238 | wininet.lib;shlwapi.lib;version.lib;%(AdditionalDependencies) 239 | 240 | 241 | call py -3 _get_nsis_sdk.py 242 | 243 | 244 | 245 | 246 | 247 | 248 | 249 | 250 | 251 | 252 | 253 | 254 | 255 | 256 | 257 | 258 | 259 | 260 | 261 | 262 | 263 | 264 | 265 | 266 | 267 | 268 | 269 | 270 | 271 | 272 | 273 | -------------------------------------------------------------------------------- /NSxfer.vcxproj.filters: -------------------------------------------------------------------------------- 1 |  2 | 3 | 4 | 5 | {4FC737F1-C7A5-4376-A066-2A32D752A2FF} 6 | cpp;c;cc;cxx;def;odl;idl;hpj;bat;asm;asmx 7 | 8 | 9 | {93995380-89BD-4b04-88EB-625FBE52EBFB} 10 | h;hpp;hxx;hm;inl;inc;xsd 11 | 12 | 13 | {67DA6AB6-F800-4c08-8B7A-83BB121AAD01} 14 | rc;ico;cur;bmp;dlg;rc2;rct;bin;rgs;gif;jpg;jpeg;jpe;resx;tiff;tif;png;wav;mfcribbon-ms 15 | 16 | 17 | {94173627-5bd7-4abd-ac49-e35d1171ab70} 18 | 19 | 20 | 21 | 22 | NSIS 23 | 24 | 25 | Source Files 26 | 27 | 28 | Source Files 29 | 30 | 31 | Source Files 32 | 33 | 34 | Source Files 35 | 36 | 37 | Source Files 38 | 39 | 40 | 41 | 42 | 43 | 44 | 45 | 46 | NSIS 47 | 48 | 49 | NSIS 50 | 51 | 52 | NSIS 53 | 54 | 55 | Header Files 56 | 57 | 58 | Header Files 59 | 60 | 61 | Header Files 62 | 63 | 64 | Header Files 65 | 66 | 67 | Header Files 68 | 69 | 70 | Header Files 71 | 72 | 73 | 74 | 75 | Resource Files 76 | 77 | 78 | -------------------------------------------------------------------------------- /NSxfer.vcxproj.user: -------------------------------------------------------------------------------- 1 |  2 | 3 | 4 | $(ProjectDir)\TestDebug\NSxfer-Debug-x86-ansi.exe 5 | WindowsLocalDebugger 6 | /dll "$(TargetPath)" 7 | 8 | 9 | $(ProjectDir)\TestDebug\NSxfer-Debug-x86-unicode.exe 10 | WindowsLocalDebugger 11 | /dll "$(TargetPath)" 12 | 13 | 14 | /dll "$(TargetPath)" 15 | WindowsLocalDebugger 16 | $(ProjectDir)\TestDebug\NSxfer-Debug-x86-ansi.exe 17 | 18 | 19 | /dll "$(TargetPath)" 20 | WindowsLocalDebugger 21 | $(ProjectDir)\TestDebug\NSxfer-Debug-x86-unicode.exe 22 | 23 | 24 | /dll "$(TargetPath)" 25 | WindowsLocalDebugger 26 | $(ProjectDir)\TestDebug\NSxfer-Debug-x86-ansi.exe 27 | 28 | 29 | /dll "$(TargetPath)" 30 | WindowsLocalDebugger 31 | $(ProjectDir)\TestDebug\NSxfer-Debug-x86-unicode.exe 32 | 33 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | # NSxfer ([NSIS](https://github.com/negrutiu/nsis) plugin) 2 | NSxfer gives you the means to perform complex HTTP/HTTPS transfers from a NSIS script 3 | 4 | [![License: zlib/libpng](https://img.shields.io/badge/License-zlib%2Flibpng-blue.svg)](LICENSE) 5 | [![Latest Release](https://img.shields.io/badge/dynamic/json.svg?label=Latest%20Release&url=https%3A%2F%2Fapi.github.com%2Frepos%2Fnegrutiu%2Fnsis-nsxfer%2Freleases%2Flatest&query=%24.name&colorB=orange)](../../releases/latest) 6 | [![Downloads](https://img.shields.io/github/downloads/negrutiu/nsis-nsxfer/total.svg?label=Downloads&colorB=orange)](../../releases/latest) 7 | [![GitHub issues](https://img.shields.io/github/issues/negrutiu/nsis-nsxfer.svg?label=Issues)](../../issues) 8 | 9 | ### Features: 10 | - **Multi threaded**: transfer multiple files in parallel 11 | - **Asynchronous**: start a download now, check its status later 12 | - **Aggressive**: multiple attempts to connect, reconnect, resume interrupted transfers, etc. 13 | - **NSIS aware**: download files at any installation stage (from `.onInit` callback, from `Sections`, from custom pages, silent installers, etc.) 14 | - **Informative**: plenty of useful information is available for each transfer (size, speed, HTTP status, HTTP headers, etc) 15 | - Supports all relevant **HTTP verbs** (GET, POST, PUT, HEAD, etc) 16 | - Supports **custom HTTP headers and data** 17 | - Supports **proxy servers** (both authenticated and open) 18 | - Supports files larger than **4GB** 19 | - Can download remote content to **RAM** instead of a file 20 | - Works well in **64-bit** [NSIS builds](https://github.com/negrutiu/nsis) 21 | - Many more... Check out the included [readme file](NSxfer.Readme.txt) 22 | 23 | ### Basic usage: 24 | - HTTP GET example: 25 | ``` 26 | NSxfer::Transfer /URL "https://httpbin.org/get?param1=1¶m2=2" /LOCAL "$TEMP\Response.json" /END 27 | Pop $0 ; "OK" for success 28 | ``` 29 | - HTTP POST `application/json`: 30 | ``` 31 | NSxfer::Transfer /URL "https://httpbin.org/post?param1=1¶m2=2" /LOCAL "$TEMP\MyFile.json" /METHOD POST /DATA '{"number_of_the_beast" : 666}' /HEADERS "Content-Type: application/json" /END 32 | Pop $0 ; "OK" for success 33 | ``` 34 | - HTTP POST `application/x-www-form-urlencoded`: 35 | ``` 36 | NSxfer::Transfer /URL "https://httpbin.org/post?param1=1¶m2=2" /LOCAL "$TEMP\MyFile.json" /METHOD POST /DATA 'User=My+User&Pass=My+Pass' /HEADERS "Content-Type: application/x-www-form-urlencoded" /END 37 | Pop $0 ; "OK" for success 38 | ``` 39 | - More complex examples in the [readme file](NSxfer.Readme.txt) 40 | -------------------------------------------------------------------------------- /SConscript: -------------------------------------------------------------------------------- 1 | target = 'NSxfer' 2 | 3 | files = Split(""" 4 | gui.c 5 | main.c 6 | queue.c 7 | thread.c 8 | utils.c 9 | """) 10 | 11 | resources = Split(""" 12 | resource.rc 13 | """) 14 | 15 | libs = Split(""" 16 | kernel32 17 | advapi32 18 | ole32 19 | uuid 20 | user32 21 | shlwapi 22 | version 23 | wininet 24 | """) 25 | 26 | examples = Split(""" 27 | Test/NSxfer-Test.nsi 28 | Test/NSxfer-Test-build.bat 29 | """) 30 | 31 | docs = Split(""" 32 | NSxfer.Readme.txt 33 | """) 34 | 35 | Import('BuildPlugin') 36 | 37 | BuildPlugin(target, files, libs, examples, docs, res = resources) -------------------------------------------------------------------------------- /Test/NSxfer-Test-build.bat: -------------------------------------------------------------------------------- 1 | REM :: Marius Negrutiu :: 2019/08/25 2 | @echo off 3 | SetLocal EnableDelayedExpansion 4 | 5 | if not exist "%NSIS%\makensis.exe" pushd "%~dp0..\.." && set NSIS=!CD!&& popd 6 | if not exist "%NSIS%\makensis.exe" set NSIS=%NSIS_INSTDIR% 7 | if not exist "%NSIS%\makensis.exe" set NSIS=%PROGRAMFILES%\NSIS 8 | if not exist "%NSIS%\makensis.exe" set NSIS=%PROGRAMFILES(X86)%\NSIS 9 | if not exist "%NSIS%\makensis.exe" echo ERROR: NSIS not found ^(Tip: NSIS_INSTDIR can be defined to point to NSIS binaries^) && pause && exit /B 2 10 | 11 | echo ******************************************************************************** 12 | echo %NSIS%\makensis.exe 13 | echo ******************************************************************************** 14 | 15 | Title Build: amd64-unicode 16 | "%NSIS%\makensis.exe" /V4 /DAMD64 "%~dp0\NSxfer-Test.nsi" 17 | if %errorlevel% neq 0 pause && exit /B %errorlevel% 18 | 19 | Title Build: x86-ansi 20 | "%NSIS%\makensis.exe" /V4 /DANSI "%~dp0\NSxfer-Test.nsi" 21 | if %errorlevel% neq 0 pause && exit /B %errorlevel% 22 | 23 | Title Build: x86-unicode 24 | "%NSIS%\makensis.exe" /V4 "%~dp0\NSxfer-Test.nsi" 25 | if %errorlevel% neq 0 pause && exit /B %errorlevel% 26 | -------------------------------------------------------------------------------- /Test/NSxfer-Test.nsi: -------------------------------------------------------------------------------- 1 | 2 | # NSxfer demo 3 | # Marius Negrutiu - https://github.com/negrutiu/nsis-nsxfer#nsis-plugin-nsxfer 4 | 5 | !ifdef AMD64 6 | !define _TARGET_ amd64-unicode 7 | Target ${_TARGET_} 8 | !else ifdef ANSI 9 | !define _TARGET_ x86-ansi 10 | Target ${_TARGET_} 11 | !else 12 | !define _TARGET_ x86-unicode ; Default 13 | !endif 14 | 15 | !include "MUI2.nsh" 16 | !define LOGICLIB_STRCMP 17 | !include "LogicLib.nsh" 18 | !include "Sections.nsh" 19 | 20 | !include "StrFunc.nsh" 21 | ${StrRep} ; Declare in advance 22 | ${StrTok} ; Declare in advance 23 | 24 | !define /ifndef NULL 0 25 | 26 | 27 | # NSxfer.dll development location 28 | !ifdef DEVEL 29 | !if ! /FileExists "..\Release-mingw-${_TARGET_}\NSxfer.dll" 30 | !error "Missing \Release-mingw-${_TARGET_}\NSxfer.dll" 31 | !endif 32 | !AddPluginDir /amd64-unicode "..\Release-mingw-amd64-unicode" 33 | !AddPluginDir /x86-unicode "..\Release-mingw-x86-unicode" 34 | !AddPluginDir /x86-ansi "..\Release-mingw-x86-ansi" 35 | !endif 36 | 37 | # GUI settings 38 | !define MUI_ICON "${NSISDIR}\Contrib\Graphics\Icons\orange-install-nsis.ico" 39 | !define MUI_WELCOMEFINISHPAGE_BITMAP "${NSISDIR}\Contrib\Graphics\Wizard\orange-nsis.bmp" 40 | 41 | # Welcome page 42 | ;!define MUI_WELCOMEPAGE_TITLE_3LINES 43 | ;!insertmacro MUI_PAGE_WELCOME 44 | 45 | # Components page 46 | InstType "All" ; 1 47 | InstType "None" ; 2 48 | !define MUI_COMPONENTSPAGE_NODESC 49 | !insertmacro MUI_PAGE_COMPONENTS 50 | 51 | # Installation page 52 | !insertmacro MUI_PAGE_INSTFILES 53 | 54 | # Language 55 | !insertmacro MUI_LANGUAGE "English" 56 | ;!insertmacro MUI_LANGUAGE "Romanian" 57 | !insertmacro MUI_RESERVEFILE_LANGDLL 58 | 59 | # Installer details 60 | Name "NSxfer-Test-${_TARGET_}" 61 | OutFile "NSxfer-Test-${_TARGET_}.exe" 62 | XPStyle on 63 | RequestExecutionLevel user ; Don't require UAC elevation 64 | ShowInstDetails show 65 | ManifestDPIAware true 66 | 67 | 68 | #---------------------------------------------------------------# 69 | # .onInit # 70 | #---------------------------------------------------------------# 71 | Function .onInit 72 | 73 | ; Initializations 74 | InitPluginsDir 75 | 76 | ; Language selection 77 | !define MUI_LANGDLL_ALLLANGUAGES 78 | !insertmacro MUI_LANGDLL_DISPLAY 79 | 80 | /* 81 | ; .onInit download demo 82 | ; NOTE: Transfers from .onInit can be either Silent or Popup (no Page!) 83 | !define /redef LINK 'https://httpbin.org/post?param1=1¶m2=2' 84 | !define /redef FILE '$EXEDIR\_Post_onInit.json' 85 | DetailPrint 'NSxfer::Transfer "${LINK}" "${FILE}"' 86 | NSxfer::Transfer /METHOD POST /MODE Popup /URL "${LINK}" /LOCAL "${FILE}" /DATA 'User=My+User&Pass=My+Pass' /HEADERS "Content-Type: application/x-www-form-urlencoded$\r$\nContent-Dummy: Dummy" /TIMEOUTCONNECT 15000 /TIMEOUTRECONNECT 60000 /REFERER "https://wikipedia.org" /END 87 | Pop $0 88 | */ 89 | 90 | ; Quick .onInit plugin test 91 | NSxfer::QueryGlobal /PLUGINNAME /END 92 | Pop $0 93 | ${If} $0 != "NSxfer" 94 | MessageBox MB_ICONSTOP '[.onInit]$\nFailed to query plugin name$\nReturn value: "$0"' 95 | ${EndIf} 96 | FunctionEnd 97 | 98 | 99 | Section "Cleanup test files" 100 | SectionIn 1 2 ; All 101 | FindFirst $0 $1 "$EXEDIR\_*.*" 102 | loop: 103 | StrCmp $1 "" done 104 | Delete "$EXEDIR\$1" 105 | FindNext $0 $1 106 | Goto loop 107 | done: 108 | FindClose $0 109 | SectionEnd 110 | 111 | 112 | Section /o "HTTP GET (Page mode)" 113 | SectionIn 1 ; All 114 | 115 | DetailPrint '-----------------------------------------------' 116 | DetailPrint '${__SECTION__}' 117 | DetailPrint '-----------------------------------------------' 118 | 119 | !define /redef LINK 'https://download.sysinternals.com/files/SysinternalsSuite.zip' 120 | !define /redef FILE '$EXEDIR\_SysinternalsSuite.zip' 121 | DetailPrint 'NSxfer::Transfer "${LINK}" "${FILE}"' 122 | NSxfer::Transfer /URL "${LINK}" /LOCAL "${FILE}" /TIMEOUTCONNECT 15000 /TIMEOUTRECONNECT 30000 /END 123 | Pop $0 124 | DetailPrint "Status: $0" 125 | SectionEnd 126 | 127 | 128 | Section /o "HTTP GET (Popup mode)" 129 | SectionIn 1 ; All 130 | 131 | DetailPrint '-----------------------------------------------' 132 | DetailPrint '${__SECTION__}' 133 | DetailPrint '-----------------------------------------------' 134 | 135 | ; NOTE: github.com doesn't support Range headers 136 | !define /redef LINK `https://github.com/cuckoobox/cuckoo/archive/master.zip` 137 | !define /redef FILE "$EXEDIR\_CuckooBox_master.zip" 138 | DetailPrint 'NSxfer::Transfer "${LINK}" "${FILE}"' 139 | NSxfer::Transfer /URL "${LINK}" /LOCAL "${FILE}" /Mode Popup /END 140 | Pop $0 141 | DetailPrint "Status: $0" 142 | SectionEnd 143 | 144 | 145 | Section /o "HTTP GET (Silent mode)" 146 | SectionIn 1 ; All 147 | 148 | DetailPrint '-----------------------------------------------' 149 | DetailPrint '${__SECTION__}' 150 | DetailPrint '-----------------------------------------------' 151 | 152 | !define /redef LINK `https://download.mozilla.org/?product=firefox-stub&os=win&lang=en-US` 153 | !define /redef FILE "$EXEDIR\_Firefox.exe" 154 | DetailPrint 'NSxfer::Transfer "${LINK}" "${FILE}"' 155 | NSxfer::Transfer /URL "${LINK}" /LOCAL "${FILE}" /Mode Silent /END 156 | Pop $0 157 | DetailPrint "Status: $0" 158 | SectionEnd 159 | 160 | 161 | Section /o "HTTP GET (Memory)" 162 | SectionIn 1 ; All 163 | 164 | DetailPrint '-----------------------------------------------' 165 | DetailPrint '${__SECTION__}' 166 | DetailPrint '-----------------------------------------------' 167 | 168 | !define /redef LINK `https://wikipedia.org` 169 | !define /redef FILE "memory" 170 | DetailPrint 'NSxfer::Transfer "${LINK}" "${FILE}"' 171 | 172 | NSxfer::Transfer /URL "${LINK}" /LOCAL "${FILE}" /RETURNID /END 173 | Pop $0 ; Unique transfer ID 174 | 175 | NSxfer::Query /ID $0 /ERRORTEXT /CONTENT /END 176 | Pop $1 ; Transfer status 177 | Pop $2 ; Remote content (trimmed to NSIS_MAX_STRLEN) 178 | 179 | StrCpy $2 $2 64 180 | StrCpy $2 "$2..." 181 | DetailPrint "Status: $1, Content: $2" 182 | SectionEnd 183 | 184 | 185 | !define /redef COUNT 32 186 | Section /o "HTTP GET (Memory * ${COUNT}x)" 187 | SectionIn 1 ; All 188 | 189 | DetailPrint '-----------------------------------------------' 190 | DetailPrint '${__SECTION__}' 191 | DetailPrint '-----------------------------------------------' 192 | 193 | !define /redef FILE "memory" 194 | 195 | ${For} $R0 1 ${COUNT} 196 | StrCpy $R1 "https://httpbin.org/post?index=$R0&total=${COUNT}" 197 | DetailPrint 'NSxfer::Request [$R0/${COUNT}] $R1 "${FILE}"' 198 | NSxfer::Request /URL $R1 /LOCAL "${FILE}" /METHOD POST /END 199 | Pop $0 200 | ${Next} 201 | 202 | NSxfer::Wait /ABORT "" "" /END 203 | Pop $1 204 | SectionEnd 205 | 206 | 207 | Section /o "HTTP GET (Parallel transfers)" 208 | SectionIn 1 ; All 209 | 210 | DetailPrint '-----------------------------------------------' 211 | DetailPrint '${__SECTION__}' 212 | DetailPrint '-----------------------------------------------' 213 | 214 | ; Request 1 215 | !define /redef LINK `https://download.mozilla.org/?product=firefox-stub&os=win&lang=en-US` 216 | !define /redef FILE "$EXEDIR\_Firefox(2).exe" 217 | DetailPrint 'NSxfer::Request "${LINK}" "${FILE}"' 218 | NSxfer::Request /URL "${LINK}" /LOCAL "${FILE}" /Mode Silent /END 219 | Pop $1 220 | 221 | ; Request 2 222 | !define /redef LINK `https://download.mozilla.org/?product=firefox-stub&os=win&lang=en-US` 223 | !define /redef FILE "$EXEDIR\_Firefox(3).exe" 224 | DetailPrint 'NSxfer::Request "${LINK}" "${FILE}"' 225 | NSxfer::Request /URL "${LINK}" /LOCAL "${FILE}" /TIMEOUTCONNECT 15000 /END 226 | Pop $2 227 | 228 | ; Request 3 229 | !define /redef LINK `http://download.osmc.tv/installers/osmc-installer.exe` 230 | !define /redef FILE "$EXEDIR\_osmc_installer.exe" 231 | DetailPrint 'NSxfer::Request "${LINK}" "${FILE}"' 232 | NSxfer::Request /URL "${LINK}" /LOCAL "${FILE}" /TIMEOUTCONNECT 15000 /END 233 | Pop $3 234 | 235 | ; Wait for all 236 | DetailPrint 'Waiting . . .' 237 | NSxfer::Wait /MODE Page /ABORT "Abort" "Are you sure?" /END 238 | Pop $0 239 | 240 | ; Validate individual transfer status... 241 | ; TODO 242 | 243 | DetailPrint 'Done' 244 | SectionEnd 245 | 246 | 247 | Section /o "-HTTP GET (proxy)" 248 | SectionIn 1 ; All 249 | 250 | DetailPrint '-----------------------------------------------' 251 | DetailPrint '${__SECTION__}' 252 | DetailPrint '-----------------------------------------------' 253 | 254 | !define /redef LINK "https://download.sysinternals.com/files/SysinternalsSuite.zip" 255 | !define /redef FILE "$EXEDIR\_SysinternalsSuiteLive_proxy.zip" 256 | !define /redef PROXY "http=54.36.139.108:8118 https=54.36.139.108:8118" ; France 257 | DetailPrint 'NSxfer::Transfer /proxy ${PROXY} "${LINK}" "${FILE}"' 258 | NSxfer::Transfer /PRIORITY 10 /URL "${LINK}" /LOCAL "${FILE}" /PROXY "${PROXY}" /TIMEOUTCONNECT 15000 /TIMEOUTRECONNECT 60000 /ABORT "Abort" "Are you sure?" /END 259 | Pop $0 260 | DetailPrint "Status: $0" 261 | SectionEnd 262 | 263 | 264 | Section /o "HTTP POST (application/json)" 265 | SectionIn 1 ; All 266 | 267 | DetailPrint '-----------------------------------------------' 268 | DetailPrint '${__SECTION__}' 269 | DetailPrint '-----------------------------------------------' 270 | 271 | !define /redef LINK 'https://httpbin.org/post?param1=1¶m2=2' 272 | !define /redef FILE '$EXEDIR\_Post_json.json' 273 | DetailPrint 'NSxfer::Transfer "${LINK}" "${FILE}"' 274 | NSxfer::Transfer /METHOD Post /URL "${LINK}" /LOCAL "${FILE}" /DATA '{"number_of_the_beast" : 666}' /HEADERS "Content-Type: application/json" /TIMEOUTCONNECT 15000 /TIMEOUTRECONNECT 60000 /REFERER "https://wikipedia.org" /END 275 | Pop $0 276 | DetailPrint "Status: $0" 277 | SectionEnd 278 | 279 | 280 | Section /o "HTTP POST (application/x-www-form-urlencoded)" 281 | SectionIn 1 ; All 282 | 283 | DetailPrint '-----------------------------------------------' 284 | DetailPrint '${__SECTION__}' 285 | DetailPrint '-----------------------------------------------' 286 | 287 | !define /redef LINK 'http://httpbin.org/post?param1=1¶m2=2' 288 | !define /redef FILE '$EXEDIR\_Post_form.json' 289 | DetailPrint 'NSxfer::Transfer "${LINK}" "${FILE}"' 290 | NSxfer::Transfer /METHOD POST /URL "${LINK}" /LOCAL "${FILE}" /DATA 'User=My+User&Pass=My+Pass' /HEADERS "Content-Type: application/x-www-form-urlencoded$\r$\nContent-Dummy: Dummy" /TIMEOUTCONNECT 15000 /TIMEOUTRECONNECT 60000 /REFERER "https://wikipedia.org" /END 291 | Pop $0 292 | DetailPrint "Status: $0" 293 | SectionEnd 294 | 295 | 296 | !macro TEST_DEPENDENCY_REQUEST _Filename _DependsOn 297 | !define /redef LINK `http://httpbin.org/post` 298 | DetailPrint 'NSxfer::Request "${LINK}" "${_Filename}.txt"' 299 | NSxfer::Request /PRIORITY 2000 /DEPEND ${_DependsOn} /METHOD POST /URL "${LINK}" /LOCAL "$EXEDIR\${_Filename}.txt" /HEADERS "Content-Type: application/x-www-form-urlencoded$\r$\nContent-Test: TEST" /DATA "user=My+User+Name&pass=My+Password" /TIMEOUTCONNECT 15000 /TIMEOUTRECONNECT 60000 /REFERER "${LINK}" /END 300 | Pop $0 ; Request ID 301 | !macroend 302 | 303 | 304 | Section /o "Test Dependencies (depend on first request)" 305 | ;SectionIn 1 ; All 306 | 307 | StrCpy $R0 0 ; First request ID 308 | StrCpy $R1 0 ; Last request ID 309 | 310 | ; First request 311 | !insertmacro TEST_DEPENDENCY_REQUEST "_DependOnFirst1" -1 312 | StrCpy $R0 $0 ; Remember the first ID 313 | StrCpy $R1 $0 ; Remember the last request ID 314 | 315 | ; Subsequent requests 316 | ${For} $1 2 20 317 | !insertmacro TEST_DEPENDENCY_REQUEST "_DependOnFirst$1" $R0 318 | StrCpy $R1 $0 ; Remember the last request ID 319 | ${Next} 320 | 321 | ; Sleep 322 | ;Sleep 2000 323 | 324 | ; Unlock the first request, and consequently all the others... 325 | NSxfer::Set /ID $R0 /SETDEPEND 0 /END 326 | Pop $0 ; Error code. Ignored 327 | 328 | ; Wait 329 | DetailPrint 'Waiting . . .' 330 | NSxfer::Wait /MODE Page /ABORT "Abort" "Are you sure?" /END 331 | Pop $0 332 | 333 | SectionEnd 334 | 335 | 336 | Section /o "Test Dependencies (depend on previous request)" 337 | ;SectionIn 1 ; All 338 | 339 | StrCpy $R0 0 ; First request ID 340 | StrCpy $R1 0 ; Last request ID 341 | 342 | ; First request 343 | !insertmacro TEST_DEPENDENCY_REQUEST "_DependOnPrevious1" -1 344 | StrCpy $R0 $0 ; Remember the first ID 345 | StrCpy $R1 $0 ; Remember the last request ID 346 | 347 | ; Subsequent requests 348 | ${For} $1 2 20 349 | !insertmacro TEST_DEPENDENCY_REQUEST "_DependOnPrevious$1" $R1 350 | StrCpy $R1 $0 ; Remember the last request ID 351 | ${Next} 352 | 353 | ; Sleep 354 | ;Sleep 2000 355 | 356 | ; Unlock the first request, and consequently all the others... 357 | NSxfer::Set /ID $R0 /SETDEPEND 0 /END 358 | Pop $0 ; Error code. Ignored 359 | 360 | ; Wait 361 | DetailPrint 'Waiting . . .' 362 | NSxfer::Wait /MODE Page /ABORT "Abort" "Are you sure?" /END 363 | Pop $0 364 | 365 | SectionEnd 366 | 367 | 368 | ; Input: [Stack] Request ID 369 | ; Output: None 370 | Function PrintRequest 371 | Exch $R1 ; Request ID 372 | Push $0 373 | Push $1 374 | Push $2 375 | 376 | NSxfer::Query /ID $R1 /PRIORITY /DEPEND /STATUS /WININETSTATUS /METHOD /URL /PROXY /IP /LOCAL /DATA /SENTHEADERS /RECVHEADERS /RECVSIZE /FILESIZE /PERCENT /SPEEDBYTES /SPEED /TIMEWAITING /TIMEDOWNLOADING /ERRORCODE /ERRORTEXT /CONNECTIONDROPS /CONTENT /END 377 | 378 | StrCpy $0 "[>] ID:$R1" 379 | Pop $1 ;PRIORITY 380 | StrCpy $0 "$0, Prio:$1" 381 | Pop $1 ;DEPEND 382 | IntCmp $1 0 +2 +1 +1 383 | StrCpy $0 "$0, DependsOn:$1" 384 | Pop $1 ;STATUS 385 | StrCpy $0 "$0, [$1]" 386 | Pop $1 ;WININETSTATUS 387 | StrCpy $0 "$0, WinINet:$1" 388 | DetailPrint $0 389 | 390 | StrCpy $0 " [Request]" 391 | Pop $1 ;METHOD 392 | StrCpy $0 "$0 $1" 393 | Pop $1 ;URL 394 | StrCpy $0 "$0 $1" 395 | DetailPrint $0 396 | 397 | Pop $1 ;PROXY 398 | StrCmp $1 "" +2 +1 399 | DetailPrint " [Proxy] $1" 400 | Pop $1 ;IP 401 | StrCmp $1 "" +2 +1 402 | DetailPrint " [Server] $1" 403 | 404 | Pop $1 ;LOCAL 405 | DetailPrint " [Local] $1" 406 | 407 | Pop $1 ;DATA 408 | ${If} $1 != "" 409 | ${StrRep} $1 "$1" "$\r" "\r" 410 | ${StrRep} $1 "$1" "$\n" "\n" 411 | DetailPrint " [Sent Data] $1" 412 | ${EndIf} 413 | Pop $1 ;SENTHEADERS 414 | ${If} $1 != "" 415 | DetailPrint " [Sent Headers]" 416 | ${For} $2 0 100 417 | ${StrTok} $0 $1 "$\r$\n" $2 1 418 | ${If} $0 == "" 419 | ${Break} 420 | ${EndIf} 421 | DetailPrint " $0" 422 | ${Next} 423 | ${EndIf} 424 | Pop $1 ;RECVHEADERS 425 | ${If} $1 != "" 426 | DetailPrint " [Recv Headers]" 427 | ${For} $2 0 100 428 | ${StrTok} $0 $1 "$\r$\n" $2 1 429 | ${If} $0 == "" 430 | ${Break} 431 | ${EndIf} 432 | DetailPrint " $0" 433 | ${Next} 434 | ${EndIf} 435 | 436 | StrCpy $0 " [Size]" 437 | Pop $1 ;RECVSIZE 438 | StrCpy $0 "$0 $1" 439 | Pop $1 ;FILESIZE 440 | StrCpy $0 "$0/$1" 441 | Pop $1 ;PERCENT 442 | StrCpy $0 "$0 ($1%)" 443 | Pop $1 ;SPEEDBYTES 444 | Pop $1 ;SPEED 445 | StrCmp $1 "" +2 +1 446 | StrCpy $0 "$0 @ $1" 447 | DetailPrint "$0" 448 | 449 | StrCpy $0 " [Time]" 450 | Pop $1 ;TIMEWAITING 451 | StrCpy $0 "$0 Waiting $1ms" 452 | Pop $1 ;TIMEDOWNLOADING 453 | StrCpy $0 "$0, Downloading $1ms" 454 | DetailPrint "$0" 455 | 456 | StrCpy $0 " [Error]" 457 | Pop $1 ;ERRORCODE 458 | StrCpy $0 "$0 $1" 459 | Pop $1 ;ERRORTEXT 460 | StrCpy $0 "$0, $1" 461 | Pop $1 ;CONNECTIONDROPS 462 | IntCmp $1 0 +2 +1 +1 463 | StrCpy $0 "$0, Drops:$1" 464 | DetailPrint "$0" 465 | 466 | Pop $1 ;CONTENT 467 | ${If} $1 != "" 468 | ; DetailPrint " [Content]" 469 | ; ${For} $2 0 100 470 | ; ${StrTok} $0 $1 "$\r$\n" $2 1 471 | ; ${If} $0 == "" 472 | ; ${Break} 473 | ; ${EndIf} 474 | ; DetailPrint " $0" 475 | ; ${Next} 476 | ${StrRep} $1 "$1" "$\r" "\r" 477 | ${StrRep} $1 "$1" "$\n" "\n" 478 | DetailPrint " [Content] $1" 479 | ${EndIf} 480 | 481 | Pop $2 482 | Pop $1 483 | Pop $0 484 | Pop $R1 485 | FunctionEnd 486 | 487 | 488 | Function PrintSummary 489 | 490 | Push $0 491 | Push $1 492 | Push $2 493 | Push $3 494 | Push $R0 495 | Push $R1 496 | Push $R2 497 | Push $R3 498 | Push $R4 499 | 500 | ; Enumerate all transfers (completed + pending + waiting) 501 | DetailPrint "NSxfer::Enumerate" 502 | NSxfer::Enumerate /END 503 | 504 | Pop $1 ; Count 505 | DetailPrint " $1 requests" 506 | ${For} $0 1 $1 507 | Call PrintRequest 508 | ${Next} 509 | 510 | NSxfer::QueryGlobal /TOTALCOUNT /TOTALCOMPLETED /TOTALDOWNLOADING /TOTALSPEED /TOTALTHREADS /PLUGINNAME /PLUGINVERSION /USERAGENT /END 511 | Pop $R0 ; Total 512 | Pop $R1 ; Completed 513 | Pop $R2 ; Downloading 514 | Pop $R3 ; Speed 515 | Pop $R4 ; Worker threads 516 | Pop $1 ; Plugin Name 517 | Pop $2 ; Plugin Version 518 | Pop $3 ; Useragent 519 | 520 | DetailPrint "Transferring $R1+$R2/$R0 items at $R3 using $R4 worker threads" 521 | DetailPrint "[>] $1 $2" 522 | DetailPrint "[>] User agent: $3" 523 | 524 | Pop $R4 525 | Pop $R3 526 | Pop $R2 527 | Pop $R1 528 | Pop $R0 529 | Pop $3 530 | Pop $2 531 | Pop $1 532 | Pop $0 533 | 534 | FunctionEnd 535 | 536 | 537 | Section -Summary 538 | DetailPrint '-----------------------------------------------' 539 | DetailPrint ' ${__SECTION__}' 540 | DetailPrint '-----------------------------------------------' 541 | Call PrintSummary 542 | SectionEnd 543 | -------------------------------------------------------------------------------- /Test/build64.bat: -------------------------------------------------------------------------------- 1 | @echo off 2 | setlocal enabledelayedexpansion 3 | 4 | if not exist "%NSIS%\makensis.exe" pushd "%~dp0..\.." && set NSIS=!cd!&& popd 5 | if not exist "%NSIS%\makensis.exe" set NSIS=%PROGRAMFILES%\NSIS 6 | if not exist "%NSIS%\makensis.exe" set NSIS=%PROGRAMFILES(X86)%\NSIS 7 | if not exist "%NSIS%\makensis.exe" for /f "delims=*" %%f in ('where makensis.exe 2^> nul') do pushd "%%~dpf" && set NSIS=!cd!&& popd 8 | if not exist "%NSIS%\makensis.exe" echo ERROR: NSIS not found&& pause && exit /b 2 9 | 10 | echo ******************************************************************************** 11 | echo %NSIS%\makensis.exe 12 | echo ******************************************************************************** 13 | 14 | "%NSIS%\makensis.exe" /DDEVEL /V4 /DAMD64 "%~dp0\NSxfer-Test.nsi" || pause && exit /b !errorlevel! 15 | 16 | REM echo ---------------------------------------------------------- 17 | REM set exe=NSxfer-Test-amd64-unicode.exe 18 | REM set /P answer=Execute %exe% (y/N)? 19 | REM if /I "%answer%" equ "y" "%~dp0\%exe%" 20 | -------------------------------------------------------------------------------- /Test/buildA.bat: -------------------------------------------------------------------------------- 1 | @echo off 2 | setlocal enabledelayedexpansion 3 | 4 | if not exist "%NSIS%\makensis.exe" pushd "%~dp0..\.." && set NSIS=!cd!&& popd 5 | if not exist "%NSIS%\makensis.exe" set NSIS=%PROGRAMFILES%\NSIS 6 | if not exist "%NSIS%\makensis.exe" set NSIS=%PROGRAMFILES(X86)%\NSIS 7 | if not exist "%NSIS%\makensis.exe" for /f "delims=*" %%f in ('where makensis.exe 2^> nul') do pushd "%%~dpf" && set NSIS=!cd!&& popd 8 | if not exist "%NSIS%\makensis.exe" echo ERROR: NSIS not found&& pause && exit /b 2 9 | 10 | echo ******************************************************************************** 11 | echo %NSIS%\makensis.exe 12 | echo ******************************************************************************** 13 | 14 | "%NSIS%\makensis.exe" /DDEVEL /V4 /DANSI "%~dp0\NSxfer-Test.nsi" || pause && exit /b !errorlevel! 15 | 16 | REM echo ---------------------------------------------------------- 17 | REM set exe=NSxfer-Test-x86-ansi.exe 18 | REM set /P answer=Execute %exe% (y/N)? 19 | REM if /I "%answer%" equ "y" "%~dp0\%exe%" 20 | -------------------------------------------------------------------------------- /Test/buildW.bat: -------------------------------------------------------------------------------- 1 | @echo off 2 | setlocal enabledelayedexpansion 3 | 4 | if not exist "%NSIS%\makensis.exe" pushd "%~dp0..\.." && set NSIS=!cd!&& popd 5 | if not exist "%NSIS%\makensis.exe" set NSIS=%PROGRAMFILES%\NSIS 6 | if not exist "%NSIS%\makensis.exe" set NSIS=%PROGRAMFILES(X86)%\NSIS 7 | if not exist "%NSIS%\makensis.exe" for /f "delims=*" %%f in ('where makensis.exe 2^> nul') do pushd "%%~dpf" && set NSIS=!cd!&& popd 8 | if not exist "%NSIS%\makensis.exe" echo ERROR: NSIS not found&& pause && exit /b 2 9 | 10 | echo ******************************************************************************** 11 | echo %NSIS%\makensis.exe 12 | echo ******************************************************************************** 13 | 14 | "%NSIS%\makensis.exe" /DDEVEL /V4 "%~dp0\NSxfer-Test.nsi" || pause && exit /b !errorlevel! 15 | 16 | REM echo ---------------------------------------------------------- 17 | REM set exe=NSxfer-Test-x86-unicode.exe 18 | REM set /P answer=Execute %exe% (y/N)? 19 | REM if /I "%answer%" equ "y" "%~dp0\%exe%" 20 | -------------------------------------------------------------------------------- /Test/cleanup.bat: -------------------------------------------------------------------------------- 1 | @del "%~dp0\*.exe" 2 | @del "%~dp0\_*.*" 3 | -------------------------------------------------------------------------------- /TestDebug/NSxfer-Debug.nsi: -------------------------------------------------------------------------------- 1 | 2 | # NSxfer development script 3 | # Marius Negrutiu - https://github.com/negrutiu/nsis-nsxfer#nsis-plugin-nsxfer 4 | 5 | !ifdef AMD64 6 | !define _TARGET_ amd64-unicode 7 | Target ${_TARGET_} 8 | !else ifdef ANSI 9 | !define _TARGET_ x86-ansi 10 | Target ${_TARGET_} 11 | !else 12 | !define _TARGET_ x86-unicode ; Default 13 | !endif 14 | 15 | # /dll commandline parameter 16 | Var /global DLL 17 | 18 | !include "MUI2.nsh" 19 | !define LOGICLIB_STRCMP 20 | !include "LogicLib.nsh" 21 | !include "Sections.nsh" 22 | 23 | !include "FileFunc.nsh" 24 | !insertmacro GetOptions 25 | !insertmacro GetParameters 26 | 27 | !include "StrFunc.nsh" 28 | ${StrRep} ; Declare in advance 29 | ${StrTok} ; Declare in advance 30 | 31 | !define /ifndef NULL 0 32 | 33 | 34 | # GUI settings 35 | !define MUI_ICON "${NSISDIR}\Contrib\Graphics\Icons\orange-install-nsis.ico" 36 | !define MUI_WELCOMEFINISHPAGE_BITMAP "${NSISDIR}\Contrib\Graphics\Wizard\orange-nsis.bmp" 37 | 38 | # Welcome page 39 | ;!define MUI_WELCOMEPAGE_TITLE_3LINES 40 | ;!insertmacro MUI_PAGE_WELCOME 41 | 42 | # Components page 43 | InstType "All" ; 1 44 | InstType "None" ; 2 45 | !define MUI_COMPONENTSPAGE_NODESC 46 | !insertmacro MUI_PAGE_COMPONENTS 47 | 48 | # Installation page 49 | !insertmacro MUI_PAGE_INSTFILES 50 | 51 | # Language 52 | !insertmacro MUI_LANGUAGE "English" 53 | ;!insertmacro MUI_LANGUAGE "Romanian" 54 | !insertmacro MUI_RESERVEFILE_LANGDLL 55 | 56 | # Installer details 57 | Name "NSxfer-Debug-${_TARGET_}" 58 | OutFile "NSxfer-Debug-${_TARGET_}.exe" 59 | XPStyle on 60 | RequestExecutionLevel user ; Don't require UAC elevation 61 | ShowInstDetails show 62 | ManifestDPIAware true 63 | 64 | !macro STACK_VERIFY_START 65 | ; This macro is optional. You don't have to use it in your script! 66 | Push "MyStackTop" 67 | !macroend 68 | 69 | !macro STACK_VERIFY_END 70 | ; This macro is optional. You don't have to use it in your script! 71 | Pop $R9 72 | StrCmp $R9 "MyStackTop" +2 +1 73 | MessageBox MB_ICONSTOP "Stack is NOT OK" 74 | !macroend 75 | 76 | #---------------------------------------------------------------# 77 | # .onInit # 78 | #---------------------------------------------------------------# 79 | Function .onInit 80 | 81 | ; Initializations 82 | InitPluginsDir 83 | 84 | ; Language selection 85 | !define MUI_LANGDLL_ALLLANGUAGES 86 | !insertmacro MUI_LANGDLL_DISPLAY 87 | 88 | ; Command line 89 | ${GetParameters} $R0 90 | ${GetOptions} "$R0" "/dll" $DLL 91 | ${If} ${Errors} 92 | MessageBox MB_ICONSTOP 'Syntax:$\n"$EXEFILE" /DLL ' 93 | Abort 94 | ${EndIf} 95 | 96 | /* 97 | ; .onInit download demo 98 | ; NOTE: Transfers from .onInit can be either Silent or Popup (no Page!) 99 | !insertmacro STACK_VERIFY_START 100 | !define /redef LINK 'https://httpbin.org/post?param1=1¶m2=2' 101 | !define /redef FILE '$EXEDIR\_Post_onInit.json' 102 | DetailPrint 'NSxfer::Transfer "${LINK}" "${FILE}"' 103 | Push "/END" 104 | Push "https://wikipedia.org" 105 | Push "/REFERER" 106 | Push 60000 107 | Push "/TIMEOUTRECONNECT" 108 | Push 15000 109 | Push "/TIMEOUTCONNECT" 110 | Push "Content-Type: application/x-www-form-urlencoded$\r$\nContent-Dummy: Dummy" 111 | Push "/HEADERS" 112 | Push 'User=My+User&Pass=My+Pass' 113 | Push "/DATA" 114 | Push "${FILE}" 115 | Push "/LOCAL" 116 | Push "${LINK}" 117 | Push "/URL" 118 | Push "POPUP" 119 | Push "/MODE" 120 | Push "POST" 121 | Push "/METHOD" 122 | CallInstDLL $DLL Transfer 123 | Pop $0 124 | !insertmacro STACK_VERIFY_END 125 | */ 126 | 127 | ; Quick .onInit plugin test 128 | Push "/END" 129 | Push "/PLUGINNAME" 130 | CallInstDLL $DLL QueryGlobal 131 | Pop $0 132 | ${If} $0 != "NSxfer" 133 | MessageBox MB_ICONSTOP '[.onInit]$\nFailed to query plugin name$\nReturn value: "$0"' 134 | ${EndIf} 135 | FunctionEnd 136 | 137 | 138 | Section "Cleanup test files" 139 | SectionIn 1 2 ; All 140 | FindFirst $0 $1 "$EXEDIR\_*.*" 141 | loop: 142 | StrCmp $1 "" done 143 | Delete "$EXEDIR\$1" 144 | FindNext $0 $1 145 | Goto loop 146 | done: 147 | FindClose $0 148 | SectionEnd 149 | 150 | 151 | Section /o "HTTP GET (Page mode)" 152 | SectionIn 1 ; All 153 | 154 | DetailPrint '-----------------------------------------------' 155 | DetailPrint '${__SECTION__}' 156 | DetailPrint '-----------------------------------------------' 157 | 158 | !insertmacro STACK_VERIFY_START 159 | !define /redef LINK 'https://download.sysinternals.com/files/SysinternalsSuite.zip' 160 | !define /redef FILE '$EXEDIR\_SysinternalsSuite.zip' 161 | DetailPrint 'NSxfer::Transfer "${LINK}" "${FILE}"' 162 | Push "/END" 163 | Push 30000 164 | Push "/TIMEOUTRECONNECT" 165 | Push 15000 166 | Push "/TIMEOUTCONNECT" 167 | Push "${FILE}" 168 | Push "/LOCAL" 169 | Push "${LINK}" 170 | Push "/URL" 171 | CallInstDLL $DLL Transfer 172 | Pop $0 173 | DetailPrint "Status: $0" 174 | !insertmacro STACK_VERIFY_END 175 | SectionEnd 176 | 177 | 178 | Section /o "HTTP GET (Popup mode)" 179 | SectionIn 1 ; All 180 | 181 | DetailPrint '-----------------------------------------------' 182 | DetailPrint '${__SECTION__}' 183 | DetailPrint '-----------------------------------------------' 184 | 185 | !insertmacro STACK_VERIFY_START 186 | ; NOTE: github.com doesn't support Range headers 187 | !define /redef LINK `https://github.com/cuckoobox/cuckoo/archive/master.zip` 188 | !define /redef FILE "$EXEDIR\_CuckooBox_master.zip" 189 | DetailPrint 'NSxfer::Transfer "${LINK}" "${FILE}"' 190 | Push "/END" 191 | Push "Popup" 192 | Push "/Mode" 193 | Push "${FILE}" 194 | Push "/LOCAL" 195 | Push "${LINK}" 196 | Push "/URL" 197 | CallInstDLL $DLL Transfer 198 | Pop $0 199 | DetailPrint "Status: $0" 200 | !insertmacro STACK_VERIFY_END 201 | SectionEnd 202 | 203 | 204 | Section /o "HTTP GET (Silent mode)" 205 | SectionIn 1 ; All 206 | 207 | DetailPrint '-----------------------------------------------' 208 | DetailPrint '${__SECTION__}' 209 | DetailPrint '-----------------------------------------------' 210 | 211 | !insertmacro STACK_VERIFY_START 212 | !define /redef LINK `https://download.mozilla.org/?product=firefox-stub&os=win&lang=en-US` 213 | !define /redef FILE "$EXEDIR\_Firefox.exe" 214 | DetailPrint 'NSxfer::Transfer "${LINK}" "${FILE}"' 215 | Push "/END" 216 | Push "Silent" 217 | Push "/MODE" 218 | Push "${FILE}" 219 | Push "/LOCAL" 220 | Push "${LINK}" 221 | Push "/URL" 222 | CallInstDLL $DLL Transfer 223 | Pop $0 224 | DetailPrint "Status: $0" 225 | !insertmacro STACK_VERIFY_END 226 | SectionEnd 227 | 228 | 229 | Section /o "HTTP GET (Memory)" 230 | SectionIn 1 ; All 231 | 232 | DetailPrint '-----------------------------------------------' 233 | DetailPrint '${__SECTION__}' 234 | DetailPrint '-----------------------------------------------' 235 | 236 | !insertmacro STACK_VERIFY_START 237 | !define /redef LINK `https://wikipedia.org` 238 | !define /redef FILE "memory" 239 | DetailPrint 'NSxfer::Transfer "${LINK}" "${FILE}"' 240 | 241 | Push "/END" 242 | Push "/RETURNID" 243 | Push "${FILE}" 244 | Push "/LOCAL" 245 | Push "${LINK}" 246 | Push "/URL" 247 | CallInstDLL $DLL Transfer 248 | Pop $0 ; Unique ID 249 | 250 | Push "/END" 251 | Push "/CONTENT" 252 | Push "/ERRORTEXT" 253 | Push $0 254 | Push "/ID" 255 | CallInstDLL $DLL Query 256 | Pop $1 ; Transfer status 257 | Pop $2 ; Remote content (trimmed to NSIS_MAX_STRLEN) 258 | 259 | StrCpy $2 $2 64 260 | StrCpy $2 "$2..." 261 | DetailPrint "Status: $1, Content: $2" 262 | !insertmacro STACK_VERIFY_END 263 | SectionEnd 264 | 265 | 266 | !define /redef COUNT 32 267 | Section /o "HTTP GET (Memory * ${COUNT}x)" 268 | SectionIn 1 ; All 269 | 270 | DetailPrint '-----------------------------------------------' 271 | DetailPrint '${__SECTION__}' 272 | DetailPrint '-----------------------------------------------' 273 | 274 | !insertmacro STACK_VERIFY_START 275 | !define /redef FILE "memory" 276 | ${For} $R0 1 ${COUNT} 277 | StrCpy $R1 "https://httpbin.org/post?index=$R0&total=${COUNT}" 278 | DetailPrint 'NSxfer::Request [$R0/${COUNT}] $R1 "${FILE}"' 279 | Push "/END" 280 | Push "POST" 281 | Push "/METHOD" 282 | Push "${FILE}" 283 | Push "/LOCAL" 284 | Push $R1 285 | Push "/URL" 286 | CallInstDLL $DLL Request 287 | Pop $0 288 | ${Next} 289 | 290 | Push "/END" 291 | Push "" ; Abort message 292 | Push "" ; Abort title 293 | Push "/ABORT" 294 | CallInstDLL $DLL Wait 295 | Pop $1 296 | 297 | !insertmacro STACK_VERIFY_END 298 | SectionEnd 299 | 300 | 301 | Section /o "HTTP GET (Parallel transfers)" 302 | SectionIn 1 ; All 303 | 304 | DetailPrint '-----------------------------------------------' 305 | DetailPrint '${__SECTION__}' 306 | DetailPrint '-----------------------------------------------' 307 | 308 | ; Request 1 309 | !insertmacro STACK_VERIFY_START 310 | !define /redef LINK `https://download.mozilla.org/?product=firefox-stub&os=win&lang=en-US` 311 | !define /redef FILE "$EXEDIR\_Firefox(2).exe" 312 | DetailPrint 'NSxfer::Request "${LINK}" "${FILE}"' 313 | Push "/END" 314 | Push "${FILE}" 315 | Push "/LOCAL" 316 | Push "${LINK}" 317 | Push "/URL" 318 | CallInstDLL $DLL Request 319 | Pop $1 320 | !insertmacro STACK_VERIFY_END 321 | 322 | ; Request 2 323 | !insertmacro STACK_VERIFY_START 324 | !define /redef LINK `https://download.mozilla.org/?product=firefox-stub&os=win&lang=en-US` 325 | !define /redef FILE "$EXEDIR\_Firefox(3).exe" 326 | DetailPrint 'NSxfer::Request "${LINK}" "${FILE}"' 327 | Push "/END" 328 | Push 15000 329 | Push "/TIMEOUTCONNECT" 330 | Push "${FILE}" 331 | Push "/LOCAL" 332 | Push "${LINK}" 333 | Push "/URL" 334 | CallInstDLL $DLL Request 335 | Pop $2 336 | !insertmacro STACK_VERIFY_END 337 | 338 | ; Request 3 339 | !insertmacro STACK_VERIFY_START 340 | !define /redef LINK `http://download.osmc.tv/installers/osmc-installer.exe` 341 | !define /redef FILE "$EXEDIR\_osmc_installer.exe" 342 | DetailPrint 'NSxfer::Request "${LINK}" "${FILE}"' 343 | Push "/END" 344 | Push 15000 345 | Push "/TIMEOUTCONNECT" 346 | Push "${FILE}" 347 | Push "/LOCAL" 348 | Push "${LINK}" 349 | Push "/URL" 350 | CallInstDLL $DLL Request 351 | Pop $3 352 | !insertmacro STACK_VERIFY_END 353 | 354 | ; Wait for all 355 | !insertmacro STACK_VERIFY_START 356 | DetailPrint 'Waiting . . .' 357 | Push "/END" 358 | Push "Are you sure?" 359 | Push "Abort" 360 | Push "/ABORT" 361 | Push "Page" 362 | Push "/Mode" 363 | CallInstDLL $DLL Wait 364 | Pop $0 365 | !insertmacro STACK_VERIFY_END 366 | 367 | ; Validate individual transfer status... 368 | ; TODO 369 | 370 | DetailPrint 'Done' 371 | SectionEnd 372 | 373 | 374 | Section /o "-HTTP GET (proxy)" 375 | SectionIn 1 ; All 376 | 377 | DetailPrint '-----------------------------------------------' 378 | DetailPrint '${__SECTION__}' 379 | DetailPrint '-----------------------------------------------' 380 | 381 | !insertmacro STACK_VERIFY_START 382 | !define /redef LINK "https://download.sysinternals.com/files/SysinternalsSuite.zip" 383 | !define /redef FILE "$EXEDIR\_SysinternalsSuiteLive_proxy.zip" 384 | !define /redef PROXY "http=54.36.139.108:8118 https=54.36.139.108:8118" ; France 385 | DetailPrint 'NSxfer::Transfer /proxy ${PROXY} "${LINK}" "${FILE}"' 386 | Push "/END" 387 | Push "Are you sure?" 388 | Push "Abort" 389 | Push "/ABORT" 390 | Push 30000 391 | Push "/TIMEOUTRECONNECT" 392 | Push 15000 393 | Push "/TIMEOUTCONNECT" 394 | Push "${PROXY}" 395 | Push "/PROXY" 396 | Push "${FILE}" 397 | Push "/LOCAL" 398 | Push "${LINK}" 399 | Push "/URL" 400 | Push 10 401 | Push "/PRIORITY" 402 | CallInstDLL $DLL Transfer 403 | Pop $0 404 | DetailPrint "Status: $0" 405 | !insertmacro STACK_VERIFY_END 406 | SectionEnd 407 | 408 | 409 | Section /o "HTTP POST (application/json)" 410 | SectionIn 1 ; All 411 | 412 | DetailPrint '-----------------------------------------------' 413 | DetailPrint '${__SECTION__}' 414 | DetailPrint '-----------------------------------------------' 415 | 416 | !insertmacro STACK_VERIFY_START 417 | !define /redef LINK 'https://httpbin.org/post?param1=1¶m2=2' 418 | !define /redef FILE '$EXEDIR\_Post_json.json' 419 | DetailPrint 'NSxfer::Transfer "${LINK}" "${FILE}"' 420 | Push "/END" 421 | Push "https://wikipedia.org" 422 | Push "/REFERER" 423 | Push 30000 424 | Push "/TIMEOUTRECONNECT" 425 | Push 15000 426 | Push "/TIMEOUTCONNECT" 427 | Push "Content-Type: application/json" 428 | Push "/HEADERS" 429 | Push '{"number_of_the_beast" : 666}' 430 | Push "/DATA" 431 | Push "${FILE}" 432 | Push "/LOCAL" 433 | Push "${LINK}" 434 | Push "/URL" 435 | Push "post" 436 | Push "/METHOD" 437 | CallInstDLL $DLL Transfer 438 | Pop $0 439 | DetailPrint "Status: $0" 440 | !insertmacro STACK_VERIFY_END 441 | SectionEnd 442 | 443 | 444 | Section /o "HTTP POST (application/x-www-form-urlencoded)" 445 | SectionIn 1 ; All 446 | 447 | DetailPrint '-----------------------------------------------' 448 | DetailPrint '${__SECTION__}' 449 | DetailPrint '-----------------------------------------------' 450 | 451 | !insertmacro STACK_VERIFY_START 452 | !define /redef LINK 'http://httpbin.org/post?param1=1¶m2=2' 453 | !define /redef FILE '$EXEDIR\_Post_form.json' 454 | DetailPrint 'NSxfer::Transfer "${LINK}" "${FILE}"' 455 | Push "/END" 456 | Push "https://wikipedia.org" 457 | Push "/REFERER" 458 | Push 30000 459 | Push "/TIMEOUTRECONNECT" 460 | Push 15000 461 | Push "/TIMEOUTCONNECT" 462 | Push "Content-Type: application/x-www-form-urlencoded$\r$\nContent-Dummy: Dummy" 463 | Push "/HEADERS" 464 | Push 'User=My+User&Pass=My+Pass' 465 | Push "/DATA" 466 | Push "${FILE}" 467 | Push "/LOCAL" 468 | Push "${LINK}" 469 | Push "/URL" 470 | Push "POST" 471 | Push "/METHOD" 472 | CallInstDLL $DLL Transfer 473 | Pop $0 474 | DetailPrint "Status: $0" 475 | !insertmacro STACK_VERIFY_END 476 | SectionEnd 477 | 478 | 479 | !macro TEST_DEPENDENCY_REQUEST _Filename _DependsOn 480 | !define /redef LINK `http://httpbin.org/post` 481 | DetailPrint 'NSxfer::Request "${LINK}" "${_Filename}.txt"' 482 | Push "/END" 483 | Push "https://wikipedia.org" 484 | Push "/REFERER" 485 | Push 30000 486 | Push "/TIMEOUTRECONNECT" 487 | Push 15000 488 | Push "/TIMEOUTCONNECT" 489 | Push "Content-Type: application/x-www-form-urlencoded$\r$\nContent-Test: TEST" 490 | Push "/HEADERS" 491 | Push "User=My+User+Name&Pass=My+Password" 492 | Push "/DATA" 493 | Push "$EXEDIR\${_Filename}.txt" 494 | Push "/LOCAL" 495 | Push "${LINK}" 496 | Push "/URL" 497 | Push "POST" 498 | Push "/METHOD" 499 | Push ${_DependsOn} 500 | Push "/DEPEND" 501 | Push 2000 502 | Push "/PRIORITY" 503 | CallInstDLL $DLL Request 504 | Pop $0 ; Request ID 505 | !macroend 506 | 507 | 508 | Section /o "Test Dependencies (depend on first request)" 509 | ;SectionIn 1 ; All 510 | !insertmacro STACK_VERIFY_START 511 | 512 | StrCpy $R0 0 ; First request ID 513 | StrCpy $R1 0 ; Last request ID 514 | 515 | ; First request 516 | !insertmacro TEST_DEPENDENCY_REQUEST "_DependOnFirst1" -1 517 | StrCpy $R0 $0 ; Remember the first ID 518 | StrCpy $R1 $0 ; Remember the last request ID 519 | 520 | ; Subsequent requests 521 | ${For} $1 2 20 522 | !insertmacro TEST_DEPENDENCY_REQUEST "_DependOnFirst$1" $R0 523 | StrCpy $R1 $0 ; Remember the last request ID 524 | ${Next} 525 | 526 | ; Sleep 527 | ;Sleep 2000 528 | 529 | ; Unlock the first request, and consequently all the others... 530 | Push "/END" 531 | Push 0 ; No dependency 532 | Push "/SETDEPEND" 533 | Push $R0 ; First request ID 534 | Push "/ID" 535 | CallInstDLL $DLL "Set" 536 | Pop $0 ; Error code. Ignored 537 | 538 | ; Wait 539 | DetailPrint 'Waiting . . .' 540 | Push "/END" 541 | Push "Are you sure?" 542 | Push "Abort" 543 | Push "/ABORT" 544 | Push "Page" 545 | Push "/Mode" 546 | CallInstDLL $DLL Wait 547 | Pop $0 548 | 549 | !insertmacro STACK_VERIFY_END 550 | SectionEnd 551 | 552 | 553 | Section /o "Test Dependencies (depend on previous request)" 554 | ;SectionIn 1 ; All 555 | !insertmacro STACK_VERIFY_START 556 | 557 | StrCpy $R0 0 ; First request ID 558 | StrCpy $R1 0 ; Last request ID 559 | 560 | ; First request 561 | !insertmacro TEST_DEPENDENCY_REQUEST "_DependOnPrevious1" -1 562 | StrCpy $R0 $0 ; Remember the first ID 563 | StrCpy $R1 $0 ; Remember the last request ID 564 | 565 | ; Subsequent requests 566 | ${For} $1 2 20 567 | !insertmacro TEST_DEPENDENCY_REQUEST "_DependOnPrevious$1" $R1 568 | StrCpy $R1 $0 ; Remember the last request ID 569 | ${Next} 570 | 571 | ; Sleep 572 | ;Sleep 2000 573 | 574 | ; Unlock the first request, and consequently all the others... 575 | Push "/END" 576 | Push 0 ; No dependency 577 | Push "/SETDEPEND" 578 | Push $R0 ; First request ID 579 | Push "/ID" 580 | CallInstDLL $DLL "Set" 581 | Pop $0 ; Error code. Ignored 582 | 583 | ; Wait 584 | DetailPrint 'Waiting . . .' 585 | 586 | Push "/END" 587 | Push "Are you sure?" 588 | Push "Abort" 589 | Push "/ABORT" 590 | ;Push $HWNDPARENT 591 | ;Push "/TITLEHWND" 592 | Push "Page" 593 | Push "/MODE" 594 | CallInstDLL $DLL Wait 595 | Pop $0 596 | 597 | !insertmacro STACK_VERIFY_END 598 | SectionEnd 599 | 600 | 601 | ; Input: [Stack] Request ID 602 | ; Output: None 603 | Function PrintRequest 604 | Exch $R1 ; Request ID 605 | Push $0 606 | Push $1 607 | Push $2 608 | 609 | Push "/END" 610 | Push "/CONTENT" 611 | Push "/CONNECTIONDROPS" 612 | Push "/ERRORTEXT" 613 | Push "/ERRORCODE" 614 | Push "/TIMEDOWNLOADING" 615 | Push "/TIMEWAITING" 616 | Push "/SPEED" 617 | Push "/SPEEDBYTES" 618 | Push "/PERCENT" 619 | Push "/FILESIZE" 620 | Push "/RECVSIZE" 621 | Push "/RECVHEADERS" 622 | Push "/SENTHEADERS" 623 | Push "/DATA" 624 | Push "/LOCAL" 625 | Push "/IP" 626 | Push "/PROXY" 627 | Push "/URL" 628 | Push "/METHOD" 629 | Push "/WININETSTATUS" 630 | Push "/STATUS" 631 | Push "/DEPEND" 632 | Push "/PRIORITY" 633 | Push $R1 ; Request ID 634 | Push "/ID" 635 | CallInstDLL $DLL Query 636 | 637 | StrCpy $0 "[>] ID:$R1" 638 | Pop $1 ;PRIORITY 639 | StrCpy $0 "$0, Prio:$1" 640 | Pop $1 ;DEPEND 641 | IntCmp $1 0 +2 +1 +1 642 | StrCpy $0 "$0, DependsOn:$1" 643 | Pop $1 ;STATUS 644 | StrCpy $0 "$0, [$1]" 645 | Pop $1 ;WININETSTATUS 646 | StrCpy $0 "$0, WinINet:$1" 647 | DetailPrint $0 648 | 649 | StrCpy $0 " [Request]" 650 | Pop $1 ;METHOD 651 | StrCpy $0 "$0 $1" 652 | Pop $1 ;URL 653 | StrCpy $0 "$0 $1" 654 | DetailPrint $0 655 | 656 | Pop $1 ;PROXY 657 | StrCmp $1 "" +2 +1 658 | DetailPrint " [Proxy] $1" 659 | Pop $1 ;IP 660 | StrCmp $1 "" +2 +1 661 | DetailPrint " [Server] $1" 662 | 663 | Pop $1 ;LOCAL 664 | DetailPrint " [Local] $1" 665 | 666 | Pop $1 ;DATA 667 | ${If} $1 != "" 668 | ${StrRep} $1 "$1" "$\r" "\r" 669 | ${StrRep} $1 "$1" "$\n" "\n" 670 | DetailPrint " [Sent Data] $1" 671 | ${EndIf} 672 | Pop $1 ;SENTHEADERS 673 | ${If} $1 != "" 674 | DetailPrint " [Sent Headers]" 675 | ${For} $2 0 100 676 | ${StrTok} $0 $1 "$\r$\n" $2 1 677 | ${If} $0 == "" 678 | ${Break} 679 | ${EndIf} 680 | DetailPrint " $0" 681 | ${Next} 682 | ${EndIf} 683 | Pop $1 ;RECVHEADERS 684 | ${If} $1 != "" 685 | DetailPrint " [Recv Headers]" 686 | ${For} $2 0 100 687 | ${StrTok} $0 $1 "$\r$\n" $2 1 688 | ${If} $0 == "" 689 | ${Break} 690 | ${EndIf} 691 | DetailPrint " $0" 692 | ${Next} 693 | ${EndIf} 694 | 695 | StrCpy $0 " [Size]" 696 | Pop $1 ;RECVSIZE 697 | StrCpy $0 "$0 $1" 698 | Pop $1 ;FILESIZE 699 | StrCpy $0 "$0/$1" 700 | Pop $1 ;PERCENT 701 | StrCpy $0 "$0 ($1%)" 702 | Pop $1 ;SPEEDBYTES 703 | Pop $1 ;SPEED 704 | StrCmp $1 "" +2 +1 705 | StrCpy $0 "$0 @ $1" 706 | DetailPrint "$0" 707 | 708 | StrCpy $0 " [Time]" 709 | Pop $1 ;TIMEWAITING 710 | StrCpy $0 "$0 Waiting $1ms" 711 | Pop $1 ;TIMEDOWNLOADING 712 | StrCpy $0 "$0, Downloading $1ms" 713 | DetailPrint "$0" 714 | 715 | StrCpy $0 " [Error]" 716 | Pop $1 ;ERRORCODE 717 | StrCpy $0 "$0 $1" 718 | Pop $1 ;ERRORTEXT 719 | StrCpy $0 "$0, $1" 720 | Pop $1 ;CONNECTIONDROPS 721 | IntCmp $1 0 +2 +1 +1 722 | StrCpy $0 "$0, Drops:$1" 723 | DetailPrint "$0" 724 | 725 | Pop $1 ;CONTENT 726 | ${If} $1 != "" 727 | DetailPrint " [Content]" 728 | ${For} $2 0 100 729 | ${StrTok} $0 $1 "$\r$\n" $2 1 730 | ${If} $0 == "" 731 | ${Break} 732 | ${EndIf} 733 | DetailPrint " $0" 734 | ${Next} 735 | ${EndIf} 736 | 737 | Pop $2 738 | Pop $1 739 | Pop $0 740 | Pop $R1 741 | FunctionEnd 742 | 743 | 744 | Function PrintSummary 745 | 746 | !insertmacro STACK_VERIFY_START 747 | Push $0 748 | Push $1 749 | Push $2 750 | Push $3 751 | Push $R0 752 | Push $R1 753 | Push $R2 754 | Push $R3 755 | Push $R4 756 | 757 | ; Enumerate all transfers (completed + pending + waiting) 758 | DetailPrint "NSxfer::Enumerate" 759 | Push "/END" 760 | CallInstDLL $DLL Enumerate 761 | Pop $1 ; Count 762 | DetailPrint " $1 requests" 763 | ${For} $0 1 $1 764 | Call PrintRequest 765 | ${Next} 766 | 767 | Push "/END" 768 | Push "/USERAGENT" 769 | Push "/PLUGINVERSION" 770 | Push "/PLUGINNAME" 771 | Push "/TOTALTHREADS" 772 | Push "/TOTALSPEED" 773 | Push "/TOTALDOWNLOADING" 774 | Push "/TOTALCOMPLETED" 775 | Push "/TOTALCOUNT" 776 | CallInstDLL $DLL QueryGlobal 777 | Pop $R0 ; Total 778 | Pop $R1 ; Completed 779 | Pop $R2 ; Downloading 780 | Pop $R3 ; Speed 781 | Pop $R4 ; Worker threads 782 | Pop $1 ; Plugin Name 783 | Pop $2 ; Plugin Version 784 | Pop $3 ; Useragent 785 | 786 | DetailPrint "Transferring $R1+$R2/$R0 items at $R3 using $R4 worker threads" 787 | DetailPrint "[>] $1 $2" 788 | DetailPrint "[>] User agent: $3" 789 | 790 | Pop $R4 791 | Pop $R3 792 | Pop $R2 793 | Pop $R1 794 | Pop $R0 795 | Pop $3 796 | Pop $2 797 | Pop $1 798 | Pop $0 799 | !insertmacro STACK_VERIFY_END 800 | 801 | FunctionEnd 802 | 803 | 804 | Section -Summary 805 | DetailPrint '-----------------------------------------------' 806 | DetailPrint ' ${__SECTION__}' 807 | DetailPrint '-----------------------------------------------' 808 | Call PrintSummary 809 | SectionEnd 810 | -------------------------------------------------------------------------------- /TestDebug/build64.bat: -------------------------------------------------------------------------------- 1 | @echo off 2 | setlocal enabledelayedexpansion 3 | 4 | if not exist "%NSIS%\makensis.exe" pushd "%~dp0..\.." && set NSIS=!cd!&& popd 5 | if not exist "%NSIS%\makensis.exe" set NSIS=%PROGRAMFILES%\NSIS 6 | if not exist "%NSIS%\makensis.exe" set NSIS=%PROGRAMFILES(X86)%\NSIS 7 | if not exist "%NSIS%\makensis.exe" for /f "delims=*" %%f in ('where makensis.exe 2^> nul') do pushd "%%~dpf" && set NSIS=!cd!&& popd 8 | if not exist "%NSIS%\makensis.exe" echo ERROR: NSIS not found&& pause && exit /b 2 9 | 10 | echo ******************************************************************************** 11 | echo %NSIS%\makensis.exe 12 | echo ******************************************************************************** 13 | 14 | "%NSIS%\makensis.exe" /V4 /DAMD64 "%~dp0\NSxfer-Debug.nsi" || pause && exit /b !errorlevel! 15 | -------------------------------------------------------------------------------- /TestDebug/buildA.bat: -------------------------------------------------------------------------------- 1 | @echo off 2 | setlocal enabledelayedexpansion 3 | 4 | if not exist "%NSIS%\makensis.exe" pushd "%~dp0..\.." && set NSIS=!cd!&& popd 5 | if not exist "%NSIS%\makensis.exe" set NSIS=%PROGRAMFILES%\NSIS 6 | if not exist "%NSIS%\makensis.exe" set NSIS=%PROGRAMFILES(X86)%\NSIS 7 | if not exist "%NSIS%\makensis.exe" for /f "delims=*" %%f in ('where makensis.exe 2^> nul') do pushd "%%~dpf" && set NSIS=!cd!&& popd 8 | if not exist "%NSIS%\makensis.exe" echo ERROR: NSIS not found&& pause && exit /b 2 9 | 10 | echo ******************************************************************************** 11 | echo %NSIS%\makensis.exe 12 | echo ******************************************************************************** 13 | 14 | "%NSIS%\makensis.exe" /V4 /DANSI "%~dp0\NSxfer-Debug.nsi" || pause && exit /b !errorlevel! 15 | -------------------------------------------------------------------------------- /TestDebug/buildW.bat: -------------------------------------------------------------------------------- 1 | @echo off 2 | setlocal enabledelayedexpansion 3 | 4 | if not exist "%NSIS%\makensis.exe" pushd "%~dp0..\.." && set NSIS=!cd!&& popd 5 | if not exist "%NSIS%\makensis.exe" set NSIS=%PROGRAMFILES%\NSIS 6 | if not exist "%NSIS%\makensis.exe" set NSIS=%PROGRAMFILES(X86)%\NSIS 7 | if not exist "%NSIS%\makensis.exe" for /f "delims=*" %%f in ('where makensis.exe 2^> nul') do pushd "%%~dpf" && set NSIS=!cd!&& popd 8 | if not exist "%NSIS%\makensis.exe" echo ERROR: NSIS not found&& pause && exit /b 2 9 | 10 | echo ******************************************************************************** 11 | echo %NSIS%\makensis.exe 12 | echo ******************************************************************************** 13 | 14 | "%NSIS%\makensis.exe" /V4 "%~dp0\NSxfer-Debug.nsi" || pause && exit /b !errorlevel! 15 | -------------------------------------------------------------------------------- /TestDebug/cleanup.bat: -------------------------------------------------------------------------------- 1 | @del "%~dp0\*.exe" 2 | @del "%~dp0\_*.*" 3 | -------------------------------------------------------------------------------- /_build_Debug.cmd: -------------------------------------------------------------------------------- 1 | REM :: Marius Negrutiu (marius.negrutiu@protonmail.com) 2 | 3 | @echo off 4 | setlocal 5 | echo. 6 | 7 | :CHDIR 8 | cd /d "%~dp0" 9 | 10 | :DEFINITIONS 11 | for /f "delims=*" %%i in ('dir /B /OD *.sln 2^> nul') do set BUILD_SOLUTION=%%~fi 12 | set BUILD_CONFIG=Debug 13 | set BUILD_VERBOSITY=normal 14 | 15 | :COMPILER 16 | if not exist "%PF%" set PF=%PROGRAMFILES(X86)% 17 | if not exist "%PF%" set PF=%PROGRAMFILES% 18 | set VSWHERE=%PF%\Microsoft Visual Studio\Installer\vswhere.exe 19 | if not exist "%VCVARSALL%" for /f "tokens=1* delims=: " %%i in ('"%VSWHERE%" -version 17 -requires Microsoft.Component.MSBuild 2^> NUL') do if /i "%%i"=="installationPath" set VCVARSALL=%%j\VC\Auxiliary\Build\VCVarsAll.bat&& set BUILD_PLATFORMTOOLSET=v143 20 | if not exist "%VCVARSALL%" for /f "tokens=1* delims=: " %%i in ('"%VSWHERE%" -version 16 -requires Microsoft.Component.MSBuild 2^> NUL') do if /i "%%i"=="installationPath" set VCVARSALL=%%j\VC\Auxiliary\Build\VCVarsAll.bat&& set BUILD_PLATFORMTOOLSET=v142 21 | if not exist "%VCVARSALL%" for /f "tokens=1* delims=: " %%i in ('"%VSWHERE%" -version 15 -requires Microsoft.Component.MSBuild 2^> NUL') do if /i "%%i"=="installationPath" set VCVARSALL=%%j\VC\Auxiliary\Build\VCVarsAll.bat&& set BUILD_PLATFORMTOOLSET=v141 22 | if not exist "%VCVARSALL%" set VCVARSALL=%PF%\Microsoft Visual Studio 14.0\VC\VcVarsAll.bat&& set BUILD_PLATFORMTOOLSET=v140 23 | if not exist "%VCVARSALL%" set VCVARSALL=%PF%\Microsoft Visual Studio 12.0\VC\VcVarsAll.bat&& set BUILD_PLATFORMTOOLSET=v120 24 | if not exist "%VCVARSALL%" set VCVARSALL=%PF%\Microsoft Visual Studio 11.0\VC\VcVarsAll.bat&& set BUILD_PLATFORMTOOLSET=v110 25 | if not exist "%VCVARSALL%" set VCVARSALL=%PF%\Microsoft Visual Studio 10.0\VC\VcVarsAll.bat&& set BUILD_PLATFORMTOOLSET=v100 26 | if not exist "%VCVARSALL%" echo ERROR: Can't find Visual Studio 2010-2022 && pause && exit /b 2 27 | 28 | :BUILD 29 | pushd "%CD%" 30 | call "%VCVARSALL%" x86 31 | popd 32 | 33 | title %BUILD_CONFIG%-x86-ansi&& msbuild /m /t:build "%BUILD_SOLUTION%" /p:Configuration=%BUILD_CONFIG%-x86-ansi /p:Platform=Win32 /p:PlatformToolset=%BUILD_PLATFORMTOOLSET% /p:WindowsTargetPlatformVersion=%WindowsSDKVersion% /nologo /verbosity:%BUILD_VERBOSITY% || pause && exit /b !errorlevel! 34 | title %BUILD_CONFIG%-x86-unicode&& msbuild /m /t:build "%BUILD_SOLUTION%" /p:Configuration=%BUILD_CONFIG%-x86-unicode /p:Platform=Win32 /p:PlatformToolset=%BUILD_PLATFORMTOOLSET% /p:WindowsTargetPlatformVersion=%WindowsSDKVersion% /nologo /verbosity:%BUILD_VERBOSITY% || pause && exit /b !errorlevel! 35 | -------------------------------------------------------------------------------- /_build_Release.cmd: -------------------------------------------------------------------------------- 1 | REM :: Marius Negrutiu (marius.negrutiu@protonmail.com) 2 | 3 | @echo off 4 | setlocal 5 | echo. 6 | 7 | :CHDIR 8 | cd /d "%~dp0" 9 | 10 | :DEFINITIONS 11 | for /f "delims=*" %%i in ('dir /B /OD *.sln 2^> nul') do set BUILD_SOLUTION=%%~fi 12 | set BUILD_CONFIG=Release 13 | set BUILD_VERBOSITY=normal 14 | 15 | :COMPILER 16 | if not exist "%PF%" set PF=%PROGRAMFILES(X86)% 17 | if not exist "%PF%" set PF=%PROGRAMFILES% 18 | set VSWHERE=%PF%\Microsoft Visual Studio\Installer\vswhere.exe 19 | if not exist "%VCVARSALL%" for /f "tokens=1* delims=: " %%i in ('"%VSWHERE%" -version 17 -requires Microsoft.Component.MSBuild 2^> NUL') do if /i "%%i"=="installationPath" set VCVARSALL=%%j\VC\Auxiliary\Build\VCVarsAll.bat&& set BUILD_PLATFORMTOOLSET=v143 20 | if not exist "%VCVARSALL%" for /f "tokens=1* delims=: " %%i in ('"%VSWHERE%" -version 16 -requires Microsoft.Component.MSBuild 2^> NUL') do if /i "%%i"=="installationPath" set VCVARSALL=%%j\VC\Auxiliary\Build\VCVarsAll.bat&& set BUILD_PLATFORMTOOLSET=v142 21 | if not exist "%VCVARSALL%" for /f "tokens=1* delims=: " %%i in ('"%VSWHERE%" -version 15 -requires Microsoft.Component.MSBuild 2^> NUL') do if /i "%%i"=="installationPath" set VCVARSALL=%%j\VC\Auxiliary\Build\VCVarsAll.bat&& set BUILD_PLATFORMTOOLSET=v141 22 | if not exist "%VCVARSALL%" set VCVARSALL=%PF%\Microsoft Visual Studio 14.0\VC\VcVarsAll.bat&& set BUILD_PLATFORMTOOLSET=v140 23 | if not exist "%VCVARSALL%" set VCVARSALL=%PF%\Microsoft Visual Studio 12.0\VC\VcVarsAll.bat&& set BUILD_PLATFORMTOOLSET=v120 24 | if not exist "%VCVARSALL%" set VCVARSALL=%PF%\Microsoft Visual Studio 11.0\VC\VcVarsAll.bat&& set BUILD_PLATFORMTOOLSET=v110 25 | if not exist "%VCVARSALL%" set VCVARSALL=%PF%\Microsoft Visual Studio 10.0\VC\VcVarsAll.bat&& set BUILD_PLATFORMTOOLSET=v100 26 | if not exist "%VCVARSALL%" echo ERROR: Can't find Visual Studio 2010-2022 && pause && exit /b 2 27 | 28 | :BUILD 29 | pushd "%CD%" 30 | call "%VCVARSALL%" x86 31 | popd 32 | 33 | title %BUILD_CONFIG%-nocrt-x86-ansi&& msbuild /m /t:build "%BUILD_SOLUTION%" /p:Configuration=%BUILD_CONFIG%-nocrt-x86-ansi /p:Platform=Win32 /p:PlatformToolset=%BUILD_PLATFORMTOOLSET% /p:WindowsTargetPlatformVersion=%WindowsSDKVersion% /nologo /verbosity:%BUILD_VERBOSITY% || pause && exit /b !errorlevel! 34 | title %BUILD_CONFIG%-nocrt-x86-unicode&& msbuild /m /t:build "%BUILD_SOLUTION%" /p:Configuration=%BUILD_CONFIG%-nocrt-x86-unicode /p:Platform=Win32 /p:PlatformToolset=%BUILD_PLATFORMTOOLSET% /p:WindowsTargetPlatformVersion=%WindowsSDKVersion% /nologo /verbosity:%BUILD_VERBOSITY% || pause && exit /b !errorlevel! 35 | title %BUILD_CONFIG%-staticcrt-x86-ansi&& msbuild /m /t:build "%BUILD_SOLUTION%" /p:Configuration=%BUILD_CONFIG%-staticcrt-x86-ansi /p:Platform=Win32 /p:PlatformToolset=%BUILD_PLATFORMTOOLSET% /p:WindowsTargetPlatformVersion=%WindowsSDKVersion% /nologo /verbosity:%BUILD_VERBOSITY% || pause && exit /b !errorlevel! 36 | title %BUILD_CONFIG%-staticcrt-x86-unicode&& msbuild /m /t:build "%BUILD_SOLUTION%" /p:Configuration=%BUILD_CONFIG%-staticcrt-x86-unicode /p:Platform=Win32 /p:PlatformToolset=%BUILD_PLATFORMTOOLSET% /p:WindowsTargetPlatformVersion=%WindowsSDKVersion% /nologo /verbosity:%BUILD_VERBOSITY% || pause && exit /b !errorlevel! 37 | -------------------------------------------------------------------------------- /_build_Release_CL.cmd: -------------------------------------------------------------------------------- 1 | REM :: Marius Negrutiu (marius.negrutiu@protonmail.com) 2 | 3 | @echo off 4 | setlocal 5 | echo. 6 | 7 | :: This script builds the project by directly calling cl.exe 8 | :: The sln/vcxproj files are ignored 9 | 10 | :CHDIR 11 | cd /d "%~dp0" 12 | 13 | :DEFINITIONS 14 | for /f "delims=*" %%i in ('dir /B /OD *.sln 2^> nul') do set BUILD_SOLUTION=%%~fi 15 | if "%BUILD_CONFIG%" equ "" set BUILD_CONFIG=%~1 16 | if "%BUILD_CONFIG%" equ "" set BUILD_CONFIG=Release 17 | set BUILD_VERBOSITY=normal 18 | 19 | :COMPILER 20 | if not exist "%PF%" set PF=%PROGRAMFILES(X86)% 21 | if not exist "%PF%" set PF=%PROGRAMFILES% 22 | set VSWHERE=%PF%\Microsoft Visual Studio\Installer\vswhere.exe 23 | if not exist "%VCVARSALL%" for /f "tokens=1* delims=: " %%i in ('"%VSWHERE%" -version 17 -requires Microsoft.Component.MSBuild 2^> NUL') do if /i "%%i"=="installationPath" set VCVARSALL=%%j\VC\Auxiliary\Build\VCVarsAll.bat&& set BUILD_PLATFORMTOOLSET=v143 24 | if not exist "%VCVARSALL%" for /f "tokens=1* delims=: " %%i in ('"%VSWHERE%" -version 16 -requires Microsoft.Component.MSBuild 2^> NUL') do if /i "%%i"=="installationPath" set VCVARSALL=%%j\VC\Auxiliary\Build\VCVarsAll.bat&& set BUILD_PLATFORMTOOLSET=v142 25 | if not exist "%VCVARSALL%" for /f "tokens=1* delims=: " %%i in ('"%VSWHERE%" -version 15 -requires Microsoft.Component.MSBuild 2^> NUL') do if /i "%%i"=="installationPath" set VCVARSALL=%%j\VC\Auxiliary\Build\VCVarsAll.bat&& set BUILD_PLATFORMTOOLSET=v141 26 | if not exist "%VCVARSALL%" set VCVARSALL=%PF%\Microsoft Visual Studio 14.0\VC\VcVarsAll.bat&& set BUILD_PLATFORMTOOLSET=v140 27 | if not exist "%VCVARSALL%" set VCVARSALL=%PF%\Microsoft Visual Studio 12.0\VC\VcVarsAll.bat&& set BUILD_PLATFORMTOOLSET=v120 28 | if not exist "%VCVARSALL%" set VCVARSALL=%PF%\Microsoft Visual Studio 11.0\VC\VcVarsAll.bat&& set BUILD_PLATFORMTOOLSET=v110 29 | if not exist "%VCVARSALL%" set VCVARSALL=%PF%\Microsoft Visual Studio 10.0\VC\VcVarsAll.bat&& set BUILD_PLATFORMTOOLSET=v100 30 | if not exist "%VCVARSALL%" echo ERROR: Can't find Visual Studio 2010-2022 && pause && exit /b 2 31 | 32 | :pluginapi 33 | call py -3 _get_nsis_sdk.py || exit /b !errorlevel! 34 | 35 | :environment 36 | set OUTNAME=NSxfer 37 | set RCNAME=resource 38 | 39 | :BUILD 40 | pushd "%CD%" 41 | call "%VCVARSALL%" x86 42 | popd 43 | 44 | echo ----------------------------------- 45 | set OUTDIR=Release-cl-x86-ansi 46 | echo %OUTDIR% 47 | echo ----------------------------------- 48 | set BUILD_MACHINE=X86 49 | call :BUILD_PARAMS 50 | set CL=/D "_MBCS" /arch:SSE %CL% 51 | set LINK=/MACHINE:X86 /SAFESEH %LINK% 52 | call :BUILD_CL 53 | if %errorlevel% neq 0 pause && exit /b %errorlevel% 54 | 55 | echo ----------------------------------- 56 | set OUTDIR=Release-cl-x86-unicode 57 | echo %OUTDIR% 58 | echo ----------------------------------- 59 | call :BUILD_PARAMS 60 | set CL=/D "_UNICODE" /D "UNICODE" /arch:SSE %CL% 61 | set LINK=/MACHINE:X86 /SAFESEH %LINK% 62 | call :BUILD_CL 63 | if %errorlevel% neq 0 pause && exit /b %errorlevel% 64 | 65 | :BUILD64 66 | pushd "%CD%" 67 | call "%VCVARSALL%" amd64 68 | popd 69 | 70 | echo ----------------------------------- 71 | set OUTDIR=Release-cl-amd64-unicode 72 | echo %OUTDIR% 73 | echo ----------------------------------- 74 | call :BUILD_PARAMS 75 | set CL=/D "_UNICODE" /D "UNICODE" %CL% 76 | set LINK=/MACHINE:AMD64 %LINK% 77 | call :BUILD_CL 78 | if %errorlevel% neq 0 pause && exit /b %errorlevel% 79 | 80 | :: Finish 81 | exit /b 0 82 | 83 | 84 | :BUILD_PARAMS 85 | set CL=^ 86 | /nologo ^ 87 | /Zi ^ 88 | /W3 /WX- ^ 89 | /O2 /Os /Oy- ^ 90 | /D WIN32 /D NDEBUG /D _WINDOWS /D _USRDLL /D _WINDLL ^ 91 | /Gm- /EHsc /MT /GS- /Gd /TC /GF /FD /LD ^ 92 | /Fo".\%OUTDIR%\temp\\" ^ 93 | /Fd".\%OUTDIR%\temp\\" ^ 94 | /Fe".\%OUTDIR%\%OUTNAME%" ^ 95 | /I. 96 | 97 | set LINK=^ 98 | /NOLOGO ^ 99 | /NODEFAULTLIB ^ 100 | /DYNAMICBASE /NXCOMPAT ^ 101 | /DEBUG ^ 102 | /OPT:REF ^ 103 | /OPT:ICF ^ 104 | /INCREMENTAL:NO ^ 105 | /MANIFEST:NO ^ 106 | /ENTRY:"DllMain" ^ 107 | kernel32.lib advapi32.lib ole32.lib uuid.lib user32.lib shlwapi.lib version.lib wininet.lib ^ 108 | ".\%OUTDIR%\temp\%RCNAME%.res" 109 | 110 | set FILES=^ 111 | "main.c" ^ 112 | "gui.c" ^ 113 | "queue.c" ^ 114 | "thread.c" ^ 115 | "utils.c" ^ 116 | "nsis\pluginapi.c" 117 | 118 | exit /b 0 119 | 120 | 121 | :BUILD_CL 122 | title %OUTDIR% 123 | echo. 124 | if not exist "%~dp0\%OUTDIR%" mkdir "%~dp0\%OUTDIR%" 125 | if not exist "%~dp0\%OUTDIR%\temp" mkdir "%~dp0\%OUTDIR%\temp" 126 | 127 | echo %RCNAME%.rc 128 | rc.exe /l"0x0409" /Fo".\%OUTDIR%\temp\%RCNAME%.res" "%RCNAME%.rc" 129 | if %errorlevel% neq 0 exit /b %errorlevel% 130 | 131 | cl.exe %FILES% 132 | if %errorlevel% neq 0 exit /b %errorlevel% 133 | 134 | exit /b 0 135 | -------------------------------------------------------------------------------- /_build_Release_mingw.bat: -------------------------------------------------------------------------------- 1 | REM :: Marius Negrutiu (marius.negrutiu@protonmail.com) 2 | 3 | @echo off 4 | echo. 5 | 6 | if not exist "%MINGW32%\bin\gcc.exe" set MINGW32=%MINGW32_INSTDIR% 7 | if not exist "%MINGW32%\bin\gcc.exe" set MINGW32=%SYSTEMROOT%\msys64\mingw32 8 | if not exist "%MINGW32%\bin\gcc.exe" set MINGW32=%SYSTEMROOT%\msys2\mingw32 9 | 10 | if not exist "%MINGW64%\bin\gcc.exe" set MINGW64=%MINGW64_INSTDIR% 11 | if not exist "%MINGW64%\bin\gcc.exe" set MINGW64=%SYSTEMROOT%\msys64\mingw64 12 | if not exist "%MINGW64%\bin\gcc.exe" set MINGW64=%SYSTEMROOT%\msys2\mingw64 13 | 14 | set ORIGINAL_PATH=%PATH% 15 | 16 | cd /d "%~dp0" 17 | 18 | :x86 19 | if not exist "%MINGW32%\bin\gcc.exe" echo ERROR: Missing "%MINGW32%" && pause && exit /b 2 20 | set PATH=%MINGW32%\bin;%ORIGINAL_PATH% 21 | 22 | echo. 23 | echo ------------------------------------------------------------------- 24 | set OUTDIR=Release-mingw-x86-ansi 25 | echo %OUTDIR% 26 | title %OUTDIR% 27 | echo ------------------------------------------------------------------- 28 | mingw32-make.exe ARCH=X86 CHAR=ANSI OUTDIR=%OUTDIR% -fMakefile.mingw clean all 29 | if %errorlevel% neq 0 pause && exit /b %errorlevel% 30 | 31 | echo. 32 | echo ------------------------------------------------------------------- 33 | set OUTDIR=Release-mingw-x86-unicode 34 | echo %OUTDIR% 35 | title %OUTDIR% 36 | echo ------------------------------------------------------------------- 37 | mingw32-make.exe ARCH=X86 CHAR=Unicode OUTDIR=%OUTDIR% -fMakefile.mingw clean all 38 | if %errorlevel% neq 0 pause && exit /b %errorlevel% 39 | 40 | 41 | :amd64 42 | if not exist "%MINGW64%\bin\gcc.exe" echo ERROR: Missing "%MINGW64%" && pause && exit /b 2 43 | set PATH=%MINGW64%\bin;%ORIGINAL_PATH% 44 | 45 | echo. 46 | echo ------------------------------------------------------------------- 47 | set OUTDIR=Release-mingw-amd64-unicode 48 | echo %OUTDIR% 49 | title %OUTDIR% 50 | echo ------------------------------------------------------------------- 51 | mingw32-make.exe ARCH=X64 CHAR=Unicode OUTDIR=%OUTDIR% -fMakefile.mingw clean all 52 | if %errorlevel% neq 0 pause && exit /b %errorlevel% 53 | 54 | echo. 55 | REM pause 56 | -------------------------------------------------------------------------------- /_cleanup.bat: -------------------------------------------------------------------------------- 1 | REM :: Marius Negrutiu (marius.negrutiu@protonmail.com) 2 | 3 | @echo off 4 | echo. 5 | 6 | cd /d "%~dp0" 7 | 8 | call "%CD%\Test\cleanup.bat" 9 | call "%CD%\TestDebug\cleanup.bat" 10 | call :CLEANUP 11 | call :CLEANUP 12 | call :CLEANUP 13 | goto :EOF 14 | 15 | 16 | :CLEANUP 17 | rd /S /Q .vs 18 | rd /S /Q ipch 19 | 20 | for /D %%a in (Debug*) do rd /S /Q "%%a" 21 | for /D %%a in (Release*) do rd /S /Q "%%a" 22 | rd /S /Q nsis 23 | 24 | del *.aps 25 | del *.bak 26 | ::del *.user 27 | del *.ncb 28 | del /AH *.suo 29 | del *.sdf 30 | del *.VC.db 31 | -------------------------------------------------------------------------------- /_get_nsis_sdk.py: -------------------------------------------------------------------------------- 1 | # Download NSIS SDK files from GitHub 2 | 3 | from pathlib import Path 4 | from urllib import request 5 | 6 | cd = Path(__file__).parent 7 | files = { 8 | cd.joinpath('nsis', 'api.h') : "https://raw.githubusercontent.com/kichik/nsis/master/Source/exehead/api.h", 9 | cd.joinpath('nsis', 'nsis_tchar.h') : "https://raw.githubusercontent.com/kichik/nsis/master/Contrib/ExDLL/nsis_tchar.h", 10 | cd.joinpath('nsis', 'pluginapi.c') : "https://raw.githubusercontent.com/kichik/nsis/master/Contrib/ExDLL/pluginapi.c", 11 | cd.joinpath('nsis', 'pluginapi.h') : "https://raw.githubusercontent.com/kichik/nsis/master/Contrib/ExDLL/pluginapi.h", 12 | } 13 | 14 | download = False 15 | for file in files: 16 | if not file.exists(): download = True 17 | 18 | if download: 19 | print("Downloading NSIS SDK ...") 20 | for file, url in files.items(): 21 | print(file.name) 22 | with request.urlopen(url) as http: 23 | file.parent.mkdir(parents=True, exist_ok=True) 24 | with open(file, 'wb') as outfile: 25 | outfile.write(http.read()) 26 | print(f" {http.status} {http.reason}, {http.getheader('Content-Length')} bytes") 27 | else: 28 | print("Use existing NSIS SDK ...") -------------------------------------------------------------------------------- /_make_package.bat: -------------------------------------------------------------------------------- 1 | @echo off 2 | 3 | cd /d "%~dp0" 4 | 5 | if not exist Release-mingw-amd64-unicode\NSxfer.dll echo ERROR: Missing Release-mingw-amd64-unicode\NSxfer.dll && pause && exit /B 2 6 | if not exist Release-mingw-x86-ansi\NSxfer.dll echo ERROR: Missing Release-mingw-x86-ansi\NSxfer.dll && pause && exit /B 2 7 | if not exist Release-mingw-x86-unicode\NSxfer.dll echo ERROR: Missing Release-mingw-x86-unicode\NSxfer.dll && pause && exit /B 2 8 | 9 | set Z7=%PROGRAMFILES%\7-Zip\7z.exe 10 | if not exist "%Z7%" echo ERROR: Missing %Z7% && pause && exit /B 2 11 | 12 | REM :: Read version from the .rc file 13 | for /f usebackq^ tokens^=3^ delims^=^"^,^ %%f in (`type Resource.rc ^| findstr /r /c:"\s*\"FileVersion\"\s*"`) do set RCVER=%%f 14 | 15 | rmdir /S /Q _Package > NUL 2> NUL 16 | mkdir _Package 17 | mkdir _Package\amd64-unicode 18 | mkdir _Package\x86-unicode 19 | mkdir _Package\x86-ansi 20 | 21 | mklink /H _Package\amd64-unicode\NSxfer.dll Release-mingw-amd64-unicode\NSxfer.dll 22 | mklink /H _Package\x86-unicode\NSxfer.dll Release-mingw-x86-unicode\NSxfer.dll 23 | mklink /H _Package\x86-ansi\NSxfer.dll Release-mingw-x86-ansi\NSxfer.dll 24 | mklink /H _Package\NSxfer.Readme.txt NSxfer.Readme.txt 25 | mklink /H _Package\README.md README.md 26 | mklink /H _Package\LICENSE.md LICENSE.md 27 | 28 | pushd _Package 29 | "%Z7%" a "..\NSxfer-%RCVER%.7z" * -r 30 | popd 31 | 32 | echo. 33 | pause 34 | 35 | rmdir /S /Q _Package > NUL 2> NUL 36 | -------------------------------------------------------------------------------- /gui.h: -------------------------------------------------------------------------------- 1 | 2 | //? Marius Negrutiu (marius.negrutiu@protonmail.com) :: 2014/10/25 3 | 4 | #pragma once 5 | 6 | typedef enum { 7 | GUI_MODE_SILENT, 8 | GUI_MODE_POPUP, 9 | GUI_MODE_PAGE 10 | } GUI_MODE; 11 | 12 | 13 | typedef struct _GUI_WAIT_PARAM { 14 | UINT iID; /// Can be ANY_REQUEST_ID 15 | ULONG iPriority; /// Can be ANY_PRIORITY 16 | GUI_MODE iMode; 17 | HWND hTitleWnd; /// Can be NULL 18 | HWND hStatusWnd; /// Can be NULL 19 | HWND hProgressWnd; /// Can be NULL 20 | LPCTSTR pszTitleText; /// Can be NULL 21 | LPCTSTR pszTitleMultiText; /// Can be NULL 22 | LPCTSTR pszStatusText; /// Can be NULL 23 | LPCTSTR pszStatusMultiText; /// Can be NULL 24 | BOOLEAN bAbort; 25 | LPCTSTR pszAbortTitle; /// Can be NULL 26 | LPCTSTR pszAbortMsg; /// Can be NULL 27 | } GUI_WAIT_PARAM, *PGUI_WAIT_PARAM; 28 | 29 | #define GuiWaitParamInit(Wait) \ 30 | MyZeroMemory( &Wait, sizeof( Wait ) ); \ 31 | Wait.iID = ANY_REQUEST_ID; \ 32 | Wait.iPriority = ANY_PRIORITY; \ 33 | Wait.iMode = GUI_MODE_PAGE; 34 | 35 | #define GuiWaitParamDestroy(Wait) \ 36 | MyFree( Wait.pszTitleText ); \ 37 | MyFree( Wait.pszTitleMultiText ); \ 38 | MyFree( Wait.pszStatusText ); \ 39 | MyFree( Wait.pszStatusMultiText ); \ 40 | MyFree( Wait.pszAbortTitle ); \ 41 | MyFree( Wait.pszAbortMsg ); \ 42 | MyZeroMemory( &Wait, sizeof( Wait ) ); 43 | 44 | 45 | /// Returns Win32 error code 46 | ULONG GuiWait( 47 | __in PGUI_WAIT_PARAM pParam 48 | ); 49 | -------------------------------------------------------------------------------- /main.c: -------------------------------------------------------------------------------- 1 | 2 | //? Marius Negrutiu (marius.negrutiu@protonmail.com) :: 2014/01/19 3 | 4 | #include "main.h" 5 | #include "utils.h" 6 | #include "queue.h" 7 | #include "gui.h" 8 | 9 | HINSTANCE g_hInst = NULL; 10 | BOOL g_bInitialized = FALSE; 11 | QUEUE g_Queue = { 0 }; 12 | 13 | // NSIS plugin API 14 | extra_parameters *g_ep = NULL; 15 | HWND g_hwndparent = NULL; 16 | 17 | 18 | //++ PluginInit 19 | BOOL PluginInit() 20 | { 21 | BOOL bRet = TRUE; 22 | if (!g_bInitialized) { 23 | 24 | TRACE( _T( "PluginInit\n" ) ); 25 | 26 | UtilsInitialize(); 27 | 28 | if (TRUE) { 29 | SYSTEM_INFO si; 30 | GetSystemInfo( &si ); 31 | QueueInitialize( &g_Queue, _T( "MAIN" ), __max( si.dwNumberOfProcessors, MIN_WORKER_THREADS ) ); 32 | } 33 | 34 | if ( TRUE ) { 35 | g_bInitialized = TRUE; 36 | } else { 37 | bRet = FALSE; 38 | } 39 | } 40 | return bRet; 41 | } 42 | 43 | 44 | //++ PluginUninit 45 | BOOL PluginUninit() 46 | { 47 | BOOL bRet = FALSE; 48 | if ( g_bInitialized ) { 49 | 50 | TRACE( _T( "PluginUninit\n" )); 51 | 52 | QueueDestroy( &g_Queue ); 53 | UtilsDestroy(); 54 | 55 | g_bInitialized = FALSE; 56 | bRet = TRUE; 57 | } 58 | return bRet; 59 | } 60 | 61 | 62 | //++ ParseRequestParameter 63 | BOOL ParseRequestParameter( 64 | _In_ int string_size, 65 | _In_ LPTSTR psz, /// Working buffer with the current parameter 66 | _In_ PQUEUE_REQUEST_PARAM pParam 67 | ) 68 | { 69 | BOOL bRet = TRUE; /// Assume that the current argument is valid 70 | assert( string_size && psz && pParam ); 71 | 72 | if (lstrcmpi( psz, _T( "/PRIORITY" ) ) == 0) { 73 | pParam->iPriority = popint(); 74 | } else if (lstrcmpi( psz, _T( "/DEPEND" ) ) == 0) { 75 | pParam->iDependId = popint(); 76 | } else if (lstrcmpi( psz, _T( "/METHOD" ) ) == 0) { 77 | if (popstring( psz ) == 0) { 78 | assert( 79 | lstrcmpi( psz, _T( "GET" ) ) == 0 || 80 | lstrcmpi( psz, _T( "POST" ) ) == 0 || 81 | lstrcmpi( psz, _T( "HEAD" ) ) == 0 82 | ); 83 | if (*psz) { 84 | MyFree( pParam->pszMethod ); 85 | MyStrDup( pParam->pszMethod, psz ); 86 | MyMakeUpper( pParam->pszMethod ); 87 | } 88 | } 89 | } else if (lstrcmpi( psz, _T( "/URL" ) ) == 0) { 90 | if (popstring( psz ) == 0) { 91 | MyFree( pParam->pszURL ); 92 | MyStrDup( pParam->pszURL, psz ); 93 | } 94 | } else if (lstrcmpi( psz, _T( "/LOCAL" ) ) == 0) { 95 | if (popstring( psz ) == 0) { 96 | MyFree( pParam->pszLocalFile ); 97 | if (lstrcmpi( psz, TEXT_LOCAL_NONE ) == 0) { 98 | pParam->iLocalType = REQUEST_LOCAL_NONE; 99 | } else if (lstrcmpi( psz, TEXT_LOCAL_MEMORY ) == 0) { 100 | pParam->iLocalType = REQUEST_LOCAL_MEMORY; 101 | } else { 102 | pParam->iLocalType = REQUEST_LOCAL_FILE; 103 | MyStrDup( pParam->pszLocalFile, psz ); 104 | } 105 | } 106 | } else if (lstrcmpi( psz, _T( "/HEADERS" ) ) == 0) { 107 | if (popstring( psz ) == 0) { 108 | MyFree( pParam->pszHeaders ); 109 | MyStrDup( pParam->pszHeaders, psz ); 110 | } 111 | } else if (lstrcmpi( psz, _T( "/DATA" ) ) == 0) { 112 | if (popstring( psz ) == 0) { 113 | MyFree( pParam->pData ); 114 | pParam->iDataSize = 0; 115 | #ifdef UNICODE 116 | pParam->pData = MyAlloc( string_size ); 117 | if (pParam->pData) { 118 | WideCharToMultiByte( CP_ACP, 0, psz, -1, (LPSTR)pParam->pData, string_size, NULL, NULL ); 119 | pParam->iDataSize = lstrlenA( (LPSTR)pParam->pData ); /// Don't trust what WideCharToMultiByte returns... 120 | } 121 | #else 122 | MyStrDup( pParam->pData, psz ); 123 | pParam->iDataSize = lstrlen( psz ); 124 | #endif 125 | } 126 | } else if (lstrcmpi( psz, _T( "/DATAFILE" ) ) == 0) { 127 | if (popstring( psz ) == 0) { 128 | HANDLE hFile = CreateFile( psz, GENERIC_READ, FILE_SHARE_READ, NULL, OPEN_EXISTING, 0, NULL ); 129 | if (hFile != INVALID_HANDLE_VALUE) { 130 | ULONG iFileSize = GetFileSize( hFile, NULL ); 131 | if (iFileSize != INVALID_FILE_SIZE || GetLastError() == ERROR_SUCCESS) { 132 | MyFree( pParam->pData ); 133 | pParam->iDataSize = 0; 134 | pParam->pData = MyAlloc( iFileSize ); 135 | if (pParam->pData) { 136 | if (!ReadFile( hFile, pParam->pData, iFileSize, &pParam->iDataSize, NULL )) { 137 | MyFree( pParam->pData ); 138 | pParam->iDataSize = 0; 139 | assert( !"/DATAFILE: Failed to read" ); 140 | } 141 | } else { 142 | assert( !"/DATAFILE: Failed to allocate memory" ); 143 | } 144 | } else { 145 | assert( !"/DATAFILE: Failed to get size" ); 146 | } 147 | CloseHandle( hFile ); 148 | } else { 149 | assert( !"/DATAFILE: Failed to open" ); 150 | } 151 | } 152 | } else if (lstrcmpi( psz, _T( "/TIMEOUTCONNECT" ) ) == 0) { 153 | pParam->iTimeoutConnect = popint(); 154 | } else if (lstrcmpi( psz, _T( "/TIMEOUTRECONNECT" ) ) == 0) { 155 | pParam->iTimeoutReconnect = popint(); 156 | } else if (lstrcmpi( psz, _T( "/OPTCONNECTRETRIES" ) ) == 0) { 157 | pParam->iOptConnectRetries = popint(); 158 | } else if (lstrcmpi( psz, _T( "/OPTCONNECTTIMEOUT" ) ) == 0) { 159 | pParam->iOptConnectTimeout = popint(); 160 | } else if (lstrcmpi( psz, _T( "/OPTRECEIVETIMEOUT" ) ) == 0) { 161 | pParam->iOptReceiveTimeout = popint(); 162 | } else if (lstrcmpi( psz, _T( "/OPTSENDTIMEOUT" ) ) == 0) { 163 | pParam->iOptSendTimeout = popint(); 164 | } else if (lstrcmpi( psz, _T( "/PROXY" ) ) == 0) { 165 | if (popstring( psz ) == 0) { 166 | MyFree( pParam->pszProxy ); 167 | MyStrDup( pParam->pszProxy, psz ); 168 | } 169 | } else if (lstrcmpi( psz, _T( "/PROXYUSER" ) ) == 0) { 170 | if (popstring( psz ) == 0) { 171 | MyFree( pParam->pszProxyUser ); 172 | MyStrDup( pParam->pszProxyUser, psz ); 173 | } 174 | } else if (lstrcmpi( psz, _T( "/PROXYPASS" ) ) == 0) { 175 | if (popstring( psz ) == 0) { 176 | MyFree( pParam->pszProxyPass ); 177 | MyStrDup( pParam->pszProxyPass, psz ); 178 | } 179 | } else if (lstrcmpi( psz, _T( "/REFERER" ) ) == 0) { 180 | if (popstring( psz ) == 0) { 181 | MyFree( pParam->pszReferrer ); 182 | MyStrDup( pParam->pszReferrer, psz ); 183 | } 184 | } else if (lstrcmpi( psz, _T( "/INTERNETFLAGS" ) ) == 0) { 185 | ULONG iFlags = (ULONG)popint_or(); 186 | if (iFlags != 0) 187 | pParam->iHttpInternetFlags = iFlags; 188 | } else if (lstrcmpi( psz, _T( "/SECURITYFLAGS" ) ) == 0) { 189 | ULONG iFlags = (ULONG)popint_or(); 190 | if (iFlags != 0) 191 | pParam->iHttpSecurityFlags = iFlags; 192 | } else { 193 | bRet = FALSE; /// This parameter is not valid for Request 194 | } 195 | 196 | return bRet; 197 | } 198 | 199 | 200 | //++ Request 201 | EXTERN_C __declspec(dllexport) 202 | void __cdecl Request( 203 | HWND parent, 204 | int string_size, 205 | TCHAR *variables, 206 | stack_t **stacktop, 207 | extra_parameters *extra 208 | ) 209 | { 210 | LPTSTR psz = NULL; 211 | QUEUE_REQUEST_PARAM Param; 212 | PQUEUE_REQUEST pReq = NULL; 213 | 214 | EXDLL_VALID_PARAMS(); 215 | EXDLL_INIT(); 216 | EXDLL_VALIDATE(); 217 | 218 | TRACE_CALL( stacktop, _T( "NSxfer!Request" ) ); 219 | 220 | // Lock the plugin in memory. PluginCallback will start receiving unload notifications 221 | extra->RegisterPluginCallback( g_hInst, PluginCallback ); 222 | 223 | /// Working buffer 224 | psz = (LPTSTR)MyAlloc( string_size * sizeof(TCHAR) ); 225 | assert( psz ); 226 | 227 | /// Parameters 228 | RequestParamInit( Param ); 229 | for (;;) 230 | { 231 | if (popstring( psz ) != 0) 232 | break; 233 | if (lstrcmpi( psz, _T( "/END" ) ) == 0) 234 | break; 235 | 236 | if (!ParseRequestParameter( string_size, psz, &Param )) { 237 | TRACE( _T( " [!] Unknown parameter \"%s\"\n" ), psz ); 238 | } 239 | } 240 | 241 | // Add to the queue 242 | QueueLock( &g_Queue ); 243 | QueueAdd( &g_Queue, &Param, &pReq ); 244 | pushint( pReq ? pReq->iId : 0 ); /// Return the request's ID 245 | QueueUnlock( &g_Queue ); 246 | 247 | RequestParamDestroy( Param ); 248 | MyFree( psz ); 249 | } 250 | 251 | 252 | //++ QueryGlobal 253 | EXTERN_C __declspec(dllexport) 254 | void __cdecl QueryGlobal( 255 | HWND parent, 256 | int string_size, 257 | TCHAR *variables, 258 | stack_t **stacktop, 259 | extra_parameters *extra 260 | ) 261 | { 262 | LPTSTR psz; 263 | LPTSTR pParam[30]; 264 | PQUEUE_REQUEST pReq; 265 | int iParamCount = 0, iDropCount = 0, i; 266 | 267 | ULONG iTotalThreads; 268 | ULONG iTotalCount = 0, iTotalCompleted = 0, iTotalDownloading = 0, iTotalWaiting = 0; 269 | ULONG64 iTotalRecvBytes = 0; 270 | ULONG iTotalSpeed = 0; 271 | 272 | EXDLL_VALID_PARAMS(); 273 | EXDLL_INIT(); 274 | EXDLL_VALIDATE(); 275 | 276 | TRACE_CALL( stacktop, _T( "NSxfer!QueryGlobal" ) ); 277 | 278 | // Lock the plugin in memory. PluginCallback will start receiving unload notifications 279 | extra->RegisterPluginCallback( g_hInst, PluginCallback ); 280 | 281 | /// Allocate the working buffer 282 | psz = (LPTSTR)MyAlloc( string_size * sizeof( TCHAR ) ); 283 | assert( psz ); 284 | 285 | /// Parameters 286 | while (TRUE) { 287 | if (popstring( psz ) != 0) 288 | break; 289 | if (lstrcmpi( psz, _T( "/END" ) ) == 0) 290 | break; 291 | if (iParamCount < ARRAYSIZE( pParam )) { 292 | /// Remember this parameter 293 | MyStrDup( pParam[iParamCount], psz ); 294 | iParamCount++; 295 | } else { 296 | /// Too many parameters 297 | iDropCount++; 298 | } 299 | } 300 | 301 | /// Statistics 302 | QueueLock( &g_Queue ); 303 | iTotalThreads = (ULONG)g_Queue.iThreadCount; 304 | for (pReq = g_Queue.pHead; pReq; pReq = pReq->pNext) { 305 | iTotalCount++; 306 | if (pReq->iStatus == REQUEST_STATUS_DONE) 307 | iTotalCompleted++; 308 | if (pReq->iStatus == REQUEST_STATUS_DOWNLOADING) 309 | iTotalDownloading++; 310 | if (pReq->iStatus == REQUEST_STATUS_WAITING) 311 | iTotalWaiting++; 312 | iTotalRecvBytes += pReq->iRecvSize; 313 | if (pReq->iStatus == REQUEST_STATUS_DOWNLOADING) 314 | iTotalSpeed += pReq->Speed.iSpeed; 315 | } 316 | QueueUnlock( &g_Queue ); 317 | 318 | /// Return empty strings for each dropped parameter 319 | for (i = 0; i < iDropCount; i++) 320 | pushstring( _T( "" ) ); 321 | 322 | /// Iterate all parameters (in reverse order) and return their values 323 | for (i = iParamCount - 1; i >= 0; i--) { 324 | if (lstrcmpi( pParam[i], _T( "/TOTALCOUNT" ) ) == 0) { 325 | pushint( iTotalCount ); 326 | } else if (lstrcmpi( pParam[i], _T( "/TOTALWAITING" ) ) == 0) { 327 | pushint( iTotalWaiting ); 328 | } else if (lstrcmpi( pParam[i], _T( "/TOTALDOWNLOADING" ) ) == 0) { 329 | pushint( iTotalDownloading ); 330 | } else if (lstrcmpi( pParam[i], _T( "/TOTALCOMPLETED" ) ) == 0) { 331 | pushint( iTotalCompleted ); 332 | } else if (lstrcmpi( pParam[i], _T( "/TOTALRECVSIZE" ) ) == 0) { 333 | TCHAR szSize[50]; 334 | #ifdef UNICODE 335 | StrFormatByteSizeW( iTotalRecvBytes, szSize, ARRAYSIZE( szSize ) ); 336 | #else 337 | StrFormatByteSizeA( (ULONG)iTotalRecvBytes, szSize, ARRAYSIZE( szSize ) ); 338 | #endif 339 | pushstring( szSize ); 340 | } else if (lstrcmpi( pParam[i], _T( "/TOTALRECVSIZEBYTES" ) ) == 0) { 341 | pushintptr( iTotalSpeed ); 342 | } else if (lstrcmpi( pParam[i], _T( "/TOTALSPEED" ) ) == 0) { 343 | TCHAR szSpeed[50]; 344 | #ifdef UNICODE 345 | StrFormatByteSizeW( (LONGLONG)iTotalSpeed, szSpeed, ARRAYSIZE( szSpeed ) ); 346 | #else 347 | StrFormatByteSizeA( iTotalSpeed, szSpeed, ARRAYSIZE( szSpeed ) ); 348 | #endif 349 | lstrcat( szSpeed, TEXT_PER_SECOND ); 350 | pushstring( szSpeed ); 351 | } else if (lstrcmpi( pParam[i], _T( "/TOTALSPEEDBYTES" ) ) == 0) { 352 | pushint( iTotalSpeed ); 353 | } else if (lstrcmpi( pParam[i], _T( "/TOTALTHREADS" ) ) == 0) { 354 | pushint( iTotalThreads ); 355 | } else if (lstrcmpi( pParam[i], _T( "/PLUGINNAME" ) ) == 0) { 356 | pushstring( PLUGINNAME ); 357 | } else if (lstrcmpi( pParam[i], _T( "/PLUGINVERSION" ) ) == 0) { 358 | TCHAR szPath[MAX_PATH], szVer[50]; 359 | szVer[0] = 0; 360 | if (GetModuleFileName( g_hInst, szPath, ARRAYSIZE( szPath ) ) > 0) 361 | ReadVersionInfoString( szPath, _T( "FileVersion" ), szVer, ARRAYSIZE( szVer ) ); 362 | pushstring( szVer ); 363 | } else if (lstrcmpi( pParam[i], _T( "/USERAGENT" ) ) == 0) { 364 | pushstring( TEXT_USERAGENT ); 365 | } else { 366 | TRACE( _T( " [!] Unknown parameter \"%s\"\n" ), pParam[i] ); 367 | pushstring( _T( "" ) ); 368 | } 369 | MyFree( pParam[i] ); 370 | } 371 | 372 | MyFree( psz ); 373 | } 374 | 375 | 376 | //++ Query 377 | EXTERN_C __declspec(dllexport) 378 | void __cdecl Query( 379 | HWND parent, 380 | int string_size, 381 | TCHAR *variables, 382 | stack_t **stacktop, 383 | extra_parameters *extra 384 | ) 385 | { 386 | LPTSTR psz; 387 | ULONG iReqID = 0; 388 | PQUEUE_REQUEST pReq = NULL; 389 | 390 | LPTSTR pParam[30]; 391 | int iParamCount = 0, iDropCount = 0, i; 392 | 393 | EXDLL_VALID_PARAMS(); 394 | EXDLL_INIT(); 395 | EXDLL_VALIDATE(); 396 | 397 | TRACE_CALL( stacktop, _T( "NSxfer!Query" ) ); 398 | 399 | // Lock the plugin in memory. PluginCallback will start receiving unload notifications 400 | extra->RegisterPluginCallback( g_hInst, PluginCallback ); 401 | 402 | /// Allocate the working buffer 403 | psz = (LPTSTR)MyAlloc( string_size * sizeof( TCHAR ) ); 404 | assert( psz ); 405 | 406 | /// Parameters 407 | while (TRUE) { 408 | if (popstring( psz ) != 0) 409 | break; 410 | if (lstrcmpi( psz, _T( "/END" ) ) == 0) 411 | break; 412 | 413 | if (lstrcmpi( psz, _T( "/ID" ) ) == 0) { 414 | iReqID = (ULONG)popint(); 415 | } else { 416 | if (iParamCount < ARRAYSIZE( pParam )) { 417 | /// Remember this parameter 418 | MyStrDup( pParam[iParamCount], psz ); 419 | iParamCount++; 420 | } else { 421 | /// Too many parameters 422 | iDropCount++; 423 | } 424 | } 425 | } 426 | 427 | /// Lock 428 | QueueLock( &g_Queue ); 429 | 430 | /// Find the request ID 431 | pReq = QueueFind( &g_Queue, iReqID ); 432 | 433 | /// Return empty strings for dropped parameters 434 | for (i = 0; i < iDropCount; i++) 435 | pushstring( _T( "" ) ); 436 | 437 | /// Iterate all parameters (in reverse order) and return their values 438 | for (i = iParamCount - 1; i >= 0; i--) { 439 | if (pReq) { 440 | if (lstrcmpi( pParam[i], _T( "/PRIORITY" ) ) == 0) { 441 | pushint( pReq->iPriority ); 442 | } else if (lstrcmpi( pParam[i], _T( "/DEPEND" ) ) == 0) { 443 | pushint( pReq->iDependId ); 444 | } else if (lstrcmpi( pParam[i], _T( "/STATUS" ) ) == 0) { 445 | switch (pReq->iStatus) { 446 | case REQUEST_STATUS_WAITING: 447 | pushstring( TEXT_STATUS_WAITING ); 448 | break; 449 | case REQUEST_STATUS_DOWNLOADING: 450 | pushstring( TEXT_STATUS_DOWNLOADING ); 451 | break; 452 | case REQUEST_STATUS_DONE: 453 | pushstring( TEXT_STATUS_COMPLETED ); 454 | break; 455 | default: 456 | pushstring( _T( "" ) ); 457 | } 458 | } else if (lstrcmpi( pParam[i], _T( "/WININETSTATUS" ) ) == 0) { 459 | pushint( pReq->iLastCallbackStatus ); 460 | } else if (lstrcmpi( pParam[i], _T( "/METHOD" ) ) == 0) { 461 | pushstring( pReq->szMethod ); 462 | } else if (lstrcmpi( pParam[i], _T( "/URL" ) ) == 0) { 463 | safepushstring( pReq->pszURL ); 464 | } else if (lstrcmpi( pParam[i], _T( "/IP" ) ) == 0) { 465 | safepushstring( pReq->pszSrvIP ); 466 | } else if (lstrcmpi( pParam[i], _T( "/PROXY" ) ) == 0) { 467 | safepushstring( pReq->pszProxy ); 468 | } else if (lstrcmpi( pParam[i], _T( "/LOCAL" ) ) == 0) { 469 | switch (pReq->iLocalType) { 470 | case REQUEST_LOCAL_NONE: 471 | pushstring( TEXT_LOCAL_NONE ); 472 | break; 473 | case REQUEST_LOCAL_FILE: 474 | safepushstring( pReq->Local.pszFile ); 475 | break; 476 | case REQUEST_LOCAL_MEMORY: 477 | pushstring( TEXT_LOCAL_MEMORY ); 478 | break; 479 | default: 480 | pushstring( _T( "" ) ); 481 | } 482 | } else if (lstrcmpi( pParam[i], _T( "/SENTHEADERS" ) ) == 0) { 483 | safepushstring( pReq->pszHeaders ); 484 | } else if (lstrcmpi( pParam[i], _T( "/RECVHEADERS" ) ) == 0) { 485 | safepushstring( pReq->pszSrvHeaders ); 486 | } else if (lstrcmpi( pParam[i], _T( "/FILESIZE" ) ) == 0) { 487 | if (pReq->iFileSize != INVALID_FILE_SIZE64) { 488 | TCHAR sz[30]; 489 | wnsprintf( sz, ARRAYSIZE( sz ), _T( "%I64u" ), pReq->iFileSize ); 490 | pushstring( sz ); 491 | } else { 492 | pushstring( _T( "" ) ); 493 | } 494 | } else if (lstrcmpi( pParam[i], _T( "/RECVSIZE" ) ) == 0) { 495 | TCHAR sz[30]; 496 | wnsprintf( sz, ARRAYSIZE( sz ), _T( "%I64u" ), pReq->iRecvSize ); 497 | pushstring( sz ); 498 | } else if (lstrcmpi( pParam[i], _T( "/PERCENT" ) ) == 0) { 499 | pushint( RequestRecvPercent( pReq ) ); 500 | } else if (lstrcmpi( pParam[i], _T( "/SPEEDBYTES" ) ) == 0) { 501 | pushint( pReq->Speed.iSpeed ); 502 | } else if (lstrcmpi( pParam[i], _T( "/SPEED" ) ) == 0) { 503 | pushstring( pReq->Speed.szSpeed ); 504 | } else if (lstrcmpi( pParam[i], _T( "/CONTENT" ) ) == 0) { 505 | RequestMemoryToString( pReq, psz, string_size ); 506 | safepushstring( psz ); 507 | } else if (lstrcmpi( pParam[i], _T( "/DATA" ) ) == 0) { 508 | RequestDataToString( pReq, psz, string_size ); 509 | safepushstring( psz ); 510 | } else if (lstrcmpi( pParam[i], _T( "/TIMEWAITING" ) ) == 0) { 511 | switch (pReq->iStatus) { 512 | case REQUEST_STATUS_WAITING: 513 | { 514 | FILETIME tmNow; 515 | GetLocalFileTime( &tmNow ); 516 | pushint( MyTimeDiff( &tmNow, &pReq->tmEnqueue ) ); 517 | break; 518 | } 519 | case REQUEST_STATUS_DOWNLOADING: 520 | case REQUEST_STATUS_DONE: 521 | { 522 | pushint( MyTimeDiff( &pReq->tmConnect, &pReq->tmEnqueue ) ); 523 | break; 524 | } 525 | default: 526 | pushstring( _T( "" ) ); 527 | } 528 | } else if (lstrcmpi( pParam[i], _T( "/TIMEDOWNLOADING" ) ) == 0) { 529 | switch (pReq->iStatus) { 530 | case REQUEST_STATUS_WAITING: 531 | pushstring( _T( "" ) ); /// No downloading time 532 | break; 533 | case REQUEST_STATUS_DOWNLOADING: 534 | { 535 | FILETIME tmNow; 536 | GetLocalFileTime( &tmNow ); 537 | pushint( MyTimeDiff( &tmNow, &pReq->tmConnect ) ); 538 | break; 539 | } 540 | case REQUEST_STATUS_DONE: 541 | { 542 | pushint( MyTimeDiff( &pReq->tmDisconnect, &pReq->tmConnect ) ); 543 | break; 544 | } 545 | default: 546 | pushstring( _T( "" ) ); 547 | } 548 | } else if (lstrcmpi( pParam[i], _T( "/CONNECTIONDROPS" ) ) == 0) { 549 | pushint( pReq->iConnectionDrops ); 550 | } else if (lstrcmpi( pParam[i], _T( "/ERRORCODE" ) ) == 0) { 551 | pushint( pReq->iWin32Error == ERROR_SUCCESS ? pReq->iHttpStatus : pReq->iWin32Error ); 552 | } else if (lstrcmpi( pParam[i], _T( "/ERRORTEXT" ) ) == 0) { 553 | safepushstring( pReq->iWin32Error == ERROR_SUCCESS ? pReq->pszHttpStatus : pReq->pszWin32Error ); 554 | } else { 555 | TRACE( _T( " [!] Unknown parameter \"%s\"\n" ), pParam[i] ); 556 | pushstring( _T( "" ) ); 557 | } 558 | } else { 559 | TRACE( _T( " [!] Transfer ID not found\n" ) ); 560 | pushstring( _T( "" ) ); 561 | } 562 | MyFree( pParam[i] ); 563 | } 564 | 565 | /// Unlock 566 | QueueUnlock( &g_Queue ); 567 | 568 | MyFree( psz ); 569 | } 570 | 571 | 572 | //++ Set 573 | EXTERN_C __declspec(dllexport) 574 | void __cdecl Set( 575 | HWND parent, 576 | int string_size, 577 | TCHAR *variables, 578 | stack_t **stacktop, 579 | extra_parameters *extra 580 | ) 581 | { 582 | DWORD err = ERROR_SUCCESS; 583 | LPTSTR psz; 584 | 585 | ULONG iId = ANY_REQUEST_ID; /// Selection parameter 586 | ULONG iPrio = ANY_PRIORITY; /// Selection parameter 587 | 588 | ULONG iNewDependId = DEFAULT_VALUE; 589 | ULONG iNewPrio = DEFAULT_VALUE; 590 | BOOLEAN bRemove = FALSE; 591 | BOOLEAN bAbort = FALSE; 592 | 593 | EXDLL_VALID_PARAMS(); 594 | EXDLL_INIT(); 595 | EXDLL_VALIDATE(); 596 | 597 | TRACE_CALL( stacktop, _T( "NSxfer!Set" ) ); 598 | 599 | // Lock the plugin in memory. PluginCallback will start receiving unload notifications 600 | extra->RegisterPluginCallback( g_hInst, PluginCallback ); 601 | 602 | // Parameters 603 | psz = (LPTSTR)MyAlloc( string_size * sizeof( TCHAR ) ); 604 | while (TRUE) { 605 | if (popstring( psz ) != 0) 606 | break; 607 | if (lstrcmpi( psz, _T( "/END" ) ) == 0) { 608 | break; 609 | } else if (lstrcmpi( psz, _T( "/ID" ) ) == 0) { 610 | iId = popint(); 611 | } else if (lstrcmpi( psz, _T( "/PRIORITY" ) ) == 0) { 612 | iPrio = popint(); 613 | } else if (lstrcmpi( psz, _T( "/SETPRIORITY" ) ) == 0) { 614 | iNewPrio = popint(); 615 | } else if (lstrcmpi( psz, _T( "/SETDEPEND" ) ) == 0) { 616 | iNewDependId = popint(); 617 | } else if (lstrcmpi( psz, _T( "/ABORT" ) ) == 0) { 618 | bAbort = TRUE; 619 | } else if (lstrcmpi( psz, _T( "/REMOVE" ) ) == 0) { 620 | bRemove = TRUE; 621 | } else { 622 | TRACE( _T( " [!] Unknown parameter \"%s\"\n" ), psz ); 623 | } 624 | } 625 | MyFree( psz ); 626 | 627 | // Set 628 | if (TRUE) { 629 | 630 | ULONG iThreadsToWake = 0; 631 | PQUEUE_REQUEST pReq; 632 | 633 | QueueLock( &g_Queue ); 634 | for (pReq = g_Queue.pHead; pReq; ) { 635 | if (RequestMatched( pReq, iId, iPrio, ANY_STATUS )) { 636 | if (bRemove) { 637 | // Abort + Remove 638 | if (QueueAbort( &g_Queue, pReq, 10000 )) { 639 | PQUEUE_REQUEST pReqRemove = pReq; 640 | pReq = pReq->pNext; 641 | if (QueueRemove( &g_Queue, pReqRemove )) { 642 | ///iRet++; 643 | continue; 644 | } 645 | } 646 | } else if (bAbort) { 647 | // Abort 648 | if (QueueAbort( &g_Queue, pReq, 10000 )) { 649 | ///iRet++; 650 | } 651 | } else { 652 | // Modify 653 | if (iNewPrio != DEFAULT_VALUE) 654 | pReq->iPriority = iNewPrio; 655 | if (iNewDependId != DEFAULT_VALUE) { 656 | pReq->iDependId = iNewDependId; 657 | iThreadsToWake++; 658 | } 659 | } 660 | } 661 | pReq = pReq->pNext; /// Next in list 662 | } 663 | QueueUnlock( &g_Queue ); 664 | 665 | if (iThreadsToWake > 0) 666 | QueueWakeThreads( &g_Queue, iThreadsToWake ); 667 | } 668 | 669 | pushint( err ); 670 | } 671 | 672 | 673 | //++ Enumerate 674 | EXTERN_C __declspec(dllexport) 675 | void __cdecl Enumerate( 676 | HWND parent, 677 | int string_size, 678 | TCHAR *variables, 679 | stack_t **stacktop, 680 | extra_parameters *extra 681 | ) 682 | { 683 | LPTSTR psz; 684 | REQUEST_STATUS iStatus = ANY_STATUS; 685 | ULONG iPrio = ANY_PRIORITY; 686 | 687 | EXDLL_VALID_PARAMS(); 688 | EXDLL_INIT(); 689 | EXDLL_VALIDATE(); 690 | 691 | TRACE_CALL( stacktop, _T( "NSxfer!Enumerate" ) ); 692 | 693 | // Lock the plugin in memory. PluginCallback will start receiving unload notifications 694 | extra->RegisterPluginCallback( g_hInst, PluginCallback ); 695 | 696 | // Decide what requests to enumerate 697 | psz = (LPTSTR)MyAlloc( string_size * sizeof( TCHAR ) ); 698 | while (TRUE) { 699 | if (popstring( psz ) != 0) 700 | break; 701 | if (lstrcmpi( psz, _T( "/END" ) ) == 0) { 702 | break; 703 | } else if (lstrcmpi( psz, _T( "/STATUS" ) ) == 0) { 704 | if (popstring( psz ) == 0) { 705 | if (lstrcmpi( psz, TEXT_STATUS_DOWNLOADING ) == 0) { 706 | iStatus = REQUEST_STATUS_DOWNLOADING; 707 | } else if (lstrcmpi( psz, TEXT_STATUS_WAITING ) == 0) { 708 | iStatus = REQUEST_STATUS_WAITING; 709 | } else if (lstrcmpi( psz, TEXT_STATUS_COMPLETED ) == 0) { 710 | iStatus = REQUEST_STATUS_DONE; 711 | //} else if (lstrcmpi( psz, _T( "paused" ) ) == 0) { 712 | // iStatus = REQUEST_STATUS_PAUSED; 713 | } 714 | } 715 | } else if (lstrcmpi( psz, _T( "/PRIORITY" ) ) == 0) { 716 | iPrio = popint(); 717 | } else { 718 | TRACE( _T( " [!] Unknown parameter \"%s\"\n" ), psz ); 719 | } 720 | } 721 | MyFree( psz ); 722 | 723 | // Enumerate 724 | if (TRUE) { 725 | PQUEUE_REQUEST pReq; 726 | int iCount = 0; 727 | QueueLock( &g_Queue ); 728 | for (pReq = g_Queue.pHead; pReq; pReq = pReq->pNext) { 729 | if (RequestMatched( pReq, ANY_REQUEST_ID, iPrio, iStatus )) { 730 | pushint( pReq->iId ); 731 | iCount++; 732 | } 733 | } 734 | pushint( iCount ); 735 | QueueUnlock( &g_Queue ); 736 | } 737 | } 738 | 739 | 740 | 741 | //++ ParseWaitParameter 742 | BOOL ParseWaitParameter( 743 | _In_ int string_size, 744 | _In_ LPTSTR psz, /// Working buffer with the current parameter 745 | _In_ PGUI_WAIT_PARAM pParam 746 | ) 747 | { 748 | BOOL bRet = TRUE; /// Assume that the current argument is valid 749 | assert( string_size && psz && pParam ); 750 | 751 | if (lstrcmpi( psz, _T( "/ID" ) ) == 0) { 752 | pParam->iID = popint(); 753 | } else if (lstrcmpi( psz, _T( "/PRIORITY" ) ) == 0) { 754 | pParam->iPriority = popint(); 755 | } else if (lstrcmpi( psz, _T( "/MODE" ) ) == 0) { 756 | if (popstring( psz ) == 0) { 757 | if (lstrcmpi( psz, _T( "SILENT" ) ) == 0) 758 | pParam->iMode = GUI_MODE_SILENT; 759 | else if (lstrcmpi( psz, _T( "POPUP" ) ) == 0) 760 | pParam->iMode = GUI_MODE_POPUP; 761 | else if (lstrcmpi( psz, _T( "PAGE" ) ) == 0) 762 | pParam->iMode = GUI_MODE_PAGE; 763 | else { 764 | pParam->iMode = GUI_MODE_POPUP; /// Default 765 | TRACE( _T( " [!] Unknown GUI mode \"%s\"\n" ), psz ); 766 | } 767 | } 768 | } else if (lstrcmpi( psz, _T( "/TITLEHWND" ) ) == 0) { 769 | pParam->hTitleWnd = (HWND)popintptr(); 770 | } else if (lstrcmpi( psz, _T( "/STATUSHWND" ) ) == 0) { 771 | pParam->hStatusWnd = (HWND)popintptr(); 772 | } else if (lstrcmpi( psz, _T( "/PROGRESSHWND" ) ) == 0) { 773 | pParam->hProgressWnd = (HWND)popintptr(); 774 | } else if (lstrcmpi( psz, _T( "/TITLETEXT" ) ) == 0) { 775 | if (popstring( psz ) == 0) { 776 | MyFree( pParam->pszTitleText ); 777 | MyStrDup( pParam->pszTitleText, psz ); 778 | } 779 | if (popstring( psz ) == 0) { 780 | MyFree( pParam->pszTitleMultiText ); 781 | MyStrDup( pParam->pszTitleMultiText, psz ); 782 | } 783 | } else if (lstrcmpi( psz, _T( "/STATUSTEXT" ) ) == 0) { 784 | if (popstring( psz ) == 0) { 785 | MyFree( pParam->pszStatusText ); 786 | MyStrDup( pParam->pszStatusText, psz ); 787 | } 788 | if (popstring( psz ) == 0) { 789 | MyFree( pParam->pszStatusMultiText ); 790 | MyStrDup( pParam->pszStatusMultiText, psz ); 791 | } 792 | } else if (lstrcmpi( psz, _T( "/ABORT" ) ) == 0) { 793 | pParam->bAbort = TRUE; 794 | if (popstring( psz ) == 0) { 795 | MyFree( pParam->pszAbortTitle ); 796 | MyStrDup( pParam->pszAbortTitle, psz ); 797 | } 798 | if (popstring( psz ) == 0) { 799 | MyFree( pParam->pszAbortMsg ); 800 | MyStrDup( pParam->pszAbortMsg, psz ); 801 | } 802 | } else { 803 | bRet = FALSE; /// This parameter is not valid for Request 804 | } 805 | 806 | return bRet; 807 | } 808 | 809 | 810 | //++ Wait 811 | EXTERN_C __declspec(dllexport) 812 | void __cdecl Wait( 813 | HWND parent, 814 | int string_size, 815 | TCHAR *variables, 816 | stack_t **stacktop, 817 | extra_parameters *extra 818 | ) 819 | { 820 | INT_PTR iRet = 0; 821 | LPTSTR psz; 822 | GUI_WAIT_PARAM Param; 823 | 824 | EXDLL_VALID_PARAMS(); 825 | EXDLL_INIT(); 826 | EXDLL_VALIDATE(); 827 | 828 | TRACE_CALL( stacktop, _T( "NSxfer!Wait" ) ); 829 | 830 | // Lock the plugin in memory. PluginCallback will start receiving unload notifications 831 | extra->RegisterPluginCallback( g_hInst, PluginCallback ); 832 | 833 | /// Working buffer 834 | psz = (LPTSTR)MyAlloc( string_size * sizeof( TCHAR ) ); 835 | assert( psz ); 836 | 837 | /// Parameters 838 | GuiWaitParamInit( Param ); 839 | for (;;) 840 | { 841 | if (popstring( psz ) != 0) 842 | break; 843 | if (lstrcmpi( psz, _T( "/END" ) ) == 0) 844 | break; 845 | 846 | if (!ParseWaitParameter( string_size, psz, &Param )) { 847 | TRACE( _T( " [!] Unknown parameter \"%s\"\n" ), psz ); 848 | } 849 | } 850 | 851 | // Wait 852 | iRet = GuiWait( &Param ); 853 | 854 | GuiWaitParamDestroy( Param ); 855 | MyFree( psz ); 856 | pushintptr( iRet ); 857 | } 858 | 859 | 860 | //++ Transfer 861 | EXTERN_C __declspec(dllexport) 862 | void __cdecl Transfer( 863 | HWND parent, 864 | int string_size, 865 | TCHAR *variables, 866 | stack_t **stacktop, 867 | extra_parameters *extra 868 | ) 869 | { 870 | LPTSTR psz = NULL; 871 | QUEUE_REQUEST_PARAM ReqParam; 872 | GUI_WAIT_PARAM WaitParam; 873 | PQUEUE_REQUEST pReq = NULL; 874 | BOOL bReturnID = FALSE; 875 | 876 | EXDLL_VALID_PARAMS(); 877 | EXDLL_INIT(); 878 | EXDLL_VALIDATE(); 879 | 880 | TRACE_CALL( stacktop, _T( "NSxfer!Transfer" ) ); 881 | 882 | // Lock the plugin in memory. PluginCallback will start receiving unload notifications 883 | extra->RegisterPluginCallback( g_hInst, PluginCallback ); 884 | 885 | /// Working buffer 886 | psz = (LPTSTR)MyAlloc( string_size * sizeof( TCHAR ) ); 887 | assert( psz ); 888 | 889 | /// Parameters 890 | RequestParamInit( ReqParam ); 891 | GuiWaitParamInit( WaitParam ); 892 | for (;;) { 893 | if (popstring( psz ) != 0) 894 | break; 895 | if (lstrcmpi( psz, _T( "/END" ) ) == 0) 896 | break; 897 | 898 | if (lstrcmpi(psz, _T("/RETURNID")) == 0) { 899 | bReturnID = TRUE; 900 | } else if ( ParseRequestParameter(string_size, psz, &ReqParam) || 901 | ParseWaitParameter(string_size, psz, &WaitParam)) 902 | { 903 | // Valid parameter, saved to pReq 904 | } else { 905 | TRACE(_T(" [!] Unknown parameter \"%s\"\n"), psz); 906 | } 907 | } 908 | 909 | // Add to the queue 910 | QueueLock( &g_Queue ); 911 | QueueAdd( &g_Queue, &ReqParam, &pReq ); 912 | QueueUnlock( &g_Queue ); 913 | 914 | // Wait 915 | if (pReq) { 916 | WaitParam.iID = pReq->iId; 917 | WaitParam.iPriority = ANY_PRIORITY; 918 | GuiWait( &WaitParam ); 919 | } 920 | 921 | // Return value 922 | if (pReq) { 923 | QueueLock( &g_Queue ); 924 | if (bReturnID) { 925 | pushint(pReq->iId); 926 | } else { 927 | if (pReq->iWin32Error == ERROR_SUCCESS) { 928 | if (pReq->iHttpStatus > 200 && pReq->iHttpStatus < 300) 929 | pushstring(_T("OK")); /// Convert any successful HTTP status to "OK" 930 | else 931 | safepushstring(pReq->pszHttpStatus); /// HTTP status 932 | } else { 933 | safepushstring(pReq->pszWin32Error); /// Win32 error 934 | } 935 | } 936 | QueueUnlock( &g_Queue ); 937 | } else { 938 | pushstring( _T( "Error" ) ); 939 | } 940 | 941 | GuiWaitParamDestroy( WaitParam ); 942 | RequestParamDestroy( ReqParam ); 943 | MyFree( psz ); 944 | } 945 | 946 | 947 | //++ Test 948 | EXTERN_C __declspec(dllexport) 949 | void __cdecl Test( 950 | HWND parent, 951 | int string_size, 952 | TCHAR *variables, 953 | stack_t **stacktop, 954 | extra_parameters *extra 955 | ) 956 | { 957 | EXDLL_VALID_PARAMS(); 958 | EXDLL_INIT(); 959 | EXDLL_VALIDATE(); 960 | 961 | TRACE_CALL( stacktop, _T( "NSxfer!Test" ) ); 962 | 963 | // Lock the plugin in memory. PluginCallback will start receiving unload notifications 964 | extra->RegisterPluginCallback( g_hInst, PluginCallback ); 965 | 966 | /*{ 967 | ULONGLONG ull, ull2; 968 | double d; 969 | 970 | ull = 1234567890; 971 | d = MyUlonglongToDouble( ull ); 972 | ull2 = MyDoubleToUlonglong( d ); 973 | assert( ull == ull2 ); 974 | }*/ 975 | 976 | /*{ 977 | ULONG i; 978 | FILETIME t1, t2; 979 | GetSystemTimeAsFileTime( &t1 ); 980 | Sleep( 1234 ); 981 | GetSystemTimeAsFileTime( &t2 ); 982 | i = MyTimeDiff( &t2, &t1 ); 983 | assert( i == 1234 ); 984 | }*/ 985 | } 986 | 987 | 988 | //++ PluginCallback 989 | UINT_PTR __cdecl PluginCallback( enum NSPIM iMessage ) 990 | { 991 | switch ( iMessage ) 992 | { 993 | case NSPIM_UNLOAD: 994 | TRACE( _T( "NSPIM_UNLOAD\n" ) ); 995 | PluginUninit(); 996 | break; 997 | 998 | case NSPIM_GUIUNLOAD: 999 | TRACE( _T( "NSPIM_GUIUNLOAD\n" ) ); 1000 | break; 1001 | } 1002 | return 0; 1003 | } 1004 | 1005 | 1006 | //++ DllMain 1007 | EXTERN_C 1008 | BOOL WINAPI DllMain( 1009 | HMODULE hInst, 1010 | UINT iReason, 1011 | LPVOID lpReserved 1012 | ) 1013 | { 1014 | if ( iReason == DLL_PROCESS_ATTACH ) { 1015 | g_hInst = hInst; 1016 | PluginInit(); 1017 | } 1018 | else if ( iReason == DLL_PROCESS_DETACH ) { 1019 | PluginUninit(); 1020 | } 1021 | return TRUE; 1022 | } 1023 | -------------------------------------------------------------------------------- /main.h: -------------------------------------------------------------------------------- 1 | 2 | //? Marius Negrutiu (marius.negrutiu@protonmail.com) :: 2014/01/19 3 | 4 | #pragma once 5 | 6 | #ifndef _DEBUG 7 | #if DBG || _DEBUG 8 | #define _DEBUG 9 | #endif 10 | #endif 11 | 12 | #define PLUGINNAME _T( "NSxfer" ) 13 | 14 | #define COBJMACROS 15 | 16 | #define _WIN32_WINNT 0x0500 17 | #define _WIN32_IE 0x0600 18 | #include 19 | #include 20 | #include /// for wvnsprintf, StrToInt64Ex 21 | #include 22 | #include /// ITaskbarList 23 | 24 | // --> NSIS plugin API 25 | #include 26 | 27 | // Validate input parameters to prevent crashes 28 | // VirusTotal "detonates" dlls by running `RunDll32.exe "",` with no parameters 29 | // If exported functions don't validate input parameters properly they will likely crash, triggering WER to launch WerFault.exe as child process 30 | // When this happens, our dll is labeled as a potential program launcher increasing the chances of being reported as malitious 31 | // NOTE: Functions called from .onInit receive parent=NULL 32 | #define EXDLL_VALID_PARAMS() \ 33 | if ((parent && !IsWindow(parent)) || string_size == 0 || (string_size % 1024) != 0 || !variables || !stacktop || !extra) \ 34 | return; 35 | 36 | #undef EXDLL_INIT 37 | #define EXDLL_INIT() { \ 38 | g_stringsize=string_size; \ 39 | g_stacktop=stacktop; \ 40 | g_variables=variables; \ 41 | g_ep=extra; \ 42 | g_hwndparent=parent; } 43 | 44 | #define EXDLL_VALIDATE() \ 45 | if (g_ep && g_ep->exec_flags && (g_ep->exec_flags->plugin_api_version != NSISPIAPIVER_CURR)) \ 46 | return; 47 | 48 | extern extra_parameters *g_ep; /// main.c 49 | extern HWND g_hwndparent; /// main.c 50 | #define safepushstring(psz) pushstring( (psz) ? (psz) : _T("") ) 51 | UINT_PTR __cdecl PluginCallback( enum NSPIM iMessage ); 52 | // <-- NSIS plugin API 53 | 54 | extern HINSTANCE g_hInst; /// Defined in main.c 55 | -------------------------------------------------------------------------------- /queue.c: -------------------------------------------------------------------------------- 1 | 2 | //? Marius Negrutiu (marius.negrutiu@protonmail.com) :: 2014/02/02 3 | 4 | #include "main.h" 5 | #include "queue.h" 6 | #include "utils.h" 7 | 8 | 9 | VOID QueueInitialize( 10 | _Inout_ PQUEUE pQueue, 11 | _In_ LPCTSTR szName, 12 | _In_ int iThreadCount 13 | ) 14 | { 15 | assert( pQueue ); 16 | assert( szName && *szName ); 17 | assert( iThreadCount < MAX_WORKER_THREADS ); 18 | 19 | // Name 20 | if ( szName && *szName ) { 21 | lstrcpyn( pQueue->szName, szName, ARRAYSIZE( pQueue->szName ) ); 22 | } else { 23 | wnsprintf( pQueue->szName, ARRAYSIZE( pQueue->szName ), _T( "%p" ), pQueue ); 24 | } 25 | 26 | // Queue 27 | InitializeCriticalSectionAndSpinCount( &pQueue->csLock, 3000 ); 28 | pQueue->pHead = NULL; 29 | pQueue->iLastId = 0; 30 | 31 | // Worker threads 32 | pQueue->iThreadMaxCount = __min( iThreadCount, MAX_WORKER_THREADS - 1 ); 33 | pQueue->iThreadCount = 0; // Don't create worker threads yet 34 | pQueue->iThreadBusyCount = 0; 35 | pQueue->hThreadTermEvent = CreateEvent( NULL, TRUE, FALSE, NULL ); /// Manual 36 | pQueue->hThreadWakeEvent = CreateEvent( NULL, FALSE, FALSE, NULL ); /// Automatic 37 | assert( pQueue->hThreadTermEvent ); 38 | assert( pQueue->hThreadWakeEvent ); 39 | 40 | TRACE( _T( " QueueInitialize(%s, ThCnt:%d)\n" ), szName, pQueue->iThreadMaxCount ); 41 | } 42 | 43 | VOID QueueDestroy( _Inout_ PQUEUE pQueue ) 44 | { 45 | assert( pQueue ); 46 | 47 | // Worker threads 48 | if ( pQueue->iThreadCount > 0 ) { 49 | 50 | HANDLE pObj[MAX_WORKER_THREADS]; 51 | ULONG i, iObjCnt = 0; 52 | const ULONG QUEUE_WAIT_FOR_THREADS = 12000; 53 | 54 | /// Signal thread termination 55 | SetEvent( pQueue->hThreadTermEvent ); 56 | 57 | /// Build a list of thread handles 58 | for (i = 0; i < pQueue->iThreadCount; i++) 59 | if (pQueue->pThreads[i].hThread) 60 | pObj[iObjCnt++] = pQueue->pThreads[i].hThread; 61 | 62 | TRACE( _T( " Waiting for %u threads (max. %ums)...\n" ), iObjCnt, QUEUE_WAIT_FOR_THREADS ); 63 | 64 | /// Wait for all threads to terminate 65 | if (iObjCnt > 0) { 66 | DWORD dwTime = dwTime = GetTickCount(); 67 | DWORD iWait = WaitForMultipleObjects( iObjCnt, pObj, TRUE, QUEUE_WAIT_FOR_THREADS ); 68 | dwTime = GetTickCount() - dwTime; 69 | if (iWait == WAIT_OBJECT_0 || iWait == WAIT_ABANDONED_0) { 70 | TRACE( _T( " Threads closed in %ums\n" ), dwTime ); 71 | MyZeroMemory( pQueue->pThreads, ARRAYSIZE( pQueue->pThreads ) * sizeof( THREAD ) ); 72 | } else if (iWait == WAIT_TIMEOUT) { 73 | TRACE( _T( " Threads failed to stop after %ums. Will terminate them forcedly\n" ), dwTime ); 74 | for (i = 0; i < iObjCnt; i++) 75 | TerminateThread( pObj[i], 666 ); 76 | } else { 77 | DWORD err = GetLastError(); 78 | TRACE( _T( " [!] WaitForMultipleObjects( ObjCnt:%u ) == 0x%x, GLE == 0x%x\n" ), iObjCnt, iWait, err ); 79 | for (i = 0; i < iObjCnt; i++) 80 | TerminateThread( pObj[i], 666 ); 81 | } 82 | } 83 | } 84 | CloseHandle( pQueue->hThreadTermEvent ); 85 | CloseHandle( pQueue->hThreadWakeEvent ); 86 | pQueue->hThreadTermEvent = NULL; 87 | pQueue->hThreadWakeEvent = NULL; 88 | pQueue->iThreadCount = 0; 89 | 90 | // Queue 91 | ///QueueLock( pQueue ); 92 | QueueReset( pQueue ); 93 | DeleteCriticalSection( &pQueue->csLock ); 94 | 95 | // Name 96 | TRACE( _T( " QueueDestroy(%s)\n" ), pQueue->szName ); 97 | *pQueue->szName = _T( '\0' ); 98 | } 99 | 100 | VOID QueueNewThread(_Inout_ PQUEUE pQueue) 101 | { 102 | ULONG availableThreads = pQueue->iThreadCount - InterlockedCompareExchange(&pQueue->iThreadBusyCount, -1, -1); 103 | if (availableThreads == 0 && pQueue->iThreadCount < pQueue->iThreadMaxCount) 104 | { 105 | PTHREAD pThread = &pQueue->pThreads[pQueue->iThreadCount]; 106 | pThread->pQueue = pQueue; 107 | pThread->hTermEvent = pQueue->hThreadTermEvent; 108 | pThread->hWakeEvent = pQueue->hThreadWakeEvent; 109 | wnsprintf(pThread->szName, ARRAYSIZE(pThread->szName), _T("%s%02d"), pQueue->szName, pQueue->iThreadCount); 110 | pThread->hThread = CreateThread(NULL, 0, (LPTHREAD_START_ROUTINE)ThreadProc, pThread, 0, &pThread->iTID); 111 | assert(pThread->hThread); 112 | if (pThread->hThread) { 113 | pQueue->iThreadCount++; 114 | TRACE(_T(" Th:%s create. %u running threads\n"), pThread->szName, pQueue->iThreadCount); 115 | } else { 116 | DWORD err = GetLastError(); 117 | TRACE(_T(" [!] CreateThread(%s) == 0x%x\n"), pThread->szName, err); 118 | MyZeroMemory(pThread, sizeof(*pThread)); 119 | } 120 | } 121 | } 122 | 123 | VOID QueueLock( _Inout_ PQUEUE pQueue ) 124 | { 125 | assert( pQueue ); 126 | EnterCriticalSection( &pQueue->csLock ); 127 | TRACE2( _T( " QueueLock(%s)\n" ), pQueue->szName ); 128 | } 129 | 130 | VOID QueueUnlock( _Inout_ PQUEUE pQueue ) 131 | { 132 | assert( pQueue ); 133 | LeaveCriticalSection( &pQueue->csLock ); 134 | TRACE2( _T( " QueueUnlock(%s)\n" ), pQueue->szName ); 135 | } 136 | 137 | BOOL QueueReset( _Inout_ PQUEUE pQueue ) 138 | { 139 | BOOL bRet = TRUE; 140 | assert( pQueue ); 141 | TRACE( _T( " QueueReset(%s)\n" ), pQueue->szName ); 142 | while ( pQueue->pHead ) 143 | bRet = bRet && QueueRemove( pQueue, pQueue->pHead ); 144 | pQueue->iLastId = 0; 145 | return bRet; 146 | } 147 | 148 | PQUEUE_REQUEST QueueFind( _Inout_ PQUEUE pQueue, _In_ ULONG iReqID ) 149 | { 150 | PQUEUE_REQUEST pReq; 151 | assert( pQueue ); 152 | for (pReq = pQueue->pHead; pReq && pReq->iId != iReqID; pReq = pReq->pNext); 153 | TRACE2( _T( " QueueFind(%s, ID:%u) == 0x%p\n" ), pQueue->szName, iReqID, pReq ); 154 | return pReq; 155 | } 156 | 157 | PQUEUE_REQUEST QueueFindNextWaiting( _Inout_ PQUEUE pQueue ) 158 | { 159 | // NOTES: 160 | // New requests are always added to the front of the queue. The first (chronologically) waiting request is the last one 161 | // We'll select the request with the highest priority (Note: lower value means higher priority) 162 | 163 | PQUEUE_REQUEST pReq, pSelectedReq = NULL; 164 | ULONG iSelectedPrio = ULONG_MAX - 1; 165 | assert( pQueue ); 166 | for (pReq = pQueue->pHead; pReq; pReq = pReq->pNext) { 167 | if (pReq->iStatus == REQUEST_STATUS_WAITING && !pReq->bAbort && pReq->iPriority <= iSelectedPrio) { 168 | if (pReq->iDependId > 0) { 169 | PQUEUE_REQUEST pReqDepend = QueueFind( pQueue, pReq->iDependId ); 170 | if (!pReqDepend || pReqDepend->iStatus != REQUEST_STATUS_DONE) 171 | continue; /// This request depends on another that's not finished 172 | } 173 | pSelectedReq = pReq; 174 | iSelectedPrio = pReq->iPriority; 175 | } 176 | } 177 | TRACE2( _T( " QueueFindNextWaiting(%s) == ID:%u, Prio:%u, Ptr:0x%p\n" ), pQueue->szName, pSelectedReq ? pSelectedReq->iId : 0, pSelectedReq ? pSelectedReq->iPriority : 0, pSelectedReq ); 178 | return pSelectedReq; 179 | } 180 | 181 | BOOL QueueAdd( 182 | _Inout_ PQUEUE pQueue, 183 | _In_ PQUEUE_REQUEST_PARAM pParam, 184 | _Out_opt_ PQUEUE_REQUEST *ppReq 185 | ) 186 | { 187 | BOOL bRet = TRUE; 188 | assert( pQueue ); 189 | assert( pParam ); 190 | if (pParam->pszURL && *pParam->pszURL && ((pParam->iLocalType != REQUEST_LOCAL_FILE) || (pParam->pszLocalFile && *pParam->pszLocalFile))) { 191 | 192 | PQUEUE_REQUEST pReq = (PQUEUE_REQUEST)MyAlloc( sizeof( QUEUE_REQUEST ) ); 193 | if (pReq) { 194 | 195 | MyZeroMemory( pReq, sizeof( *pReq ) ); 196 | pReq->iId = ++pQueue->iLastId; 197 | pReq->iPriority = (pParam->iPriority == DEFAULT_VALUE) ? DEFAULT_PRIORITY : pParam->iPriority; 198 | pReq->iDependId = pParam->iDependId; 199 | pReq->iStatus = REQUEST_STATUS_WAITING; 200 | pReq->pQueue = pQueue; 201 | 202 | MyStrDup( pReq->pszURL, pParam->pszURL ); 203 | 204 | pReq->iLocalType = pParam->iLocalType; 205 | if (pReq->iLocalType == REQUEST_LOCAL_FILE) 206 | MyStrDup( pReq->Local.pszFile, pParam->pszLocalFile ); 207 | 208 | if (pParam->pszProxy && *pParam->pszProxy) { 209 | MyStrDup( pReq->pszProxy, pParam->pszProxy ); 210 | } 211 | if (pParam->pszProxyUser && *pParam->pszProxyUser) { 212 | MyStrDup( pReq->pszProxyUser, pParam->pszProxyUser ); 213 | } 214 | if (pParam->pszProxyPass && *pParam->pszProxyPass) { 215 | MyStrDup( pReq->pszProxyPass, pParam->pszProxyPass ); 216 | } 217 | 218 | if (pParam->pszMethod && *pParam->pszMethod) { 219 | lstrcpyn( pReq->szMethod, pParam->pszMethod, ARRAYSIZE( pReq->szMethod ) ); 220 | } else { 221 | lstrcpy( pReq->szMethod, _T( "GET" ) ); /// Default 222 | } 223 | if (pParam->pszHeaders && *pParam->pszHeaders) { 224 | MyStrDup( pReq->pszHeaders, pParam->pszHeaders ); 225 | } 226 | if (pParam->pData && (pParam->iDataSize > 0)) { 227 | MyDataDup( pReq->pData, pParam->pData, pParam->iDataSize ); 228 | pReq->iDataSize = pParam->iDataSize; 229 | } 230 | pReq->iTimeoutConnect = pParam->iTimeoutConnect; 231 | pReq->iTimeoutReconnect = pParam->iTimeoutReconnect; 232 | pReq->iOptConnectRetries = pParam->iOptConnectRetries; 233 | pReq->iOptConnectTimeout = pParam->iOptConnectTimeout; 234 | pReq->iOptReceiveTimeout = pParam->iOptReceiveTimeout; 235 | pReq->iOptSendTimeout = pParam->iOptSendTimeout; 236 | if (pParam->pszReferrer && *pParam->pszReferrer) { 237 | MyStrDup( pReq->pszReferer, pParam->pszReferrer ); 238 | } 239 | pReq->iHttpInternetFlags = pParam->iHttpInternetFlags; 240 | pReq->iHttpSecurityFlags = pParam->iHttpSecurityFlags; 241 | 242 | GetLocalFileTime( &pReq->tmEnqueue ); 243 | 244 | pReq->iWin32Error = ERROR_SUCCESS; 245 | pReq->pszWin32Error = NULL; 246 | AllocErrorStr( pReq->iWin32Error, &pReq->pszWin32Error ); 247 | 248 | pReq->iHttpStatus = 0; 249 | pReq->pszHttpStatus = NULL; 250 | MyStrDup( pReq->pszHttpStatus, _T( "N/A" ) ); 251 | 252 | // Add to the front 253 | pReq->pNext = pQueue->pHead; 254 | pQueue->pHead = pReq; 255 | 256 | // Return 257 | if (ppReq) 258 | *ppReq = pReq; 259 | 260 | // Wake up one worker thread 261 | SetEvent( pQueue->hThreadWakeEvent ); 262 | 263 | // Create a worker thread if necessary 264 | QueueNewThread(pQueue); 265 | 266 | TRACE( 267 | _T( " QueueAdd(%s, ID:%u, %s %s -> %s, Prio:%u, DependsOn:%d)\n" ), 268 | pQueue->szName, 269 | pReq->iId, 270 | pReq->szMethod, 271 | pReq->pszURL, 272 | pReq->iLocalType == REQUEST_LOCAL_NONE ? TEXT_LOCAL_NONE : (pReq->iLocalType == REQUEST_LOCAL_FILE ? pReq->Local.pszFile : TEXT_LOCAL_MEMORY), 273 | pReq->iPriority, pReq->iDependId 274 | ); 275 | 276 | } else { 277 | bRet = FALSE; 278 | } 279 | } else { 280 | bRet = FALSE; 281 | } 282 | return bRet; 283 | } 284 | 285 | 286 | BOOL QueueRemove( _Inout_ PQUEUE pQueue, _In_ PQUEUE_REQUEST pReq ) 287 | { 288 | BOOL bRet = TRUE; 289 | assert( pQueue ); 290 | if (pReq) { 291 | 292 | TRACE( 293 | _T( " QueueRemove(%s, ID:%u, Err:%u \"%s\", St:%s, Speed:%s, Time:%ums, Drops:%u, %s %s -> %s)\n" ), 294 | pQueue->szName, 295 | pReq->iId, 296 | pReq->iWin32Error != ERROR_SUCCESS ? pReq->iWin32Error : pReq->iHttpStatus, 297 | pReq->iWin32Error != ERROR_SUCCESS ? pReq->pszWin32Error : pReq->pszHttpStatus, 298 | pReq->iStatus == REQUEST_STATUS_WAITING ? TEXT_STATUS_WAITING : (pReq->iStatus == REQUEST_STATUS_DOWNLOADING ? TEXT_STATUS_DOWNLOADING : TEXT_STATUS_COMPLETED), 299 | pReq->Speed.szSpeed, 300 | MyTimeDiff( &pReq->tmDisconnect, &pReq->tmConnect ), 301 | pReq->iConnectionDrops, 302 | pReq->szMethod, 303 | pReq->pszURL, 304 | pReq->iLocalType == REQUEST_LOCAL_NONE ? TEXT_LOCAL_NONE : (pReq->iLocalType == REQUEST_LOCAL_FILE ? pReq->Local.pszFile : TEXT_LOCAL_MEMORY) 305 | ); 306 | 307 | // Free request's content 308 | MyFree( pReq->pszURL ); 309 | MyFree( pReq->pszProxy ); 310 | MyFree( pReq->pszProxyUser ); 311 | MyFree( pReq->pszProxyPass ); 312 | MyFree( pReq->pszHeaders ); 313 | MyFree( pReq->pData ); 314 | MyFree( pReq->pszSrvHeaders ); 315 | MyFree( pReq->pszSrvIP ); 316 | MyFree( pReq->pszWin32Error ); 317 | MyFree( pReq->pszHttpStatus ); 318 | MyFree( pReq->pszReferer ); 319 | 320 | switch (pReq->iLocalType) 321 | { 322 | case REQUEST_LOCAL_FILE: 323 | MyFree( pReq->Local.pszFile ); 324 | if (pReq->Local.hFile != NULL && pReq->Local.hFile != INVALID_HANDLE_VALUE) 325 | CloseHandle( pReq->Local.hFile ); 326 | break; 327 | case REQUEST_LOCAL_MEMORY: 328 | VirtualFree(pReq->Local.pMemory, 0, MEM_RELEASE); 329 | break; 330 | case REQUEST_LOCAL_NONE: 331 | break; 332 | } 333 | 334 | // Remove from list 335 | { 336 | PQUEUE_REQUEST pPrevReq; 337 | for (pPrevReq = pQueue->pHead; (pPrevReq != NULL) && (pPrevReq->pNext != pReq); pPrevReq = pPrevReq->pNext); 338 | if (pPrevReq) { 339 | pPrevReq->pNext = pReq->pNext; 340 | } else { 341 | pQueue->pHead = pReq->pNext; 342 | } 343 | } 344 | 345 | // Destroy the request 346 | MyFree( pReq ); 347 | } 348 | else { 349 | bRet = FALSE; 350 | } 351 | return bRet; 352 | } 353 | 354 | 355 | BOOL QueueAbort( _In_ PQUEUE pQueue, _In_ PQUEUE_REQUEST pReq, _In_opt_ DWORD dwWaitMS ) 356 | { 357 | BOOL bRet = TRUE; 358 | assert( pQueue ); 359 | if (pReq) { 360 | 361 | TRACE( 362 | _T( " QueueAbort(%s, ID:%u, Prio:%u, St:%s, %s %s -> %s)\n" ), 363 | pQueue->szName, 364 | pReq->iId, pReq->iPriority, 365 | pReq->iStatus == REQUEST_STATUS_WAITING ? TEXT_STATUS_WAITING : (pReq->iStatus == REQUEST_STATUS_DOWNLOADING ? TEXT_STATUS_DOWNLOADING : TEXT_STATUS_COMPLETED), 366 | pReq->szMethod, 367 | pReq->pszURL, 368 | pReq->iLocalType == REQUEST_LOCAL_NONE ? TEXT_LOCAL_NONE : (pReq->iLocalType == REQUEST_LOCAL_FILE ? pReq->Local.pszFile : TEXT_LOCAL_MEMORY) 369 | ); 370 | 371 | switch (pReq->iStatus) { 372 | case REQUEST_STATUS_WAITING: 373 | pReq->bAbort = TRUE; 374 | pReq->iStatus = REQUEST_STATUS_DONE; 375 | /// Error code 376 | pReq->iWin32Error = ERROR_INTERNET_OPERATION_CANCELLED; 377 | MyFree( pReq->pszWin32Error ); 378 | AllocErrorStr( pReq->iWin32Error, &pReq->pszWin32Error ); 379 | break; 380 | case REQUEST_STATUS_DOWNLOADING: 381 | pReq->bAbort = TRUE; 382 | /// The worker thread will do the rest... 383 | if (dwWaitMS) { 384 | /// Wait until it'll finish the job 385 | const DWORD dwAbortWait = 100; 386 | DWORD dwAbortElapsed = 0; 387 | while ((pReq->iStatus == REQUEST_STATUS_DOWNLOADING) && (dwAbortElapsed < dwWaitMS)) { 388 | Sleep( dwAbortWait ); 389 | dwAbortElapsed += dwAbortWait; 390 | } 391 | } 392 | break; 393 | case REQUEST_STATUS_DONE: 394 | /// Nothing to do 395 | break; 396 | default: assert( !"Unknown request status" ); 397 | } 398 | 399 | } else { 400 | bRet = FALSE; 401 | } 402 | return bRet; 403 | } 404 | 405 | 406 | ULONG QueueSize( _Inout_ PQUEUE pQueue ) 407 | { 408 | PQUEUE_REQUEST pReq; 409 | ULONG iSize; 410 | assert( pQueue ); 411 | for (pReq = pQueue->pHead, iSize = 0; pReq; pReq = pReq->pNext, iSize++); 412 | TRACE2( _T( " QueueSize(%s) == %u\n" ), pQueue->szName, iSize ); 413 | return iSize; 414 | } 415 | 416 | 417 | ULONG RequestOptimalBufferSize( _In_ PQUEUE_REQUEST pReq ) 418 | { 419 | ULONG iSize; 420 | assert( pReq ); 421 | 422 | if (pReq->Speed.iSpeed > 0) { 423 | /// Use already computed speed (bps) 424 | iSize = pReq->Speed.iSpeed; 425 | } else { 426 | /// Estimate speed based on what's been received so far 427 | ULONG iMS = GetTickCount() - pReq->Speed.iChunkTime; 428 | if (iMS > 10) { 429 | iSize = (pReq->Speed.iChunkSize / iMS) * 1000; 430 | } else { 431 | iSize = MIN_BUFFER_SIZE; 432 | } 433 | } 434 | 435 | iSize -= iSize % 1024; /// Align to kilobyte 436 | iSize *= 4; /// Allow the speed to grow. Accommodate more seconds' data... 437 | iSize = __max( iSize, MIN_BUFFER_SIZE ); 438 | iSize = __min( iSize, MAX_BUFFER_SIZE ); 439 | if (pReq->iLocalType == REQUEST_LOCAL_MEMORY) { 440 | iSize = __min( iSize, (ULONG)((ULONG64)MAX_MEMORY_CONTENT_LENGTH - pReq->iRecvSize) ); 441 | } 442 | 443 | /* TRACE( 444 | _T( "[BufSize = %.4u KB] Speed = %u bps, ChunkTime = %u (%u ms), ChunkSize = %u\n" ), 445 | iSize / 1024, 446 | pReq->Speed.iSpeed, 447 | pReq->Speed.iChunkTime, GetTickCount() - pReq->Speed.iChunkTime, 448 | pReq->Speed.iChunkSize 449 | );*/ 450 | return iSize; 451 | } 452 | 453 | 454 | BOOL RequestMemoryToString( _In_ PQUEUE_REQUEST pReq, _Out_ LPTSTR pszString, _In_ ULONG iStringLen ) 455 | { 456 | BOOL bRet = FALSE; 457 | if (pReq && pszString && iStringLen) { 458 | pszString[0] = _T( '\0' ); 459 | if (pReq->iLocalType == REQUEST_LOCAL_MEMORY) { 460 | if (pReq->iStatus == REQUEST_STATUS_DONE) { 461 | if (pReq->iWin32Error == ERROR_SUCCESS && (pReq->iHttpStatus >= 200 && pReq->iHttpStatus < 300)) { 462 | if (pReq->Local.pMemory && pReq->iRecvSize > 0) { 463 | BinaryToString( pReq->Local.pMemory, (ULONG)pReq->iRecvSize, pszString, iStringLen ); 464 | bRet = TRUE; 465 | } 466 | } 467 | } 468 | } 469 | } 470 | return bRet; 471 | } 472 | 473 | 474 | BOOL RequestDataToString( _In_ PQUEUE_REQUEST pReq, _Out_ LPTSTR pszString, _In_ ULONG iStringLen ) 475 | { 476 | BOOL bRet = FALSE; 477 | if (pReq && pszString && iStringLen) { 478 | BinaryToString( pReq->pData, pReq->iDataSize, pszString, iStringLen ); 479 | bRet = TRUE; 480 | } 481 | return bRet; 482 | } 483 | 484 | 485 | int QueueWakeThreads( _In_ PQUEUE pQueue, _In_ ULONG iThreadsToWake ) 486 | { 487 | int iCount = 0; 488 | 489 | assert( pQueue ); 490 | if (iThreadsToWake > 0) { 491 | 492 | ULONG i, n; 493 | 494 | /// Number of threads 495 | n = __min( iThreadsToWake, pQueue->iThreadCount ); 496 | 497 | /// Determine whether we're already running in a worker thread 498 | for (i = 0; i < pQueue->iThreadCount; i++) { 499 | if (pQueue->pThreads[i].iTID == GetCurrentThreadId()) { 500 | n--; /// One less thread to wake up 501 | break; 502 | } 503 | } 504 | 505 | /// Wake 506 | for (i = 0; i < n; i++) 507 | SetEvent( pQueue->hThreadWakeEvent ); 508 | iCount += n; 509 | } 510 | 511 | return iCount; 512 | } 513 | -------------------------------------------------------------------------------- /queue.h: -------------------------------------------------------------------------------- 1 | 2 | //? Marius Negrutiu (marius.negrutiu@protonmail.com) :: 2014/02/02 3 | 4 | #pragma once 5 | #include "thread.h" 6 | 7 | #define KILOBYTES(n) ((n) * 1024) 8 | #define MEGABYTES(n) ((n) * 1024 * 1024) 9 | 10 | #define MIN_WORKER_THREADS 2 11 | #define MAX_WORKER_THREADS 20 12 | #define INVALID_FILE_SIZE64 ((ULONG64)-1) 13 | #define DEFAULT_VALUE ((ULONG)-1) 14 | #define DEFAULT_PRIORITY 1000 15 | #define MIN_BUFFER_SIZE KILOBYTES(2) 16 | #define MAX_BUFFER_SIZE MEGABYTES(2) 17 | #define MAX_MEMORY_CONTENT_LENGTH KILOBYTES(16) // The NSIS script can only retrieve max NSIS_MAX_STRLEN characters (usually 4K, sometimes 8K...) 18 | #define ANY_REQUEST_ID 0 19 | #define ANY_PRIORITY 0 20 | #define ANY_STATUS ((REQUEST_STATUS)-1) 21 | 22 | #define TEXT_USERAGENT _T( "xfer/1.0" ) 23 | #define TEXT_STATUS_WAITING _T( "Waiting" ) 24 | #define TEXT_STATUS_DOWNLOADING _T( "Downloading" ) 25 | #define TEXT_STATUS_COMPLETED _T( "Completed" ) 26 | #define TEXT_LOCAL_NONE _T( "None" ) 27 | #define TEXT_LOCAL_FILE _T( "File" ) 28 | #define TEXT_LOCAL_MEMORY _T( "Memory" ) 29 | #define TEXT_PER_SECOND _T( "/s" ) 30 | 31 | /* 32 | About queues: 33 | - A queue manages the list of transfer requests, and a pool of worker threads 34 | - Each transfer request waits in queue (REQUEST_STATUS_WAITING) until a worker thread becomes available (REQUEST_STATUS_DOWNLOADING) 35 | - After completion, the request will remain in queue (REQUEST_STATUS_DONE) until explicitly removed 36 | - QueueClear(...) will empty the queue. All completed transfers will be forgotten. 37 | */ 38 | 39 | typedef enum { 40 | REQUEST_STATUS_WAITING, /// The request is waiting in queue. Not downloaded yet 41 | REQUEST_STATUS_DOWNLOADING, /// The request is being downloaded 42 | REQUEST_STATUS_DONE /// The request has been downloaded (successful or not) 43 | } REQUEST_STATUS; 44 | 45 | typedef enum { 46 | REQUEST_LOCAL_NONE, /// The remote content will not be downloaded. Useful to simply connect (HTTP GET) and disconnect instantly 47 | REQUEST_LOCAL_FILE, /// The remote content will be downloaded to a local file 48 | REQUEST_LOCAL_MEMORY /// The remote content will be downloaded to a memory 49 | } REQUEST_LOCAL_TYPE; 50 | 51 | 52 | //++ struct QUEUE_REQUEST_PARAM 53 | typedef struct _QUEUE_REQUEST_PARAM { 54 | ULONG iPriority; /// can be DEFAULT_VALUE 55 | ULONG iDependId; /// can be DEFAULT_VALUE 56 | LPCTSTR pszURL; 57 | REQUEST_LOCAL_TYPE iLocalType; 58 | LPCTSTR pszLocalFile; 59 | LPCTSTR pszProxy; /// can be NULL 60 | LPCTSTR pszProxyUser; /// can be NULL 61 | LPCTSTR pszProxyPass; /// can be NULL 62 | LPCTSTR pszMethod; /// can be NULL 63 | LPCTSTR pszHeaders; /// can be NULL 64 | LPVOID pData; /// can be NULL 65 | ULONG iDataSize; /// can be 0 66 | ULONG iTimeoutConnect; /// can be DEFAULT_VALUE 67 | ULONG iTimeoutReconnect; /// can be DEFAULT_VALUE 68 | ULONG iOptConnectRetries; /// can be DEFAULT_VALUE 69 | ULONG iOptConnectTimeout; /// can be DEFAULT_VALUE 70 | ULONG iOptReceiveTimeout; /// can be DEFAULT_VALUE 71 | ULONG iOptSendTimeout; /// can be DEFAULT_VALUE 72 | LPCTSTR pszReferrer; /// can be NULL 73 | ULONG iHttpInternetFlags; /// can be DEFAULT_VALUE 74 | ULONG iHttpSecurityFlags; /// can be DEFAULT_VALUE 75 | } QUEUE_REQUEST_PARAM, *PQUEUE_REQUEST_PARAM; 76 | 77 | #define RequestParamInit(Param) \ 78 | MyZeroMemory( &Param, sizeof( Param ) ); \ 79 | Param.iPriority = DEFAULT_VALUE; \ 80 | Param.iTimeoutConnect = Param.iTimeoutReconnect = DEFAULT_VALUE; \ 81 | Param.iOptConnectRetries = Param.iOptConnectTimeout = Param.iOptReceiveTimeout = Param.iOptSendTimeout= DEFAULT_VALUE; \ 82 | Param.iHttpInternetFlags = Param.iHttpSecurityFlags = DEFAULT_VALUE; 83 | 84 | #define RequestParamDestroy(Param) \ 85 | MyFree( Param.pszMethod ); \ 86 | MyFree( Param.pszURL ); \ 87 | MyFree( Param.pszLocalFile ); \ 88 | MyFree( Param.pszHeaders ); \ 89 | MyFree( Param.pData ); \ 90 | MyFree( Param.pszProxy ); \ 91 | MyFree( Param.pszProxyUser ); \ 92 | MyFree( Param.pszProxyPass ); \ 93 | MyFree( Param.pszReferrer ); \ 94 | MyZeroMemory( &Param, sizeof( Param ) ); 95 | 96 | 97 | //++ struct QUEUE_REQUEST 98 | typedef struct _QUEUE_REQUEST { 99 | 100 | ULONG iId; /// Unique request ID 101 | REQUEST_STATUS iStatus; 102 | ULONG iPriority; /// 0 (high prio) -> ULONG_MAX-1 (low prio) 103 | ULONG iDependId; 104 | 105 | // Related objects 106 | struct _QUEUE *pQueue; 107 | struct _THREAD *pThread; 108 | 109 | // Source 110 | LPTSTR pszURL; 111 | 112 | LPTSTR pszProxy; /// CERN type proxies (ex: "HTTP=http://my_http_proxy:port HTTPS=https://my_https_proxy:port") 113 | LPTSTR pszProxyUser; 114 | LPTSTR pszProxyPass; 115 | 116 | // Destination 117 | REQUEST_LOCAL_TYPE iLocalType; 118 | union { 119 | struct { 120 | LPTSTR pszFile; /// Valid for REQUEST_LOCAL_FILE 121 | HANDLE hFile; 122 | }; 123 | LPBYTE pMemory; /// Valid for REQUEST_LOCAL_MEMORY. Reserves MAX_MEMORY_CONTENT_LENGTH of virtual memory 124 | } Local; 125 | 126 | // Transfer options 127 | TCHAR szMethod[20]; /// GET, POST, HEAD, etc. Default is GET 128 | LPTSTR pszHeaders; /// Additional HTTP headers sent by HttpSendRequest 129 | LPVOID pData; /// Additional data sent by HttpSendRequest (useful for POST) 130 | ULONG iDataSize; 131 | ULONG iTimeoutConnect; /// Keep trying to connect for X ms. Default is 0 132 | ULONG iTimeoutReconnect; /// Keep trying to reconnect for X ms when the connection drops while downloading. Default is 0 133 | ULONG iOptConnectRetries; /// InternetSetOption( INTERNET_OPTION_CONNECT_RETRIES ). Relevant only for hosts with multiple IPs! 134 | ULONG iOptConnectTimeout; /// InternetSetOption( INTERNET_OPTION_CONNECT_TIMEOUT ) 135 | ULONG iOptReceiveTimeout; /// InternetSetOption( INTERNET_OPTION_RECEIVE_TIMEOUT ) 136 | ULONG iOptSendTimeout; /// InternetSetOption( INTERNET_OPTION_SEND_TIMEOUT ) 137 | LPTSTR pszReferer; /// Sent to HttpOpenRequest. Default is NULL 138 | ULONG iHttpInternetFlags; /// INTERNET_FLAG_XXX. Default is INTERNET_FLAG_NO_CACHE_WRITE|INTERNET_FLAG_IGNORE_CERT_DATE_INVALID|INTERNET_FLAG_NO_COOKIES|INTERNET_FLAG_NO_UI|INTERNET_FLAG_RELOAD 139 | ULONG iHttpSecurityFlags; /// SECURITY_FLAG_XXX. Default is SECURITY_FLAG_IGNORE_REVOCATION|SECURITY_FLAG_IGNORE_CERT_DATE_INVALID 140 | 141 | // Runtime statistics 142 | FILETIME tmEnqueue; /// Enque timestamp 143 | FILETIME tmConnect; /// Connect timestamp 144 | FILETIME tmDisconnect; /// Disconnect timestamp 145 | 146 | ULONG64 iFileSize; /// File size or -1 if not available 147 | ULONG64 iRecvSize; /// Received bytes 148 | 149 | struct { 150 | FILETIME tmStart; /// Last transfer (loop) startup time 151 | FILETIME tmEnd; /// Last transfer (loop) finish time 152 | ULONG64 iXferSize; /// Last transfer (loop) data size 153 | } Xfer; 154 | 155 | struct { 156 | ULONG iSpeed; /// Transfer speed (bytes/sec) 157 | TCHAR szSpeed[30]; /// Transfer speed nicely formatted ("255.4 KB/s", "2 MB/s", etc) 158 | ULONG iChunkTime; /// Tick count 159 | ULONG iChunkSize; /// Bytes 160 | } Speed; 161 | 162 | ULONG iConnectionDrops; /// Number of connections dropped out 163 | 164 | // Runtime variables 165 | BOOLEAN bAbort; /// Aborted by user 166 | HINTERNET hSession; /// InternetOpen 167 | HINTERNET hConnect; /// InternetConnect 168 | HINTERNET hRequest; /// HttpOpenRequest 169 | BOOLEAN bUsingRanges; /// The HTTP "Range" header is in use (supported by server) 170 | ULONG iLastCallbackStatus; /// Last status received in InternetStatusCallback 171 | LPCTSTR pszSrvIP; /// Servers resolved IP address (as string) 172 | LPCTSTR pszSrvHeaders; /// HTTP headers received from server 173 | BOOLEAN bConnected; /// Received INTERNET_STATUS_CONNECTED_TO_SERVER 174 | 175 | // Error code (Win32 and HTTP) 176 | ULONG iWin32Error; /// Last Win32 error code 177 | LPTSTR pszWin32Error; /// Last Win32 error code (as string) 178 | ULONG iHttpStatus; /// Last HTTP status code 179 | LPTSTR pszHttpStatus; /// Last HTTP status code (as string) 180 | 181 | struct _QUEUE_REQUEST *pNext; /// Singly linked list 182 | 183 | } QUEUE_REQUEST, *PQUEUE_REQUEST; 184 | 185 | 186 | #define RequestReconnectionAllowed(pReq) \ 187 | ((pReq)->iTimeoutReconnect != DEFAULT_VALUE) && \ 188 | ((pReq)->iTimeoutReconnect > 0) 189 | 190 | #define RequestRecvPercent(pReq) \ 191 | (int)(((pReq)->iFileSize == 0 || (pReq)->iFileSize == INVALID_FILE_SIZE64) ? 0 : MyMulDiv64((pReq)->iRecvSize, 100, (pReq)->iFileSize)) 192 | 193 | ULONG RequestOptimalBufferSize( _In_ PQUEUE_REQUEST pReq ); 194 | 195 | 196 | #define RequestMatched(pReq, ID, Prio, Status) \ 197 | (ID == ANY_REQUEST_ID || ID == pReq->iId) && \ 198 | (Prio == ANY_PRIORITY || Prio == pReq->iPriority) && \ 199 | (Status == ANY_STATUS || Status == pReq->iStatus) 200 | 201 | BOOL RequestMemoryToString( _In_ PQUEUE_REQUEST pReq, _Out_ LPTSTR pszString, _In_ ULONG iStringLen ); 202 | BOOL RequestDataToString( _In_ PQUEUE_REQUEST pReq, _Out_ LPTSTR pszString, _In_ ULONG iStringLen ); 203 | 204 | 205 | //++ struct QUEUE 206 | typedef struct _QUEUE { 207 | 208 | TCHAR szName[20]; /// Queue name. The default queue will be named MAIN 209 | 210 | // Queue 211 | CRITICAL_SECTION csLock; 212 | PQUEUE_REQUEST pHead; 213 | ULONG iLastId; 214 | 215 | // Worker threads 216 | THREAD pThreads[MAX_WORKER_THREADS]; 217 | ULONG iThreadCount; // Current thread count 218 | volatile LONG iThreadBusyCount; // Busy thread count 219 | ULONG iThreadMaxCount; // Maximum thread count 220 | HANDLE hThreadTermEvent; 221 | HANDLE hThreadWakeEvent; 222 | 223 | } QUEUE, *PQUEUE; 224 | 225 | 226 | // Initializing 227 | VOID QueueInitialize( _Inout_ PQUEUE pQueue, _In_ LPCTSTR szName, _In_ int iThreadCount ); 228 | VOID QueueDestroy( _Inout_ PQUEUE pQueue ); 229 | 230 | // Queue locking 231 | VOID QueueLock( _Inout_ PQUEUE pQueue ); 232 | VOID QueueUnlock( _Inout_ PQUEUE pQueue ); 233 | 234 | 235 | // Clear the queue, destroy everything 236 | // The queue must be locked 237 | BOOL QueueReset( _Inout_ PQUEUE pQueue ); 238 | 239 | // Find a request in the queue 240 | // The queue must be locked 241 | PQUEUE_REQUEST QueueFind( _Inout_ PQUEUE pQueue, _In_ ULONG iReqID ); /// ...by ID 242 | PQUEUE_REQUEST QueueFindNextWaiting( _Inout_ PQUEUE pQueue ); /// ...by status 243 | 244 | // Add a new request in the queue 245 | // The queue must be locked 246 | BOOL QueueAdd( 247 | _Inout_ PQUEUE pQueue, 248 | _In_ PQUEUE_REQUEST_PARAM pParam, 249 | _Out_opt_ PQUEUE_REQUEST *ppReq 250 | ); 251 | 252 | // Remove a request from the queue and destroy it 253 | // The queue must be locked 254 | BOOL QueueRemove( _Inout_ PQUEUE pQueue, _In_ PQUEUE_REQUEST pReq ); 255 | 256 | // Abort a request 257 | // The queue must be locked 258 | // This routine will only schedule the request for abortion. It'll be aborted asynchronously by its worker thread. 259 | // By specifying a wait period, the routine will wait until the request is actually aborted... 260 | BOOL QueueAbort( _In_ PQUEUE pQueue, _In_ PQUEUE_REQUEST pReq, _In_opt_ DWORD dwWaitMS ); 261 | 262 | // Retrieve the queue size 263 | // The queue must be locked 264 | ULONG QueueSize( _Inout_ PQUEUE pQueue ); 265 | 266 | // Wake up one or more worker threads 267 | // Returns the number of threads woken up 268 | // The queue must be locked 269 | int QueueWakeThreads( _In_ PQUEUE pQueue, _In_ ULONG iThreadsToWake ); 270 | -------------------------------------------------------------------------------- /resource.h: -------------------------------------------------------------------------------- 1 | //{{NO_DEPENDENCIES}} 2 | // Microsoft Visual C++ generated include file. 3 | // Used by resource.rc 4 | // 5 | #define IDD_POPUP 101 6 | #define IDD_CONFIRMATION 102 7 | #define IDC_STATIC_TEXT 1001 8 | #define IDC_POPUP_PROGRESS 1002 9 | #define IDC_POPUP_ICON 1003 10 | #define IDC_POPUP_STATUS 1004 11 | 12 | // Next default values for new objects 13 | // 14 | #ifdef APSTUDIO_INVOKED 15 | #ifndef APSTUDIO_READONLY_SYMBOLS 16 | #define _APS_NEXT_RESOURCE_VALUE 103 17 | #define _APS_NEXT_COMMAND_VALUE 40001 18 | #define _APS_NEXT_CONTROL_VALUE 1005 19 | #define _APS_NEXT_SYMED_VALUE 101 20 | #endif 21 | #endif 22 | -------------------------------------------------------------------------------- /resource.rc: -------------------------------------------------------------------------------- 1 | // Microsoft Visual C++ generated resource script. 2 | // 3 | #include "resource.h" 4 | 5 | #define APSTUDIO_READONLY_SYMBOLS 6 | ///////////////////////////////////////////////////////////////////////////// 7 | // 8 | // Generated from the TEXTINCLUDE 2 resource. 9 | // 10 | #include 11 | 12 | ///////////////////////////////////////////////////////////////////////////// 13 | #undef APSTUDIO_READONLY_SYMBOLS 14 | 15 | ///////////////////////////////////////////////////////////////////////////// 16 | // English (United States) resources 17 | 18 | #if !defined(AFX_RESOURCE_DLL) || defined(AFX_TARG_ENU) 19 | LANGUAGE LANG_ENGLISH, SUBLANG_ENGLISH_US 20 | 21 | #ifdef APSTUDIO_INVOKED 22 | ///////////////////////////////////////////////////////////////////////////// 23 | // 24 | // TEXTINCLUDE 25 | // 26 | 27 | 1 TEXTINCLUDE 28 | BEGIN 29 | "resource.h\0" 30 | END 31 | 32 | 2 TEXTINCLUDE 33 | BEGIN 34 | "#include \r\n" 35 | "\0" 36 | END 37 | 38 | 3 TEXTINCLUDE 39 | BEGIN 40 | "\r\n" 41 | "\0" 42 | END 43 | 44 | #endif // APSTUDIO_INVOKED 45 | 46 | 47 | ///////////////////////////////////////////////////////////////////////////// 48 | // 49 | // Version 50 | // 51 | 52 | VS_VERSION_INFO VERSIONINFO 53 | FILEVERSION 1,2023,8,4 54 | PRODUCTVERSION 1,0,0,0 55 | FILEFLAGSMASK 0x3fL 56 | #ifdef _DEBUG 57 | FILEFLAGS 0x1L 58 | #else 59 | FILEFLAGS 0x0L 60 | #endif 61 | FILEOS 0x40004L 62 | FILETYPE 0x2L 63 | FILESUBTYPE 0x0L 64 | BEGIN 65 | BLOCK "StringFileInfo" 66 | BEGIN 67 | BLOCK "040904b0" 68 | BEGIN 69 | VALUE "CompanyName", "Marius Negrutiu (marius.negrutiu@protonmail.com)" 70 | VALUE "FileDescription", "NSIS WinINet Plugin" 71 | VALUE "FileVersion", "1.2023.8.4" 72 | VALUE "InternalName", "NSxfer.dll" 73 | VALUE "LegalCopyright", "(C)2014-2023, Marius Negrutiu. All rights reserved." 74 | VALUE "LegalTrademarks", "https://github.com/negrutiu/nsis-nsxfer" 75 | VALUE "OriginalFilename", "NSxfer.dll" 76 | VALUE "ProductName", "NSIS WinINet Plugin" 77 | VALUE "ProductVersion", "1.0.0.0" 78 | END 79 | END 80 | BLOCK "VarFileInfo" 81 | BEGIN 82 | VALUE "Translation", 0x409, 1200 83 | END 84 | END 85 | 86 | 87 | ///////////////////////////////////////////////////////////////////////////// 88 | // 89 | // Dialog 90 | // 91 | 92 | IDD_POPUP DIALOGEX 0, 0, 294, 52 93 | STYLE DS_3DLOOK | DS_CENTER | DS_MODALFRAME | DS_SHELLFONT | WS_CAPTION | WS_VISIBLE | WS_GROUP | WS_POPUP | WS_SYSMENU 94 | CAPTION "Downloading..." 95 | FONT 8, "MS Shell Dlg", 400, 0, 0x1 96 | BEGIN 97 | LTEXT "", IDC_POPUP_STATUS, 48, 12, 234, 10, SS_LEFT, WS_EX_LEFT 98 | CONTROL "", IDC_POPUP_PROGRESS, "msctls_progress32", 0, 48, 26, 234, 14, WS_EX_LEFT 99 | ICON "", IDC_POPUP_ICON, 12, 16, 21, 20, SS_ICON, WS_EX_LEFT 100 | END 101 | 102 | 103 | IDD_CONFIRMATION DIALOGEX 0, 0, 237, 58 104 | STYLE DS_3DLOOK | DS_CENTER | DS_MODALFRAME | DS_SETFOREGROUND | DS_SHELLFONT | WS_CAPTION | WS_VISIBLE | WS_SYSMENU 105 | CAPTION "Abort" 106 | FONT 8, "MS Shell Dlg", 400, 0, 0x1 107 | BEGIN 108 | PUSHBUTTON "&Yes", IDYES, 66, 36, 50, 14, 0, WS_EX_LEFT 109 | PUSHBUTTON "&No", IDNO, 120, 36, 50, 14, 0, WS_EX_LEFT 110 | ICON "", IDC_POPUP_ICON, 12, 12, 21, 20, SS_ICON, WS_EX_LEFT 111 | LTEXT "Are you sure?", IDC_STATIC_TEXT, 42, 12, 186, 18, SS_LEFT, WS_EX_LEFT 112 | END 113 | 114 | ///////////////////////////////////////////////////////////////////////////// 115 | // 116 | // DESIGNINFO 117 | // 118 | 119 | #ifdef APSTUDIO_INVOKED 120 | GUIDELINES DESIGNINFO 121 | BEGIN 122 | IDD_CONFIRMATION, DIALOG 123 | BEGIN 124 | END 125 | END 126 | #endif // APSTUDIO_INVOKED 127 | 128 | #endif // English (United States) resources 129 | ///////////////////////////////////////////////////////////////////////////// 130 | 131 | 132 | 133 | #ifndef APSTUDIO_INVOKED 134 | ///////////////////////////////////////////////////////////////////////////// 135 | // 136 | // Generated from the TEXTINCLUDE 3 resource. 137 | // 138 | 139 | 140 | ///////////////////////////////////////////////////////////////////////////// 141 | #endif // not APSTUDIO_INVOKED 142 | 143 | -------------------------------------------------------------------------------- /thread.h: -------------------------------------------------------------------------------- 1 | 2 | //? Marius Negrutiu (marius.negrutiu@protonmail.com) :: 2014/02/08 3 | 4 | #pragma once 5 | 6 | typedef struct _THREAD { 7 | LPVOID pQueue; /// The QUEUE that owns this thread 8 | HANDLE hThread; /// Thread handle 9 | ULONG iTID; /// Cached TID 10 | TCHAR szName[50]; /// Thread name. Ex: MAIN01 11 | HANDLE hWakeEvent; /// Wake a thread when new requests are added to the queue 12 | HANDLE hTermEvent; /// Terminate threads 13 | } THREAD, *PTHREAD; 14 | 15 | 16 | // Thread function passed to CreateThread 17 | DWORD WINAPI ThreadProc( _In_ PTHREAD pThread ); 18 | -------------------------------------------------------------------------------- /todo.txt: -------------------------------------------------------------------------------- 1 | _________________________________________________________________________________________________ 2 | 3 | TODO: 4 | _________________________________________________________________________________________________ 5 | 6 | * Parametrized InternetOpenUrl flags 7 | * Parametrized HttpSendRequest flags 8 | * Proxy 9 | * reconnect during dld 10 | * Connect time-out parameter 11 | * Reconnect time-out parameter 12 | * Resume 13 | * Resume memory downloads as well 14 | * query file size 15 | * download file with unknown size 16 | - HTTP/HTTPS authentication? 17 | - PROXY authentication? 18 | - PUT(WebDAV) 19 | * POST headers 20 | - Multiple download sources 21 | * Support files > 4GB 22 | x Support destination folder only. Use the file name from HTTP_QUERY_CONTENT_DISPOSITION (ex: piriform downloads) => Easily done by the caller *after* transfer completes 23 | * Use InternetSetStatusCallback (remember last status, remember server IP) 24 | * Retry opening local file if ERROR_LOCK_VIOLATION 25 | x Download to temporary file => Easily done by the caller 26 | * Measure transfer speed 27 | * Save the raw headers and make them available for querying 28 | * Queue priorities 29 | * Enumerate requests in queue 30 | x Pause/Resume transfer. Create paused => Can be emulated with dependencies 31 | * Abort transfer 32 | - Attach the "Zone.Identifier" stream (optional) 33 | * Support for the NSIS script to retrieve memory content 34 | * All functions with variable number of arguments must receive /END 35 | * NSxfer::Query return time information (elapsed waiting, elapsed transferring, etc) 36 | * NSxfer::QueryGlobal converted to receive variable number of arguments + /END 37 | - Configurable: worker threads, NSXFER_USERAGENT, TRANSFER_CHUNK_SIZE, MAX_MEMORY_CONTENT_LENGTH, CONNECT_RETRY_DELAY, SPEED_MEASURE_INTERVAL 38 | * Transfer GUI 39 | * Test with silent installers 40 | - Reuse connection to the same server 41 | * Dynamic transfer buffer, based on speed (1KB - 256KB) 42 | - Incorrect ETA when resuming. Data already downloaded must be excluded 43 | - Add {FILELIST} keyword 44 | - Transfer() doesn't return the ID, therefore no later querying is possible. Suggestion: add /PUSHID parameter to push the transfer ID on the stack (in addition to the status) 45 | -------------------------------------------------------------------------------- /tools/ITaskbarList.h: -------------------------------------------------------------------------------- 1 | #pragma once 2 | 3 | #include 4 | 5 | 6 | //++ ITaskbarList3 7 | #ifndef __ITaskbarList3_INTERFACE_DEFINED__ 8 | #define __ITaskbarList3_INTERFACE_DEFINED__ 9 | 10 | typedef enum THUMBBUTTONFLAGS { 11 | THBF_ENABLED = 0, 12 | THBF_DISABLED = 0x1, 13 | THBF_DISMISSONCLICK = 0x2, 14 | THBF_NOBACKGROUND = 0x4, 15 | THBF_HIDDEN = 0x8, 16 | THBF_NONINTERACTIVE = 0x10 17 | } THUMBBUTTONFLAGS; 18 | DEFINE_ENUM_FLAG_OPERATORS(THUMBBUTTONFLAGS); 19 | 20 | typedef enum THUMBBUTTONMASK { 21 | THB_BITMAP = 0x1, 22 | THB_ICON = 0x2, 23 | THB_TOOLTIP = 0x4, 24 | THB_FLAGS = 0x8 25 | } THUMBBUTTONMASK; 26 | DEFINE_ENUM_FLAG_OPERATORS(THUMBBUTTONMASK); 27 | 28 | #include 29 | typedef struct THUMBBUTTON { 30 | THUMBBUTTONMASK dwMask; 31 | UINT iId; 32 | UINT iBitmap; 33 | HICON hIcon; 34 | WCHAR szTip[ 260 ]; 35 | THUMBBUTTONFLAGS dwFlags; 36 | } THUMBBUTTON; 37 | typedef struct THUMBBUTTON *LPTHUMBBUTTON; 38 | #include 39 | 40 | #define THBN_CLICKED 0x1800 41 | 42 | 43 | typedef interface ITaskbarList3 ITaskbarList3; 44 | 45 | /* interface ITaskbarList3 */ 46 | /* [object][uuid] */ 47 | 48 | typedef enum TBPFLAG { 49 | TBPF_NOPROGRESS = 0, 50 | TBPF_INDETERMINATE = 0x1, 51 | TBPF_NORMAL = 0x2, 52 | TBPF_ERROR = 0x4, 53 | TBPF_PAUSED = 0x8 54 | } TBPFLAG; 55 | DEFINE_ENUM_FLAG_OPERATORS(TBPFLAG); 56 | 57 | 58 | ///EXTERN_C const IID IID_ITaskbarList3; 59 | const IID IID_ITaskbarList3 = { 0xea1afb91, 0x9e28, 0x4b86, { 0x90,0xe9,0x9e,0x9f,0x8a,0x5e,0xef,0xaf }}; 60 | 61 | 62 | #if defined(__cplusplus) && !defined(CINTERFACE) 63 | 64 | MIDL_INTERFACE("ea1afb91-9e28-4b86-90e9-9e9f8a5eefaf") 65 | ITaskbarList3 : public ITaskbarList2 66 | { 67 | public: 68 | virtual HRESULT STDMETHODCALLTYPE SetProgressValue( 69 | /* [in] */ __RPC__in HWND hwnd, 70 | /* [in] */ ULONGLONG ullCompleted, 71 | /* [in] */ ULONGLONG ullTotal) = 0; 72 | 73 | virtual HRESULT STDMETHODCALLTYPE SetProgressState( 74 | /* [in] */ __RPC__in HWND hwnd, 75 | /* [in] */ TBPFLAG tbpFlags) = 0; 76 | 77 | virtual HRESULT STDMETHODCALLTYPE RegisterTab( 78 | /* [in] */ __RPC__in HWND hwndTab, 79 | /* [in] */ __RPC__in HWND hwndMDI) = 0; 80 | 81 | virtual HRESULT STDMETHODCALLTYPE UnregisterTab( 82 | /* [in] */ __RPC__in HWND hwndTab) = 0; 83 | 84 | virtual HRESULT STDMETHODCALLTYPE SetTabOrder( 85 | /* [in] */ __RPC__in HWND hwndTab, 86 | /* [in] */ __RPC__in HWND hwndInsertBefore) = 0; 87 | 88 | virtual HRESULT STDMETHODCALLTYPE SetTabActive( 89 | /* [in] */ __RPC__in HWND hwndTab, 90 | /* [in] */ __RPC__in HWND hwndMDI, 91 | /* [in] */ DWORD dwReserved) = 0; 92 | 93 | virtual HRESULT STDMETHODCALLTYPE ThumbBarAddButtons( 94 | /* [in] */ __RPC__in HWND hwnd, 95 | /* [in] */ UINT cButtons, 96 | /* [size_is][in] */ __RPC__in_ecount_full(cButtons) LPTHUMBBUTTON pButton) = 0; 97 | 98 | virtual HRESULT STDMETHODCALLTYPE ThumbBarUpdateButtons( 99 | /* [in] */ __RPC__in HWND hwnd, 100 | /* [in] */ UINT cButtons, 101 | /* [size_is][in] */ __RPC__in_ecount_full(cButtons) LPTHUMBBUTTON pButton) = 0; 102 | 103 | virtual HRESULT STDMETHODCALLTYPE ThumbBarSetImageList( 104 | /* [in] */ __RPC__in HWND hwnd, 105 | /* [in] */ __RPC__in_opt HIMAGELIST himl) = 0; 106 | 107 | virtual HRESULT STDMETHODCALLTYPE SetOverlayIcon( 108 | /* [in] */ __RPC__in HWND hwnd, 109 | /* [in] */ __RPC__in HICON hIcon, 110 | /* [string][unique][in] */ __RPC__in_opt_string LPCWSTR pszDescription) = 0; 111 | 112 | virtual HRESULT STDMETHODCALLTYPE SetThumbnailTooltip( 113 | /* [in] */ __RPC__in HWND hwnd, 114 | /* [string][unique][in] */ __RPC__in_opt_string LPCWSTR pszTip) = 0; 115 | 116 | virtual HRESULT STDMETHODCALLTYPE SetThumbnailClip( 117 | /* [in] */ __RPC__in HWND hwnd, 118 | /* [in] */ __RPC__in RECT *prcClip) = 0; 119 | 120 | }; 121 | 122 | #else /* C style interface */ 123 | 124 | typedef struct ITaskbarList3Vtbl 125 | { 126 | BEGIN_INTERFACE 127 | 128 | HRESULT ( STDMETHODCALLTYPE *QueryInterface )( 129 | __RPC__in ITaskbarList3 * This, 130 | /* [in] */ __RPC__in REFIID riid, 131 | /* [annotation][iid_is][out] */ 132 | __RPC__deref_out void **ppvObject); 133 | 134 | ULONG ( STDMETHODCALLTYPE *AddRef )( 135 | __RPC__in ITaskbarList3 * This); 136 | 137 | ULONG ( STDMETHODCALLTYPE *Release )( 138 | __RPC__in ITaskbarList3 * This); 139 | 140 | HRESULT ( STDMETHODCALLTYPE *HrInit )( 141 | __RPC__in ITaskbarList3 * This); 142 | 143 | HRESULT ( STDMETHODCALLTYPE *AddTab )( 144 | __RPC__in ITaskbarList3 * This, 145 | /* [in] */ __RPC__in HWND hwnd); 146 | 147 | HRESULT ( STDMETHODCALLTYPE *DeleteTab )( 148 | __RPC__in ITaskbarList3 * This, 149 | /* [in] */ __RPC__in HWND hwnd); 150 | 151 | HRESULT ( STDMETHODCALLTYPE *ActivateTab )( 152 | __RPC__in ITaskbarList3 * This, 153 | /* [in] */ __RPC__in HWND hwnd); 154 | 155 | HRESULT ( STDMETHODCALLTYPE *SetActiveAlt )( 156 | __RPC__in ITaskbarList3 * This, 157 | /* [in] */ __RPC__in HWND hwnd); 158 | 159 | HRESULT ( STDMETHODCALLTYPE *MarkFullscreenWindow )( 160 | __RPC__in ITaskbarList3 * This, 161 | /* [in] */ __RPC__in HWND hwnd, 162 | /* [in] */ BOOL fFullscreen); 163 | 164 | HRESULT ( STDMETHODCALLTYPE *SetProgressValue )( 165 | __RPC__in ITaskbarList3 * This, 166 | /* [in] */ __RPC__in HWND hwnd, 167 | /* [in] */ ULONGLONG ullCompleted, 168 | /* [in] */ ULONGLONG ullTotal); 169 | 170 | HRESULT ( STDMETHODCALLTYPE *SetProgressState )( 171 | __RPC__in ITaskbarList3 * This, 172 | /* [in] */ __RPC__in HWND hwnd, 173 | /* [in] */ TBPFLAG tbpFlags); 174 | 175 | HRESULT ( STDMETHODCALLTYPE *RegisterTab )( 176 | __RPC__in ITaskbarList3 * This, 177 | /* [in] */ __RPC__in HWND hwndTab, 178 | /* [in] */ __RPC__in HWND hwndMDI); 179 | 180 | HRESULT ( STDMETHODCALLTYPE *UnregisterTab )( 181 | __RPC__in ITaskbarList3 * This, 182 | /* [in] */ __RPC__in HWND hwndTab); 183 | 184 | HRESULT ( STDMETHODCALLTYPE *SetTabOrder )( 185 | __RPC__in ITaskbarList3 * This, 186 | /* [in] */ __RPC__in HWND hwndTab, 187 | /* [in] */ __RPC__in HWND hwndInsertBefore); 188 | 189 | HRESULT ( STDMETHODCALLTYPE *SetTabActive )( 190 | __RPC__in ITaskbarList3 * This, 191 | /* [in] */ __RPC__in HWND hwndTab, 192 | /* [in] */ __RPC__in HWND hwndMDI, 193 | /* [in] */ DWORD dwReserved); 194 | 195 | HRESULT ( STDMETHODCALLTYPE *ThumbBarAddButtons )( 196 | __RPC__in ITaskbarList3 * This, 197 | /* [in] */ __RPC__in HWND hwnd, 198 | /* [in] */ UINT cButtons, 199 | /* [size_is][in] */ __RPC__in_ecount_full(cButtons) LPTHUMBBUTTON pButton); 200 | 201 | HRESULT ( STDMETHODCALLTYPE *ThumbBarUpdateButtons )( 202 | __RPC__in ITaskbarList3 * This, 203 | /* [in] */ __RPC__in HWND hwnd, 204 | /* [in] */ UINT cButtons, 205 | /* [size_is][in] */ __RPC__in_ecount_full(cButtons) LPTHUMBBUTTON pButton); 206 | 207 | HRESULT ( STDMETHODCALLTYPE *ThumbBarSetImageList )( 208 | __RPC__in ITaskbarList3 * This, 209 | /* [in] */ __RPC__in HWND hwnd, 210 | /* [in] */ __RPC__in_opt HIMAGELIST himl); 211 | 212 | HRESULT ( STDMETHODCALLTYPE *SetOverlayIcon )( 213 | __RPC__in ITaskbarList3 * This, 214 | /* [in] */ __RPC__in HWND hwnd, 215 | /* [in] */ __RPC__in HICON hIcon, 216 | /* [string][unique][in] */ __RPC__in_opt_string LPCWSTR pszDescription); 217 | 218 | HRESULT ( STDMETHODCALLTYPE *SetThumbnailTooltip )( 219 | __RPC__in ITaskbarList3 * This, 220 | /* [in] */ __RPC__in HWND hwnd, 221 | /* [string][unique][in] */ __RPC__in_opt_string LPCWSTR pszTip); 222 | 223 | HRESULT ( STDMETHODCALLTYPE *SetThumbnailClip )( 224 | __RPC__in ITaskbarList3 * This, 225 | /* [in] */ __RPC__in HWND hwnd, 226 | /* [in] */ __RPC__in RECT *prcClip); 227 | 228 | END_INTERFACE 229 | } ITaskbarList3Vtbl; 230 | 231 | interface ITaskbarList3 232 | { 233 | CONST_VTBL struct ITaskbarList3Vtbl *lpVtbl; 234 | }; 235 | 236 | 237 | 238 | #ifdef COBJMACROS 239 | 240 | 241 | #define ITaskbarList3_QueryInterface(This,riid,ppvObject) \ 242 | ( (This)->lpVtbl -> QueryInterface(This,riid,ppvObject) ) 243 | 244 | #define ITaskbarList3_AddRef(This) \ 245 | ( (This)->lpVtbl -> AddRef(This) ) 246 | 247 | #define ITaskbarList3_Release(This) \ 248 | ( (This)->lpVtbl -> Release(This) ) 249 | 250 | 251 | #define ITaskbarList3_HrInit(This) \ 252 | ( (This)->lpVtbl -> HrInit(This) ) 253 | 254 | #define ITaskbarList3_AddTab(This,hwnd) \ 255 | ( (This)->lpVtbl -> AddTab(This,hwnd) ) 256 | 257 | #define ITaskbarList3_DeleteTab(This,hwnd) \ 258 | ( (This)->lpVtbl -> DeleteTab(This,hwnd) ) 259 | 260 | #define ITaskbarList3_ActivateTab(This,hwnd) \ 261 | ( (This)->lpVtbl -> ActivateTab(This,hwnd) ) 262 | 263 | #define ITaskbarList3_SetActiveAlt(This,hwnd) \ 264 | ( (This)->lpVtbl -> SetActiveAlt(This,hwnd) ) 265 | 266 | 267 | #define ITaskbarList3_MarkFullscreenWindow(This,hwnd,fFullscreen) \ 268 | ( (This)->lpVtbl -> MarkFullscreenWindow(This,hwnd,fFullscreen) ) 269 | 270 | 271 | #define ITaskbarList3_SetProgressValue(This,hwnd,ullCompleted,ullTotal) \ 272 | ( (This)->lpVtbl -> SetProgressValue(This,hwnd,ullCompleted,ullTotal) ) 273 | 274 | #define ITaskbarList3_SetProgressState(This,hwnd,tbpFlags) \ 275 | ( (This)->lpVtbl -> SetProgressState(This,hwnd,tbpFlags) ) 276 | 277 | #define ITaskbarList3_RegisterTab(This,hwndTab,hwndMDI) \ 278 | ( (This)->lpVtbl -> RegisterTab(This,hwndTab,hwndMDI) ) 279 | 280 | #define ITaskbarList3_UnregisterTab(This,hwndTab) \ 281 | ( (This)->lpVtbl -> UnregisterTab(This,hwndTab) ) 282 | 283 | #define ITaskbarList3_SetTabOrder(This,hwndTab,hwndInsertBefore) \ 284 | ( (This)->lpVtbl -> SetTabOrder(This,hwndTab,hwndInsertBefore) ) 285 | 286 | #define ITaskbarList3_SetTabActive(This,hwndTab,hwndMDI,dwReserved) \ 287 | ( (This)->lpVtbl -> SetTabActive(This,hwndTab,hwndMDI,dwReserved) ) 288 | 289 | #define ITaskbarList3_ThumbBarAddButtons(This,hwnd,cButtons,pButton) \ 290 | ( (This)->lpVtbl -> ThumbBarAddButtons(This,hwnd,cButtons,pButton) ) 291 | 292 | #define ITaskbarList3_ThumbBarUpdateButtons(This,hwnd,cButtons,pButton) \ 293 | ( (This)->lpVtbl -> ThumbBarUpdateButtons(This,hwnd,cButtons,pButton) ) 294 | 295 | #define ITaskbarList3_ThumbBarSetImageList(This,hwnd,himl) \ 296 | ( (This)->lpVtbl -> ThumbBarSetImageList(This,hwnd,himl) ) 297 | 298 | #define ITaskbarList3_SetOverlayIcon(This,hwnd,hIcon,pszDescription) \ 299 | ( (This)->lpVtbl -> SetOverlayIcon(This,hwnd,hIcon,pszDescription) ) 300 | 301 | #define ITaskbarList3_SetThumbnailTooltip(This,hwnd,pszTip) \ 302 | ( (This)->lpVtbl -> SetThumbnailTooltip(This,hwnd,pszTip) ) 303 | 304 | #define ITaskbarList3_SetThumbnailClip(This,hwnd,prcClip) \ 305 | ( (This)->lpVtbl -> SetThumbnailClip(This,hwnd,prcClip) ) 306 | 307 | #endif /* COBJMACROS */ 308 | 309 | #endif /* C style interface */ 310 | 311 | #endif /* __ITaskbarList3_INTERFACE_DEFINED__ */ 312 | 313 | 314 | // --------------------------------------------------------------------- 315 | 316 | 317 | //++ ITaskbarList4 318 | #ifndef __ITaskbarList4_INTERFACE_DEFINED__ 319 | #define __ITaskbarList4_INTERFACE_DEFINED__ 320 | 321 | /* interface ITaskbarList4 */ 322 | /* [object][uuid] */ 323 | 324 | typedef enum STPFLAG { 325 | STPF_NONE = 0, 326 | STPF_USEAPPTHUMBNAILALWAYS = 0x1, 327 | STPF_USEAPPTHUMBNAILWHENACTIVE = 0x2, 328 | STPF_USEAPPPEEKALWAYS = 0x4, 329 | STPF_USEAPPPEEKWHENACTIVE = 0x8 330 | } STPFLAG; 331 | DEFINE_ENUM_FLAG_OPERATORS(STPFLAG); 332 | 333 | 334 | //EXTERN_C const IID IID_ITaskbarList4; 335 | const IID IID_ITaskbarList4 = { 0xc43dc798, 0x95d1, 0x4bea, { 0x90, 0x30, 0xbb, 0x99, 0xe2, 0x98, 0x3a, 0x1a }}; 336 | 337 | 338 | typedef interface ITaskbarList4 ITaskbarList4; 339 | 340 | 341 | #if defined(__cplusplus) && !defined(CINTERFACE) 342 | 343 | MIDL_INTERFACE("c43dc798-95d1-4bea-9030-bb99e2983a1a") 344 | ITaskbarList4 : public ITaskbarList3 345 | { 346 | public: 347 | virtual HRESULT STDMETHODCALLTYPE SetTabProperties( 348 | /* [in] */ __RPC__in HWND hwndTab, 349 | /* [in] */ STPFLAG stpFlags) = 0; 350 | 351 | }; 352 | 353 | #else /* C style interface */ 354 | 355 | typedef struct ITaskbarList4Vtbl 356 | { 357 | BEGIN_INTERFACE 358 | 359 | HRESULT ( STDMETHODCALLTYPE *QueryInterface )( 360 | __RPC__in ITaskbarList4 * This, 361 | /* [in] */ __RPC__in REFIID riid, 362 | /* [annotation][iid_is][out] */ 363 | __RPC__deref_out void **ppvObject); 364 | 365 | ULONG ( STDMETHODCALLTYPE *AddRef )( 366 | __RPC__in ITaskbarList4 * This); 367 | 368 | ULONG ( STDMETHODCALLTYPE *Release )( 369 | __RPC__in ITaskbarList4 * This); 370 | 371 | HRESULT ( STDMETHODCALLTYPE *HrInit )( 372 | __RPC__in ITaskbarList4 * This); 373 | 374 | HRESULT ( STDMETHODCALLTYPE *AddTab )( 375 | __RPC__in ITaskbarList4 * This, 376 | /* [in] */ __RPC__in HWND hwnd); 377 | 378 | HRESULT ( STDMETHODCALLTYPE *DeleteTab )( 379 | __RPC__in ITaskbarList4 * This, 380 | /* [in] */ __RPC__in HWND hwnd); 381 | 382 | HRESULT ( STDMETHODCALLTYPE *ActivateTab )( 383 | __RPC__in ITaskbarList4 * This, 384 | /* [in] */ __RPC__in HWND hwnd); 385 | 386 | HRESULT ( STDMETHODCALLTYPE *SetActiveAlt )( 387 | __RPC__in ITaskbarList4 * This, 388 | /* [in] */ __RPC__in HWND hwnd); 389 | 390 | HRESULT ( STDMETHODCALLTYPE *MarkFullscreenWindow )( 391 | __RPC__in ITaskbarList4 * This, 392 | /* [in] */ __RPC__in HWND hwnd, 393 | /* [in] */ BOOL fFullscreen); 394 | 395 | HRESULT ( STDMETHODCALLTYPE *SetProgressValue )( 396 | __RPC__in ITaskbarList4 * This, 397 | /* [in] */ __RPC__in HWND hwnd, 398 | /* [in] */ ULONGLONG ullCompleted, 399 | /* [in] */ ULONGLONG ullTotal); 400 | 401 | HRESULT ( STDMETHODCALLTYPE *SetProgressState )( 402 | __RPC__in ITaskbarList4 * This, 403 | /* [in] */ __RPC__in HWND hwnd, 404 | /* [in] */ TBPFLAG tbpFlags); 405 | 406 | HRESULT ( STDMETHODCALLTYPE *RegisterTab )( 407 | __RPC__in ITaskbarList4 * This, 408 | /* [in] */ __RPC__in HWND hwndTab, 409 | /* [in] */ __RPC__in HWND hwndMDI); 410 | 411 | HRESULT ( STDMETHODCALLTYPE *UnregisterTab )( 412 | __RPC__in ITaskbarList4 * This, 413 | /* [in] */ __RPC__in HWND hwndTab); 414 | 415 | HRESULT ( STDMETHODCALLTYPE *SetTabOrder )( 416 | __RPC__in ITaskbarList4 * This, 417 | /* [in] */ __RPC__in HWND hwndTab, 418 | /* [in] */ __RPC__in HWND hwndInsertBefore); 419 | 420 | HRESULT ( STDMETHODCALLTYPE *SetTabActive )( 421 | __RPC__in ITaskbarList4 * This, 422 | /* [in] */ __RPC__in HWND hwndTab, 423 | /* [in] */ __RPC__in HWND hwndMDI, 424 | /* [in] */ DWORD dwReserved); 425 | 426 | HRESULT ( STDMETHODCALLTYPE *ThumbBarAddButtons )( 427 | __RPC__in ITaskbarList4 * This, 428 | /* [in] */ __RPC__in HWND hwnd, 429 | /* [in] */ UINT cButtons, 430 | /* [size_is][in] */ __RPC__in_ecount_full(cButtons) LPTHUMBBUTTON pButton); 431 | 432 | HRESULT ( STDMETHODCALLTYPE *ThumbBarUpdateButtons )( 433 | __RPC__in ITaskbarList4 * This, 434 | /* [in] */ __RPC__in HWND hwnd, 435 | /* [in] */ UINT cButtons, 436 | /* [size_is][in] */ __RPC__in_ecount_full(cButtons) LPTHUMBBUTTON pButton); 437 | 438 | HRESULT ( STDMETHODCALLTYPE *ThumbBarSetImageList )( 439 | __RPC__in ITaskbarList4 * This, 440 | /* [in] */ __RPC__in HWND hwnd, 441 | /* [in] */ __RPC__in_opt HIMAGELIST himl); 442 | 443 | HRESULT ( STDMETHODCALLTYPE *SetOverlayIcon )( 444 | __RPC__in ITaskbarList4 * This, 445 | /* [in] */ __RPC__in HWND hwnd, 446 | /* [in] */ __RPC__in HICON hIcon, 447 | /* [string][unique][in] */ __RPC__in_opt_string LPCWSTR pszDescription); 448 | 449 | HRESULT ( STDMETHODCALLTYPE *SetThumbnailTooltip )( 450 | __RPC__in ITaskbarList4 * This, 451 | /* [in] */ __RPC__in HWND hwnd, 452 | /* [string][unique][in] */ __RPC__in_opt_string LPCWSTR pszTip); 453 | 454 | HRESULT ( STDMETHODCALLTYPE *SetThumbnailClip )( 455 | __RPC__in ITaskbarList4 * This, 456 | /* [in] */ __RPC__in HWND hwnd, 457 | /* [in] */ __RPC__in RECT *prcClip); 458 | 459 | HRESULT ( STDMETHODCALLTYPE *SetTabProperties )( 460 | __RPC__in ITaskbarList4 * This, 461 | /* [in] */ __RPC__in HWND hwndTab, 462 | /* [in] */ STPFLAG stpFlags); 463 | 464 | END_INTERFACE 465 | } ITaskbarList4Vtbl; 466 | 467 | interface ITaskbarList4 468 | { 469 | CONST_VTBL struct ITaskbarList4Vtbl *lpVtbl; 470 | }; 471 | 472 | 473 | 474 | #ifdef COBJMACROS 475 | 476 | 477 | #define ITaskbarList4_QueryInterface(This,riid,ppvObject) \ 478 | ( (This)->lpVtbl -> QueryInterface(This,riid,ppvObject) ) 479 | 480 | #define ITaskbarList4_AddRef(This) \ 481 | ( (This)->lpVtbl -> AddRef(This) ) 482 | 483 | #define ITaskbarList4_Release(This) \ 484 | ( (This)->lpVtbl -> Release(This) ) 485 | 486 | 487 | #define ITaskbarList4_HrInit(This) \ 488 | ( (This)->lpVtbl -> HrInit(This) ) 489 | 490 | #define ITaskbarList4_AddTab(This,hwnd) \ 491 | ( (This)->lpVtbl -> AddTab(This,hwnd) ) 492 | 493 | #define ITaskbarList4_DeleteTab(This,hwnd) \ 494 | ( (This)->lpVtbl -> DeleteTab(This,hwnd) ) 495 | 496 | #define ITaskbarList4_ActivateTab(This,hwnd) \ 497 | ( (This)->lpVtbl -> ActivateTab(This,hwnd) ) 498 | 499 | #define ITaskbarList4_SetActiveAlt(This,hwnd) \ 500 | ( (This)->lpVtbl -> SetActiveAlt(This,hwnd) ) 501 | 502 | 503 | #define ITaskbarList4_MarkFullscreenWindow(This,hwnd,fFullscreen) \ 504 | ( (This)->lpVtbl -> MarkFullscreenWindow(This,hwnd,fFullscreen) ) 505 | 506 | 507 | #define ITaskbarList4_SetProgressValue(This,hwnd,ullCompleted,ullTotal) \ 508 | ( (This)->lpVtbl -> SetProgressValue(This,hwnd,ullCompleted,ullTotal) ) 509 | 510 | #define ITaskbarList4_SetProgressState(This,hwnd,tbpFlags) \ 511 | ( (This)->lpVtbl -> SetProgressState(This,hwnd,tbpFlags) ) 512 | 513 | #define ITaskbarList4_RegisterTab(This,hwndTab,hwndMDI) \ 514 | ( (This)->lpVtbl -> RegisterTab(This,hwndTab,hwndMDI) ) 515 | 516 | #define ITaskbarList4_UnregisterTab(This,hwndTab) \ 517 | ( (This)->lpVtbl -> UnregisterTab(This,hwndTab) ) 518 | 519 | #define ITaskbarList4_SetTabOrder(This,hwndTab,hwndInsertBefore) \ 520 | ( (This)->lpVtbl -> SetTabOrder(This,hwndTab,hwndInsertBefore) ) 521 | 522 | #define ITaskbarList4_SetTabActive(This,hwndTab,hwndMDI,dwReserved) \ 523 | ( (This)->lpVtbl -> SetTabActive(This,hwndTab,hwndMDI,dwReserved) ) 524 | 525 | #define ITaskbarList4_ThumbBarAddButtons(This,hwnd,cButtons,pButton) \ 526 | ( (This)->lpVtbl -> ThumbBarAddButtons(This,hwnd,cButtons,pButton) ) 527 | 528 | #define ITaskbarList4_ThumbBarUpdateButtons(This,hwnd,cButtons,pButton) \ 529 | ( (This)->lpVtbl -> ThumbBarUpdateButtons(This,hwnd,cButtons,pButton) ) 530 | 531 | #define ITaskbarList4_ThumbBarSetImageList(This,hwnd,himl) \ 532 | ( (This)->lpVtbl -> ThumbBarSetImageList(This,hwnd,himl) ) 533 | 534 | #define ITaskbarList4_SetOverlayIcon(This,hwnd,hIcon,pszDescription) \ 535 | ( (This)->lpVtbl -> SetOverlayIcon(This,hwnd,hIcon,pszDescription) ) 536 | 537 | #define ITaskbarList4_SetThumbnailTooltip(This,hwnd,pszTip) \ 538 | ( (This)->lpVtbl -> SetThumbnailTooltip(This,hwnd,pszTip) ) 539 | 540 | #define ITaskbarList4_SetThumbnailClip(This,hwnd,prcClip) \ 541 | ( (This)->lpVtbl -> SetThumbnailClip(This,hwnd,prcClip) ) 542 | 543 | 544 | #define ITaskbarList4_SetTabProperties(This,hwndTab,stpFlags) \ 545 | ( (This)->lpVtbl -> SetTabProperties(This,hwndTab,stpFlags) ) 546 | 547 | #endif /* COBJMACROS */ 548 | 549 | #endif /* C style interface */ 550 | 551 | #endif /* __ITaskbarList4_INTERFACE_DEFINED__ */ 552 | -------------------------------------------------------------------------------- /utils.c: -------------------------------------------------------------------------------- 1 | 2 | //? Marius Negrutiu (marius.negrutiu@protonmail.com) :: 2014/02/02 3 | 4 | #include "main.h" 5 | #include "utils.h" 6 | 7 | 8 | int _fltused = 0; 9 | MEMORY_STATS g_MemStats = { 0 }; 10 | 11 | 12 | //++ UtilsInitialize 13 | VOID UtilsInitialize() 14 | { 15 | TRACE( _T( " UtilsInitialize()\n" ) ); 16 | } 17 | 18 | //++ UtilsDestroy 19 | VOID UtilsDestroy() 20 | { 21 | TRACE( _T( " MyAlloc: %u calls, %I64u bytes\n" ), g_MemStats.AllocCalls, g_MemStats.AllocBytes ); 22 | TRACE( _T( " MyFree: %u calls, %I64u bytes\n" ), g_MemStats.FreeCalls, g_MemStats.FreeBytes ); 23 | TRACE( _T( " UtilsDestroy()\n" ) ); 24 | } 25 | 26 | //++ TraceImpl 27 | #if defined (TRACE_ENABLED) 28 | VOID TraceImpl( __in LPCTSTR pszFormat, ... ) 29 | { 30 | DWORD err = ERROR_SUCCESS; 31 | if ( pszFormat && *pszFormat ) { 32 | 33 | TCHAR szStr[1024]; 34 | int iLen1, iLen2; 35 | va_list args; 36 | 37 | iLen1 = wnsprintf( szStr, (int)ARRAYSIZE( szStr ), _T( "[xfer.th%04x] " ), GetCurrentThreadId() ); 38 | 39 | va_start( args, pszFormat ); 40 | iLen2 = wvnsprintf( szStr + iLen1, (int)ARRAYSIZE( szStr ) - iLen1, pszFormat, args ); 41 | if ( iLen2 > 0 ) { 42 | if ( iLen1 + iLen2 < ARRAYSIZE( szStr ) ) 43 | szStr[iLen1 + iLen2] = 0; /// The string is not guaranteed to be null terminated 44 | } else { 45 | szStr[ARRAYSIZE( szStr ) - 1] = 0; 46 | } 47 | OutputDebugString( szStr ); 48 | va_end( args ); 49 | } 50 | } 51 | #endif 52 | 53 | //++ TraceCallImpl 54 | #if defined (TRACE_ENABLED) 55 | VOID TraceCallImpl( __in stack_t **ppStackTop, __in_opt LPCTSTR pszPrefix ) 56 | { 57 | int iStrLen = 4096; 58 | LPTSTR pszStr = MyAllocStr( iStrLen ); 59 | assert( pszStr ); 60 | if (pszStr) { 61 | 62 | stack_t *p; 63 | LPTSTR psz = pszStr; 64 | int i; 65 | 66 | pszStr[0] = _T( '\0' ); 67 | 68 | if (pszPrefix && *pszPrefix) 69 | i = wnsprintf( psz, iStrLen, _T( "%s" ), pszPrefix ), psz += i, iStrLen -= i; 70 | 71 | if (ppStackTop) { 72 | for (p = *ppStackTop; p; p = p->next) { 73 | i = wnsprintf( psz, iStrLen, _T( " %s" ), p->text ), psz += i, iStrLen -= i; 74 | if (CompareString( 0, NORM_IGNORECASE, p->text, -1, _T( "/END" ), -1 ) == CSTR_EQUAL) 75 | break; 76 | } 77 | } 78 | 79 | if (pszStr[0] != _T( '\0' )) { 80 | i = wnsprintf( psz, iStrLen, _T( "\n" ) ), psz += i, iStrLen -= i; 81 | OutputDebugString( pszStr ); 82 | } 83 | 84 | MyFree( pszStr ); 85 | } 86 | } 87 | #endif 88 | 89 | //++ GetLocalFileTime 90 | BOOL GetLocalFileTime( _Out_ LPFILETIME lpFT ) 91 | { 92 | if ( lpFT ) { 93 | SYSTEMTIME st; 94 | GetLocalTime( &st ); 95 | return SystemTimeToFileTime( &st, lpFT ); 96 | } 97 | return FALSE; 98 | } 99 | 100 | //++ AllocErrorStr 101 | VOID AllocErrorStr( _In_ DWORD dwErrCode, _Out_ TCHAR **ppszErrText ) 102 | { 103 | if ( ppszErrText ) { 104 | 105 | DWORD dwLen; 106 | TCHAR szTextError[512]; 107 | HMODULE hModule = NULL; 108 | DWORD dwExtraFlags = 0; 109 | 110 | if ( dwErrCode >= INTERNET_ERROR_BASE && dwErrCode <= INTERNET_ERROR_LAST ) { 111 | hModule = GetModuleHandle( _T( "wininet.dll" ) ); 112 | dwExtraFlags = FORMAT_MESSAGE_FROM_HMODULE; 113 | } else { 114 | dwExtraFlags = FORMAT_MESSAGE_FROM_SYSTEM; 115 | } 116 | 117 | szTextError[0] = 0; 118 | dwLen = FormatMessage( FORMAT_MESSAGE_IGNORE_INSERTS | dwExtraFlags, hModule, dwErrCode, 0, szTextError, ARRAYSIZE( szTextError ), NULL ); 119 | if ( dwLen > 0 ) { 120 | StrTrim( szTextError, _T( ". \r\n" ) ); 121 | MyStrDup( *ppszErrText, szTextError ); 122 | } 123 | } 124 | } 125 | 126 | 127 | /// ULONLONG <-> double conversion groups 128 | /// ULLONG_MAX == 18446744073709551615 129 | ULONGLONG ULONGLONG_groups[19] = { 1000000000000000000, 100000000000000000, 10000000000000000, 1000000000000000, 100000000000000, 10000000000000, 1000000000000, 100000000000, 10000000000, 1000000000, 100000000, 10000000, 1000000, 100000, 10000, 1000, 100, 10, 1 }; 130 | double double_groups[19] = { 1000000000000000000.0F, 100000000000000000.0F, 10000000000000000.0F, 1000000000000000.0F, 100000000000000.0F, 10000000000000.0F, 1000000000000.0F, 100000000000.0F, 10000000000.0F, 1000000000.0F, 100000000.0F, 10000000.0F, 1000000.0F, 100000.0F, 10000.0F, 1000.0F, 100.0F, 10.0F, 1.0F }; 131 | 132 | 133 | //++ MyUlonglongToDouble 134 | double MyUlonglongToDouble( __in ULONGLONG ull ) 135 | { 136 | double d = 0.0F; 137 | int i; 138 | for (i = 0; i < 19; i++) { 139 | while (ull >= ULONGLONG_groups[i]) { 140 | d += double_groups[i]; 141 | ull -= ULONGLONG_groups[i]; 142 | } 143 | } 144 | return d; 145 | } 146 | 147 | 148 | //++ MyDoubleToUlonglong 149 | ULONGLONG MyDoubleToUlonglong( __in double d ) 150 | { 151 | ULONGLONG ull = 0; 152 | int i; 153 | for (i = 0; i < 19; i++) { 154 | while (d >= double_groups[i]) { 155 | ull += ULONGLONG_groups[i]; 156 | d -= double_groups[i]; 157 | } 158 | } 159 | return ull; 160 | } 161 | 162 | //++ MyDiv64 163 | ULONGLONG MyDiv64( __in ULONGLONG iNumerator, __in ULONGLONG iDenominator ) 164 | { 165 | /* ULONGLONG iQuotient = 0; 166 | if (iDenominator != 0) { 167 | while (iNumerator >= iDenominator) { 168 | iNumerator -= iDenominator; 169 | iQuotient++; 170 | } 171 | } 172 | return iQuotient;*/ 173 | 174 | if (iDenominator == 0) 175 | iDenominator = 1; 176 | return MyDoubleToUlonglong( MyUlonglongToDouble( iNumerator ) / MyUlonglongToDouble( iDenominator ) ); 177 | } 178 | 179 | 180 | //++ MyMulDiv64 181 | ULONGLONG MyMulDiv64( __in ULONGLONG iNumber, __in ULONGLONG iNumerator, __in ULONGLONG iDenominator ) 182 | { 183 | /// iNumber*(iNumerator/iDenominator) 184 | return MyDoubleToUlonglong( MyUlonglongToDouble( iNumber ) * (MyUlonglongToDouble( iNumerator ) / MyUlonglongToDouble( iDenominator )) ); 185 | } 186 | 187 | 188 | //++ MyTimeDiff 189 | ULONG MyTimeDiff( __in PFILETIME pEndTime, __in PFILETIME pStartTime ) 190 | { 191 | /// NOTE: Large integer operations are not available because we're not linking to CRT 192 | if (pStartTime && pEndTime) { 193 | ULONGLONG iDiff = MyDiv64( ((PULARGE_INTEGER)pEndTime)->QuadPart - ((PULARGE_INTEGER)pStartTime)->QuadPart, 10000 ); 194 | if (iDiff < UINT_MAX) 195 | return (ULONG)iDiff; 196 | else 197 | return UINT_MAX; /// ~49 days 198 | } 199 | return 0; 200 | } 201 | 202 | 203 | //++ ReadVersionInfoString 204 | DWORD ReadVersionInfoString( 205 | __in_opt LPCTSTR szFile, 206 | __in LPCTSTR szStringName, 207 | __out LPTSTR szStringValue, 208 | __in UINT iStringValueLen 209 | ) 210 | { 211 | DWORD err = ERROR_SUCCESS; 212 | 213 | // Validate parameters 214 | if (szStringName && *szStringName && szStringValue && (iStringValueLen > 0)) { 215 | 216 | TCHAR szExeFile[MAX_PATH]; 217 | DWORD dwVerInfoSize; 218 | szStringValue[0] = 0; 219 | 220 | if (szFile && *szFile) { 221 | lstrcpyn( szExeFile, szFile, ARRAYSIZE( szExeFile ) ); 222 | } else { 223 | GetModuleFileName( NULL, szExeFile, ARRAYSIZE( szExeFile ) ); /// Current executable 224 | } 225 | 226 | dwVerInfoSize = GetFileVersionInfoSize( szExeFile, NULL ); 227 | if (dwVerInfoSize > 0) { 228 | HANDLE hMem = GlobalAlloc( GMEM_MOVEABLE, dwVerInfoSize ); 229 | if (hMem) { 230 | LPBYTE pMem = GlobalLock( hMem ); 231 | if (pMem) { 232 | if (GetFileVersionInfo( szExeFile, 0, dwVerInfoSize, pMem )) { 233 | typedef struct _LANGANDCODEPAGE { WORD wLanguage; WORD wCodePage; } LANGANDCODEPAGE; 234 | LANGANDCODEPAGE *pCodePage; 235 | UINT iCodePageSize = sizeof( *pCodePage ); 236 | /// Code page 237 | if (VerQueryValue( pMem, _T( "\\VarFileInfo\\Translation" ), (LPVOID*)&pCodePage, &iCodePageSize )) { 238 | TCHAR szTemp[255]; 239 | LPCTSTR szValue = NULL; 240 | UINT iValueLen = 0; 241 | /// Read version string 242 | wnsprintf( szTemp, ARRAYSIZE( szTemp ), _T( "\\StringFileInfo\\%04x%04x\\%s" ), pCodePage->wLanguage, pCodePage->wCodePage, szStringName ); 243 | if (VerQueryValue( pMem, szTemp, (LPVOID*)&szValue, &iValueLen )) { 244 | /// Output 245 | if (*szValue) { 246 | lstrcpyn( szStringValue, szValue, iStringValueLen ); 247 | if (iValueLen > iStringValueLen) { 248 | /// The output buffer is not large enough 249 | /// We'll return the truncated string, and ERROR_BUFFER_OVERFLOW error code 250 | err = ERROR_BUFFER_OVERFLOW; 251 | } 252 | } else { 253 | err = ERROR_NOT_FOUND; 254 | } 255 | } else { 256 | err = ERROR_NOT_FOUND; 257 | } 258 | } else { 259 | err = ERROR_NOT_FOUND; 260 | } 261 | } else { 262 | err = GetLastError(); 263 | } 264 | GlobalUnlock( hMem ); 265 | } else { 266 | err = GetLastError(); 267 | } 268 | GlobalFree( hMem ); 269 | } else { 270 | err = GetLastError(); 271 | } 272 | } else { 273 | err = GetLastError(); 274 | } 275 | } else { 276 | err = ERROR_INVALID_PARAMETER; 277 | } 278 | return err; 279 | } 280 | 281 | 282 | //++ BinaryToString 283 | ULONG BinaryToString( 284 | __in LPVOID pData, __in ULONG iDataSize, 285 | __out LPTSTR pszStr, __in ULONG iStrLen 286 | ) 287 | { 288 | ULONG iLen = 0; 289 | if (pszStr && iStrLen) { 290 | pszStr[0] = _T( '\0' ); 291 | if (pData) { 292 | ULONG i, n; 293 | CHAR ch; 294 | for (i = 0, n = (ULONG)__min( iDataSize, iStrLen - 1 ); i < n; i++) { 295 | ch = ((PCH)pData)[i]; 296 | if ((ch >= 32 /*&& ch < 127*/) || ch == '\r' || ch == '\n') { 297 | pszStr[i] = ch; 298 | } else { 299 | pszStr[i] = _T( '.' ); 300 | } 301 | } 302 | pszStr[i] = _T( '\0' ); 303 | iLen += i; /// Not including NULL terminator 304 | } 305 | } 306 | return iLen; 307 | } 308 | 309 | 310 | //++ RegReadDWORD 311 | DWORD RegReadDWORD( __in HKEY hRoot, __in LPCTSTR pszKey, __in LPCTSTR pszValue, __out LPDWORD pdwValue ) 312 | { 313 | DWORD err = ERROR_SUCCESS; 314 | if (hRoot && pszKey && *pszKey && pszValue && pdwValue) { 315 | HKEY hKey; 316 | *pdwValue = 0; /// Init 317 | err = RegOpenKeyEx( hRoot, pszKey, 0, KEY_READ | KEY_WOW64_64KEY, &hKey ); 318 | if (err == ERROR_SUCCESS) { 319 | DWORD dwType, dwSize = sizeof( *pdwValue ); 320 | err = RegQueryValueEx( hKey, pszValue, NULL, &dwType, (LPBYTE)pdwValue, &dwSize ); 321 | if (err == ERROR_SUCCESS) { 322 | if (dwType == REG_DWORD) { 323 | /// Done 324 | } else { 325 | err = ERROR_INVALID_DATATYPE; 326 | } 327 | } 328 | RegCloseKey( hKey ); 329 | } 330 | } else { 331 | err = ERROR_INVALID_PARAMETER; 332 | } 333 | return err; 334 | } 335 | 336 | 337 | //++ RegWriteDWORD 338 | DWORD RegWriteDWORD( __in HKEY hRoot, __in LPCTSTR pszKey, __in LPCTSTR pszValue, __in DWORD dwValue ) 339 | { 340 | DWORD err = ERROR_SUCCESS; 341 | if (hRoot && pszKey && *pszKey && pszValue) { 342 | HKEY hKey; 343 | err = RegOpenKeyEx( hRoot, pszKey, 0, KEY_WRITE | KEY_WOW64_64KEY, &hKey ); 344 | if (err == ERROR_SUCCESS) { 345 | err = RegSetValueEx( hKey, pszValue, 0, REG_DWORD, (LPBYTE)&dwValue, sizeof( dwValue ) ); 346 | RegCloseKey( hKey ); 347 | } 348 | } else { 349 | err = ERROR_INVALID_PARAMETER; 350 | } 351 | return err; 352 | } 353 | 354 | 355 | //++ MyStrToInt64 356 | BOOL MyStrToInt64( _In_ LPCTSTR pszStr, _Out_ PUINT64 piNum ) 357 | { 358 | BOOL bRet = FALSE; 359 | int ch, i; 360 | UINT64 n; 361 | if (pszStr && piNum) { 362 | for (*piNum = 0;;) { 363 | 364 | ch = *(pszStr++) - _T( '0' ); 365 | if (ch < 0 || ch > 9) 366 | break; 367 | 368 | /// *piNum *= 10; 369 | for (i = 0, n = *piNum; i < 9; i++) 370 | *piNum += n; 371 | 372 | *piNum += ch; 373 | } 374 | bRet = TRUE; 375 | } 376 | return bRet; 377 | } 378 | 379 | 380 | //++ MyCreateDirectory 381 | ULONG MyCreateDirectory( _In_ LPCTSTR pszPath, _In_ BOOLEAN bHasFilename ) 382 | { 383 | ULONG e = ERROR_SUCCESS; 384 | LPTSTR pszBuf; 385 | 386 | if (!pszPath || !*pszPath) 387 | return ERROR_INVALID_PARAMETER; 388 | 389 | // Work with a copy of the string 390 | MyStrDup( pszBuf, pszPath ); 391 | if (pszBuf) { 392 | 393 | TCHAR *psz, ch; 394 | ULONG len = lstrlen( pszBuf ); 395 | // Strip trailing backslashes 396 | for (psz = pszBuf + len - 1; (psz > pszBuf) && (*psz == _T( '\\' ) || *psz == _T( '/' )); psz--) 397 | *psz = _T( '\0' ), len--; 398 | if (bHasFilename) { 399 | // Strip filename 400 | for (psz = pszBuf + len - 1; (psz > pszBuf) && (*psz != _T( '\\' )) && (*psz != _T( '/' )); psz--) 401 | *psz = _T( '\0' ), len--; 402 | // Strip trailing backslashes 403 | for (psz = pszBuf + len - 1; (psz > pszBuf) && (*psz == _T( '\\' ) || *psz == _T( '/' )); psz--) 404 | *psz = _T( '\0' ), len--; 405 | } 406 | psz = pszBuf; 407 | // Skip UNC server name (\\?\UNC\SERVER) 408 | if (CompareString( CP_ACP, NORM_IGNORECASE, psz, 8, _T( "\\\\?\\UNC\\" ), -1 ) == CSTR_EQUAL) { 409 | psz += 8; 410 | for (; *psz != _T( '\\' ) && *psz != _T( '\0' ); psz++); 411 | } 412 | // Skip \\?\ prefix 413 | if (CompareString( CP_ACP, NORM_IGNORECASE, psz, 4, _T( "\\\\?\\" ), -1 ) == CSTR_EQUAL) 414 | psz += 4; 415 | // Skip network server name (\\SERVER) 416 | if (CompareString( CP_ACP, NORM_IGNORECASE, psz, 2, _T( "\\\\" ), -1 ) == CSTR_EQUAL) { 417 | psz += 2; 418 | for (; *psz != _T( '\\' ) && *psz != _T( '\0' ); psz++); 419 | } 420 | // Create intermediate directories 421 | for (; (e == ERROR_SUCCESS) && *psz; ) { 422 | for (; *psz == _T( '\\' ); psz++); 423 | for (; *psz != _T( '\\' ) && *psz != _T( '\0' ); psz++); 424 | ch = *psz, *psz = _T( '\0' ); 425 | e = CreateDirectory( pszBuf, NULL ) ? ERROR_SUCCESS : GetLastError(); 426 | if (e == ERROR_ALREADY_EXISTS || e == ERROR_ACCESS_DENIED) 427 | e = ERROR_SUCCESS; 428 | *psz = ch; 429 | } 430 | 431 | MyFree( pszBuf ); 432 | 433 | } else { 434 | e = ERROR_OUTOFMEMORY; 435 | } 436 | 437 | return e; 438 | } 439 | -------------------------------------------------------------------------------- /utils.h: -------------------------------------------------------------------------------- 1 | 2 | //? Marius Negrutiu (marius.negrutiu@protonmail.com) :: 2014/02/02 3 | 4 | #pragma once 5 | 6 | //+ Init functions 7 | VOID UtilsInitialize(); 8 | VOID UtilsDestroy(); 9 | 10 | 11 | //+ TRACE 12 | #if DBG || _DEBUG 13 | #define TRACE_ENABLED 14 | #endif 15 | 16 | #if defined (TRACE_ENABLED) 17 | #define TRACE TraceImpl 18 | #define TRACE2(...) /// More verbose tracing 19 | VOID TraceImpl( __in LPCTSTR pszFormat, ... ); 20 | 21 | #define TRACE_CALL TraceCallImpl 22 | VOID TraceCallImpl( __in stack_t **ppStackTop, __in LPCTSTR pszPrefix ); 23 | #else 24 | #define TRACE_CALL(...) 25 | #define TRACE(...) 26 | #define TRACE2(...) 27 | #endif 28 | 29 | //+ assert 30 | #if DBG || _DEBUG 31 | #define assert(expr) \ 32 | if ( !(expr)) { \ 33 | TCHAR szMsg[512]; \ 34 | TRACE( _T(" [!] %s, %s:%u\n"), _T(#expr), _T( __FILE__ ), __LINE__ ); \ 35 | wnsprintf( szMsg, (int)ARRAYSIZE( szMsg ), _T("%s\n%s : %u\n"), _T(#expr), _T( __FILE__ ), __LINE__ ); \ 36 | if (MessageBox( NULL, szMsg, _T("ASSERT"), MB_ICONERROR|MB_OKCANCEL ) != IDOK) \ 37 | ExitProcess( 666 ); \ 38 | } 39 | #define verify assert 40 | #else 41 | #define assert(...) 42 | #define verify(expr) ((VOID)(expr)) 43 | #endif 44 | 45 | //+ Utils 46 | BOOL GetLocalFileTime( _Out_ LPFILETIME lpFT ); 47 | VOID AllocErrorStr( _In_ DWORD dwErrCode, _Out_ TCHAR **ppszErrText ); /// Call MyFree(ppszErrText) when no longer needed 48 | 49 | //+ Memory 50 | /// We'll use global memory, to be compatible with NSIS API 51 | #define MyAlloc(_size) GlobalLock(GlobalAlloc(GMEM_FIXED, _size)); g_MemStats.AllocBytes += (_size); g_MemStats.AllocCalls++ 52 | #define MyAllocStr(_len) (LPTSTR)MyAlloc( ((_len) + 1) * sizeof(TCHAR)) 53 | #define MyFree(_ptr) if ( _ptr ) { g_MemStats.FreeBytes += GlobalSize((HGLOBAL)(_ptr)); g_MemStats.FreeCalls++; GlobalFree((HGLOBAL)(_ptr)); (_ptr) = NULL; } 54 | 55 | #define MyDataDup(_dst, _src, _size) { \ 56 | (_dst) = MyAlloc( _size ); \ 57 | if (_dst) { \ 58 | LPBYTE pDst = (LPBYTE)(_dst); \ 59 | LPBYTE pSrc = (LPBYTE)(_src); \ 60 | ULONG i; \ 61 | for ( i = 0; i < (_size); i++, pDst++, pSrc++ ) \ 62 | *pDst = *pSrc; \ 63 | } \ 64 | } 65 | 66 | #define MyStrDup(_dst, _src) { \ 67 | (_dst) = MyAllocStr( lstrlen( _src )); \ 68 | if (_dst) \ 69 | lstrcpy( (LPTSTR)(_dst), (LPTSTR)(_src) ); \ 70 | } 71 | 72 | #define MyMakeUpper(_dst) { \ 73 | if (_dst) { \ 74 | TCHAR *p; \ 75 | for (p = (TCHAR*)(_dst); *p; p++) { \ 76 | if (*p >= 'a' && *p <= 'z') { \ 77 | *p -= ('a' - 'A'); \ 78 | } \ 79 | } \ 80 | } \ 81 | } 82 | 83 | #define MyZeroMemory(_ptr, _cnt) { \ 84 | LPBYTE p; \ 85 | for ( p = (LPBYTE)(_ptr) + (_cnt) - 1; p >= (LPBYTE)(_ptr); p-- ) *p = 0; \ 86 | } 87 | 88 | typedef struct { 89 | UINT64 AllocBytes; 90 | UINT AllocCalls; 91 | UINT64 FreeBytes; 92 | UINT FreeCalls; 93 | } MEMORY_STATS; 94 | extern MEMORY_STATS g_MemStats; 95 | 96 | 97 | #define VALID_FILE_HANDLE(h) \ 98 | ((h) != NULL) && ((h) != INVALID_HANDLE_VALUE) 99 | 100 | 101 | //+ ULONGLONG <-> double 102 | double MyUlonglongToDouble( __in ULONGLONG ull ); 103 | ULONGLONG MyDoubleToUlonglong( __in double d ); 104 | 105 | //+ MyDiv64 106 | ULONGLONG MyDiv64( __in ULONGLONG iNumerator, __in ULONGLONG iDenominator ); 107 | 108 | //+ MyMulDiv64 109 | ULONGLONG MyMulDiv64( __in ULONGLONG iNumber, __in ULONGLONG iNumerator, __in ULONGLONG iDenominator ); 110 | 111 | //+ MyTimeDiff 112 | // Returns milliseconds 113 | ULONG MyTimeDiff( __in PFILETIME pEndTime, __in PFILETIME pStartTime ); 114 | 115 | //+ ReadVersionInfoString 116 | // Returns Win32 error 117 | DWORD ReadVersionInfoString( __in_opt LPCTSTR szFile, __in LPCTSTR szStringName, __out LPTSTR szStringValue, __in UINT iStringValueLen ); 118 | 119 | //+ BinaryToString 120 | // Returns number of TCHAR-s written, not including the NULL terminator 121 | ULONG BinaryToString( __in LPVOID pData, __in ULONG iDataSize, __out LPTSTR pszStr, __in ULONG iStrLen ); 122 | 123 | //+ RegXxxDWORD 124 | // Returns Win32 error 125 | DWORD RegReadDWORD( __in HKEY hRoot, __in LPCTSTR pszKey, __in LPCTSTR pszValue, __out LPDWORD pdwValue ); 126 | DWORD RegWriteDWORD( __in HKEY hRoot, __in LPCTSTR pszKey, __in LPCTSTR pszValue, __in DWORD dwValue ); 127 | 128 | //+ MyStrToInt64 129 | // Replacement for shlwapi!StrToInt64Ex introduced in "Update Rollup 1 for Windows 2000 SP4" 130 | BOOL MyStrToInt64( _In_ LPCTSTR pszStr, _Out_ PUINT64 piNum ); 131 | 132 | //+ MyCreateDirectory 133 | ULONG MyCreateDirectory( _In_ LPCTSTR pszPath, _In_ BOOLEAN bHasFilename ); 134 | --------------------------------------------------------------------------------