#include <windows.h>
#include <assert.h>
#include "mirc.h"
#include "options.h"
#define MVERSION(a) mIRC_version_code = a; break
#define MDLL(func) __declspec(dllexport) int __stdcall func(HWND mWnd, HWND aWnd, char *data, char *parms, BOOL show, BOOL nopause)
#define vsnprintf _vsnprintf
extern int __main(int argc, char **argv);
extern int _vsnprintf(char *, size_t, const char *, va_list);
extern struct options;
extern int close(int);
void wget_thread(param_info *params);
int thread_count = 0;
int mIRC_version_code = -1;
CRITICAL_SECTION CriticalSection;
char *strErrors[] =
{
"NOCONERROR", "HOSTERR", "CONSOCKERR", "CONERROR", "CONSSLERR",
"CONREFUSED", "NEWLOCATION", "NOTENOUGHMEM", "CONPORTERR",
"BINDERR", "BINDOK", "LISTENERR", "ACCEPTERR", "ACCEPTOK",
"CONCLOSED", "FTPOK", "FTPLOGINC", "FTPLOGREFUSED", "FTPPORTERR",
"FTPNSFOD", "FTPRETROK", "FTPUNKNOWNTYPE", "FTPRERR",
"FTPREXC", "FTPSRVERR", "FTPRETRINT", "FTPRESTFAIL", "URLERROR",
"FOPENERR", "FWRITEERR", "HOK", "HLEXC", "HEOF",
"HERR", "RETROK", "RECLEVELEXC", "FTPACCDENIED", "WRONGCODE",
"FTPINVPASV", "FTPNOPASV",
"CONTNOTSUPPORTED", "RETRUNNEEDED", "RETRFINISHED", "READERR", "TRYLIMEXC",
"URLBADPATTERN", "FILEBADFILE", "RANGEERR", "RETRBADPATTERN",
"RETNOTSUP", "ROBOTSOK", "NOROBOTS", "PROXERR", "AUTHFAILED",
"QUOTEXC", "WRITEFAILED",
"SSLERRCERTFILE","SSLERRCERTKEY","SSLERRCTXCREATE"
};
char *null_string = "";
char *uerr_to_string(int error) { return (error < 0 || error >= sizeof(strErrors) ? null_string : strErrors[error]); }
/* These are the addresses of the command parser in mIRC
from 6.16 to 6.11. Anything before 6.11 has a different
parameter list. All of these have been tested and work.
*/
DWORD __parser_address[] = {
0x0043E080, /* 6.16 */
0x0043D990, /* 6.15 */
0x0043CFC0, /* 6.14 */
0x0043BBA0, /* 6.12 */
0x0043BB50, /* 6.11 */
0x0043AFC0, /* 6.1 */
0x004B07E0, /* 6.03 */
0x004B099C, /* 6.02 */
0x004AE788, /* 6.01 */
0x004AD47C, /* 6.0 */
0x0049BC6C /* 5.91 */
};
void (__fastcall *command_parserA)(HWND mWnd, char *command, int u1, int u2, int u3, int u4, HWND *pmWnd) = NULL;
void (__stdcall *command_parserB)(HWND mWnd, char *command, int u1, int u2, int u3, int u4, HWND *pmWnd) = NULL;
void (__stdcall *command_parserC)(HWND mWnd, char *command, int u1, int u2, int u3, int u4) = NULL;
void __fastcall inject_command(char *cmd)
{
if (command_parserA)
{
/* command parser function for mIRC 6.1+ */
command_parserA(mIRC_window, cmd, 0, 0, 0, 0, &mIRC_window);
}
else
{
/* mIRC does not like using the mIRC_window as a parameter in older
versions (pre 6.02). This probably means Khaled is phasing out the need
for a HWND parameter in the code parser.
In any event, we need to find a window, preferably the Status Window (any cid will do)
*/
HWND sWindow = FindWindowEx(mIRC_window, NULL, "MDIClient", NULL);
if (command_parserB)
{
sWindow = FindWindowEx(sWindow, NULL, "mIRC_Status", NULL);
/* command parser function for mIRC 6.0-6.03 */
command_parserB(sWindow, cmd, 0, 0, 0, 0, &sWindow);
}
else if (command_parserC)
{
sWindow = FindWindowEx(sWindow, NULL, "status", NULL);
/* command parser function for pre-6.0 mIRC */
command_parserC(sWindow, cmd, 0, 0, 0, 0);
}
}
}
int __declspec(dllexport) __stdcall parse(HWND mWnd, HWND aWnd, char *data, char *parms, BOOL show, BOOL nopause)
{
#ifdef INJECT_COMMAND
inject_command(data);
#endif
return 1;
}
BOOL sendmessage_command(HWND hwnd, char *command)
{
HANDLE hMapFile;
LPSTR mData;
BOOL result = FALSE;
hMapFile = CreateFileMapping(INVALID_HANDLE_VALUE, 0, PAGE_READWRITE, 0, 4096, "mIRC");
mData = (LPSTR)MapViewOfFile(hMapFile, FILE_MAP_ALL_ACCESS, 0, 0, 0);
wsprintf(mData, command);
SendMessage(hwnd, WM_MCOMMAND, (WPARAM)NULL, (LPARAM)NULL);
if (strcmp(mData, "1") == 0) result = TRUE;
UnmapViewOfFile(mData);
CloseHandle(hMapFile);
return result;
}
/* We used to use the SendMessage API here, but due to a bad mIRC implementation of MapFiles
it was phased out. Instead we're hijacking mIRC's script parser function by using ASM
to call the address location of the parser. (inject_command)
*/
BOOL vSendCommand(HWND hwnd, char *fmt, va_list args)
{
char mData[901] = {0}, *p;
vsnprintf(mData, 900, fmt, args);
/* mIRC won't like newlines in a message, let's 'remove' them
so the scripter won't have to
*/
p = mData;
while (*p) { if (*p == '\n' || *p == '\r') *p = ' '; p++; }
if (mIRC_version_code < 0)
{
/* In a case where we don't have a documented parser address
we have no choice but to rely on the SendMessage API.
*/
sendmessage_command(mIRC_window, mData);
}
else
{
inject_command(mData);
}
return TRUE;
}
BOOL SendCommand(HWND hwnd, char *fmt, ...)
{
va_list args;
BOOL ret = FALSE;
va_start(args, fmt);
ret = vSendCommand(hwnd, fmt, args);
va_end (args);
return ret;
}
pthread_t wget_newthread(char *data)
{
int id;
pthread_t t = NULL;
param_info *p = malloc(sizeof(param_info));
for (id = 0; id < thread_count; id++)
{
if (thread[id]->status == STATUS_CLOSED) break;
}
if (id == thread_count) /* could not find an empty thread */
{
/* Since we're (possibly) moving our thread pointer array with realloc(),
we should really make sure that no other threads are going
to be accessing it until we're done, so let's make this
a critical section
*/
EnterCriticalSection(&CriticalSection);
thread = realloc(thread, (thread_count+1) * sizeof(pthread_t));
LeaveCriticalSection(&CriticalSection);
thread[thread_count++] = malloc(sizeof(thread_t));
}
t = thread[id];
memset(t, 0, sizeof(thread_t));
p->thread = t;
p->data = data;
t->id = id;
t->handle = CreateThread(0, 0, (LPTHREAD_START_ROUTINE)&wget_thread, (LPVOID)p, 0, &t->thread_id);
t->status = STATUS_LOADING;
return t;
}
int wget_active_threads()
{
int i, count = 0;
for (i = 0; i < thread_count; i++)
{
if (thread[i]->status > STATUS_CLOSED) count++;
}
return count;
}
int wget_get_thread_by_id(long thread_id)
{
int i;
for (i = 0; i < thread_count; i++)
{
if (thread[i]->thread_id == thread_id) return i;
}
return -1;
}
/* Clean exit */
void wget_exit_thread(int thread_num)
{
if (thread_num < 0 || thread_num >= thread_count || thread[thread_num]->status == STATUS_CLOSED) return;
SendCommand(mIRC_window, "/.signal WGET_QUIT %d NORMAL_EXIT", thread_num+1);
thread[thread_num]->status = STATUS_CLOSED;
}
/* Abnormal abortion of thread exit */
void wget_abort_thread(int thread_num)
{
if (thread_num < 0 || thread_num >= thread_count || thread[thread_num]->status == STATUS_CLOSED) return;
/* We need the close some things that the thread probably didn't */
if (thread[thread_num]->sock_fd) close(thread[thread_num]->sock_fd);
if (thread[thread_num]->file_handle) fclose(thread[thread_num]->file_handle);
TerminateThread(thread[thread_num]->handle, 0);
CloseHandle(thread[thread_num]->handle);
SendCommand(mIRC_window, "/.signal WGET_QUIT %d ERR_ABNORMAL_EXIT", thread_num+1);
thread[thread_num]->status = STATUS_CLOSED;
}
typedef struct {
struct {
short hiVersion;
short loVersion;
} mVersion;
HWND mHwnd;
BOOL mKeep;
} LOADINFO;
void __declspec(dllexport) __stdcall LoadDll(LOADINFO *t)
{
// Initialize the critical section one time only.
InitializeCriticalSection(&CriticalSection);
mIRC_window = t->mHwnd;
t->mKeep = TRUE;
switch (t->mVersion.hiVersion * 100 + t->mVersion.loVersion)
{
case 616: MVERSION(0);
case 615: MVERSION(1);
case 614: MVERSION(2);
case 612: MVERSION(3);
case 611: MVERSION(4);
/* mIRC breaks the mVersion reply at 6.1,
it does not properly fill in the HIWORD of mVersion
*/
case 601: MVERSION(5); /* 6.1 */
case 3: MVERSION(6); /* 6.03 */
case 2: MVERSION(7); /* 6.02 */
case 1: MVERSION(8); /* 6.01 */
case 0: MVERSION(9); /* 6.0 */
case 91: MVERSION(10); /* 5.91 */
default:
mIRC_version_code = -1;
}
if (mIRC_version_code > 9)
command_parserC = __parser_address[mIRC_version_code];
else if (mIRC_version_code > 5)
command_parserB = __parser_address[mIRC_version_code];
else if (mIRC_version_code >= 0)
command_parserA = __parser_address[mIRC_version_code];
}
int __declspec(dllexport) __stdcall UnloadDll(int mTimeout)
{
int i;
if (mTimeout == 1 && wget_active_threads() > 0) return 1;
for (i = 0; i < thread_count; i++)
{
pthread_t t = thread[i];
if (t->sock_fd) close(t->sock_fd);
if (t->file_handle) fclose(t->file_handle);
if (t->handle && t->status > STATUS_CLOSED)
{
TerminateThread(t->handle, 0);
CloseHandle(t->handle);
}
free(t);
}
// Don't need the critical section anymore.
DeleteCriticalSection(&CriticalSection);
return 0;
}
int __declspec(dllexport) __stdcall wget(HWND mWnd, HWND aWnd, char *data, char *parms, BOOL show, BOOL nopause)
{
pthread_t t;
if (*data == '\0')
{
strcpy(data, "//echo $color(info) -a * wget: usage: /wget [OPTION]... [URL]... | halt");
return 2;
}
if ((t = wget_newthread(data)) == NULL)
{
SendCommand(mIRC_window, "/.signal WGET_QUIT ERR_CREATE_THREAD");
return 1;
}
while (t->status < STATUS_LOADED)
{
/* Make sure we still have this function running while wget_thread is starting up,
because we need the *data string, and rather than waste cycles copying the data we
should just reference it
*/
WaitForSingleObject(t->handle, 1);
}
return 1;
}
int __declspec(dllexport) __stdcall wget_close(HWND mWnd, HWND aWnd, char *data, char *parms, BOOL show, BOOL nopause)
{
int id = atoi(data) - 1;
if (id >= 0 && id < thread_count && thread[id]->status > 0)
{
/* call abort thread function */
wget_abort_thread(id);
}
return 1;
}
int __declspec(dllexport) __stdcall wget_status(HWND mWnd, HWND aWnd, char *data, char *parms, BOOL show, BOOL nopause)
{
int id, status = 0;
if (*data == '\0')
{
strcpy(data, "//echo $color(info) -ae * wget_status: insufficient parameters | halt");
return 2;
}
id = atoi(data) - 1;
if (id >= 0 && id < thread_count)
{
status = thread[id]->status;
}
sprintf(data, "%d", status);
return 3;
}
int __declspec(dllexport) __stdcall wget_filename(HWND mWnd, HWND aWnd, char *data, char *parms, BOOL show, BOOL nopause)
{
int id, status = 0;
if (*data == '\0')
{
strcpy(data, "//echo $color(info) -ae * wget_filename: insufficient parameters | halt");
return 2;
}
id = atoi(data) - 1;
strcpy(data, "$null");
if (id >= 0 && id < thread_count &&
thread[id]->filename && *thread[id]->filename != '\0')
{
sprintf(data, "%s", thread[id]->filename);
}
return 3;
}
int __declspec(dllexport) __stdcall wget_count(HWND mWnd, HWND aWnd, char *data, char *parms, BOOL show, BOOL nopause)
{
sprintf(data, "%d", wget_active_threads());
return 3;
}
int __declspec(dllexport) __stdcall wget_rate(HWND mWnd, HWND aWnd, char *data, char *parms, BOOL show, BOOL nopause)
{
int id;
if (*data == '\0')
{
strcpy(data, "//echo $color(info) -ae * wget_rate: insufficient parameters | halt");
return 2;
}
id = atoi(data) - 1;
strcpy(data, "$null");
if (id >= 0 && id < thread_count)
{
sprintf(data, "%.2fKB/s", thread[id]->download_info.rate);
}
return 3;
}
int __declspec(dllexport) __stdcall wget_total_rate(HWND mWnd, HWND aWnd, char *data, char *parms, BOOL show, BOOL nopause)
{
int i;
double rate = 0.0;
for (i = 0; i < thread_count; i++)
{
if (thread[i]->status)
{
rate += thread[i]->download_info.rate;
}
}
sprintf(data, "%.2fKB/s", rate);
return 3;
}
int __declspec(dllexport) __stdcall wget_percent(HWND mWnd, HWND aWnd, char *data, char *parms, BOOL show, BOOL nopause)
{
int id;
if (*data == '\0')
{
strcpy(data, "//echo $color(info) -ae * wget_percent: insufficient parameters | halt");
return 2;
}
id = atoi(data) - 1;
strcpy(data, "$null");
if (id >= 0 && id < thread_count)
{
sprintf(data, "%3d", thread[id]->download_info.percent);
}
return 3;
}
int __declspec(dllexport) __stdcall wget_size(HWND mWnd, HWND aWnd, char *data, char *parms, BOOL show, BOOL nopause)
{
int id;
if (*data == '\0')
{
strcpy(data, "//echo $color(info) -ae * wget_size: insufficient parameters | halt");
return 2;
}
id = atoi(data) - 1;
strcpy(data, "$null");
if (id >= 0 && id < thread_count)
{
sprintf(data, "%d", thread[id]->download_info.size);
}
return 3;
}
int __declspec(dllexport) __stdcall wget_total(HWND mWnd, HWND aWnd, char *data, char *parms, BOOL show, BOOL nopause)
{
int id;
if (*data == '\0')
{
strcpy(data, "//echo $color(info) -ae * wget_total: insufficient parameters | halt");
return 2;
}
id = atoi(data) - 1;
strcpy(data, "$null");
if (id >= 0 && id < thread_count)
{
sprintf(data, "%d", thread[id]->download_info.total);
}
return 3;
}
#define ARGV_LIMIT 256
void wget_thread(param_info *params)
{
/* setup argv, argc */
char *argv[ARGV_LIMIT] = {0};
int argc = 1;
argv[0] = strdup("wget");
argv[argc] = strtok(params->data, " ");
/* main() conventions call for argv[argc] == NULL, so we're only going to use
ARGV_LIMIT-1 elements
*/
while (argc < ARGV_LIMIT-1 && (argv[++argc] = strtok(NULL, " ")));
/* Allocate options */
params->thread->opt = malloc(sizeof(struct options));
/* WTF? Weirdest bugfix ever... */
strdup(" ");
/* call main wget procedure */
params->thread->status = STATUS_LOADED;
__main(argc, argv);
params->thread->status = STATUS_CLEANUP;
/* exit the calling thread */
wget_exit_thread(params->thread->id);
/* Do memory cleanup */
free(params);
ExitThread(0);
} Powered by
GeSHi Syntax Highlighting software.
Author of all (other) material unless otherwise specified:
Loren Segal. Copyright 2005.