Add autonomous webhooks and dynamic WebView discovery

This commit is contained in:
Ops Agent committed 2026-10-11 08:43:39 +02:00
1 parent ab69f287b6
commit 97f874d183
29 files changed
+3044 -1489

No files matched your search

+13
View File
@@ -0,0 +1,13 @@
@echo off
setlocal
rem Run from an x64 MSVC Native Tools prompt. Output defaults to native/build.
if "%~1"=="" (set "PROBE_OUT=%~dp0build") else (set "PROBE_OUT=%~f1")
if not exist "%PROBE_OUT%" mkdir "%PROBE_OUT%"
pushd "%PROBE_OUT%"
cl /nologo /O2 /MT /LD "%~dp0math_probe.cpp" /Fe:math_probe.dll /link windowsapp.lib user32.lib
if errorlevel 1 exit /b 1
cl /nologo /O2 /MT /LD "%~dp0search_probe.cpp" /Fe:search_probe.dll /link windowsapp.lib user32.lib
if errorlevel 1 exit /b 1
cl /nologo /O2 /MT "%~dp0discovery_check.cpp" /Fe:discovery_check.exe
if errorlevel 1 exit /b 1
popd
+9
View File
@@ -0,0 +1,9 @@
@echo off
setlocal
rem Run in an x64 MSVC Native Tools prompt.
if "%~1"=="" (set "PROBE_OUT=%~dp0build") else (set "PROBE_OUT=%~f1")
if not exist "%PROBE_OUT%" mkdir "%PROBE_OUT%"
pushd "%PROBE_OUT%"
cl /nologo /O2 /MT /EHsc /std:c++17 /LD "%~dp0webhook_probe.cpp" /Fe:webhook_probe.dll /link windowsapp.lib user32.lib winhttp.lib
if errorlevel 1 exit /b 1
popd
+62
View File
@@ -0,0 +1,62 @@
#pragma once
#include <cstring>
// Conservative fingerprint discovery: no module-relative code/data RVAs.
// These relocation-free prefixes describe a supported compiler layout, not
// every possible WebView release. Ambiguous/changed layouts fail closed.
struct CoreLayout { uintptr_t primary, secondary, query; };
static bool discoverCoreLayout(uintptr_t base, CoreLayout& out) {
const BYTE query[] = {0x48,0x89,0x5c,0x24,0x08,0x48,0x89,0x6c,0x24,0x10,
0x48,0x89,0x74,0x24,0x18,0x57,0x48,0x83,0xec,0x20,
0x48,0x8d,0x99,0x08,0x01,0x00,0x00,0x33,0xed,0x48,0x85,0xdb};
const BYTE execute[] = {0x40,0x53,0x56,0x57,0x41,0x56,0x41,0x57,
0x48,0x83,0xec,0x30,0x4d,0x8b,0xf0,0x45,0x33,0xff,0x4d,0x89,0x38,
0x48,0x8d,0x71,0xf0};
auto dos=(IMAGE_DOS_HEADER*)base;
if(dos->e_magic!=IMAGE_DOS_SIGNATURE || dos->e_lfanew<=0 || dos->e_lfanew>0x100000)return false;
auto nt=(IMAGE_NT_HEADERS64*)(base+dos->e_lfanew);
if(nt->Signature!=IMAGE_NT_SIGNATURE || nt->FileHeader.Machine!=IMAGE_FILE_MACHINE_AMD64 ||
nt->OptionalHeader.Magic!=IMAGE_NT_OPTIONAL_HDR64_MAGIC || nt->FileHeader.NumberOfSections>96)return false;
auto sections=IMAGE_FIRST_SECTION(nt);
unsigned nq=0,ne=0,np=0;
for(unsigned i=0;i<nt->FileHeader.NumberOfSections;i++) {
auto& s=sections[i];size_t size=s.Misc.VirtualSize;
if(s.VirtualAddress>=nt->OptionalHeader.SizeOfImage || size>nt->OptionalHeader.SizeOfImage-s.VirtualAddress)return false;
if(!(s.Characteristics&IMAGE_SCN_MEM_EXECUTE))continue;
const BYTE* start=(BYTE*)(base+s.VirtualAddress);
for(size_t j=0;j+sizeof(query)<=size;j++)if(!memcmp(start+j,query,sizeof(query))){out.query=(uintptr_t)(start+j);nq++;}
for(size_t j=0;j+sizeof(execute)<=size;j++)if(!memcmp(start+j,execute,sizeof(execute)))ne++;
}
if(nq!=1 || ne==0)return false;
for(unsigned i=0;i<nt->FileHeader.NumberOfSections;i++) {
auto& s=sections[i];size_t size=s.Misc.VirtualSize;
if(!(s.Characteristics&IMAGE_SCN_MEM_READ) || (s.Characteristics&(IMAGE_SCN_MEM_WRITE|IMAGE_SCN_MEM_EXECUTE)))continue;
auto start=base+s.VirtualAddress;
for(size_t j=0;j+sizeof(uintptr_t)*6<=size;j+=sizeof(uintptr_t)) {
auto v=(uintptr_t*)(start+j);
if(v[0]==out.query){out.primary=(uintptr_t)v;np++;}
}
}
// The secondary table immediately precedes this implementation's primary
// table (eight ABI entries). Keep this structural requirement explicit.
if(np!=1 || out.primary<base+8*sizeof(uintptr_t))return false;
out.secondary=out.primary-8*sizeof(uintptr_t);
for(unsigned i=0;i<6;i++) {
auto target=((uintptr_t*)out.secondary)[i];
if(target<base || target>=base+nt->OptionalHeader.SizeOfImage)return false;
MEMORY_BASIC_INFORMATION m{};
if(!VirtualQuery((void*)target,&m,sizeof(m)) || m.State!=MEM_COMMIT ||
(m.Protect!=PAGE_EXECUTE_READ && m.Protect!=PAGE_EXECUTE_READWRITE))return false;
}
return true;
}
static bool supportedExecute(void* code) {
const BYTE prefix[]={0x40,0x53,0x56,0x57,0x41,0x56,0x41,0x57,
0x48,0x83,0xec,0x30,0x4d,0x8b,0xf0,0x45,0x33,0xff,0x4d,0x89,0x38,
0x48,0x8d,0x71,0xf0};
MEMORY_BASIC_INFORMATION m{};
if(!VirtualQuery(code,&m,sizeof(m)) || m.State!=MEM_COMMIT ||
(m.Protect!=PAGE_EXECUTE_READ && m.Protect!=PAGE_EXECUTE_READWRITE))return false;
return memcmp(code,prefix,sizeof(prefix))==0;
}
+14
View File
@@ -0,0 +1,14 @@
#define WIN32_LEAN_AND_MEAN
#include <windows.h>
#include <stdint.h>
#include <stdio.h>
#include "core_discovery.h"
int wmain(int argc,wchar_t** argv) {
if(argc!=2)return 2;
HMODULE h=LoadLibraryExW(argv[1],nullptr,DONT_RESOLVE_DLL_REFERENCES);
if(!h)return 3;
CoreLayout l{};bool ok=discoverCoreLayout((uintptr_t)h,l);
if(ok)printf("primary=%llx secondary=%llx query=%llx\n",
l.primary-(uintptr_t)h,l.secondary-(uintptr_t)h,l.query-(uintptr_t)h);
FreeLibrary(h);return ok?0:4;
}
+6 -3
View File
@@ -6,6 +6,7 @@
#include <stdint.h>
#include <stdio.h>
#include <wchar.h>
#include "core_discovery.h"
struct Packet { volatile LONG state; LONG hr; DWORD thread; DWORD protocol;
wchar_t script[8192]; wchar_t result[32768]; };
static const UINT Wake = WM_APP + 0x498;
@@ -41,6 +42,8 @@ static void fail(HRESULT hr) { clearAsync();packet->hr=hr;InterlockedExchange(&p
static HRESULT findCore() {
auto module=(uintptr_t)GetModuleHandleW(L"Microsoft.Web.WebView2.Core.dll");
if(!module)return HRESULT_FROM_WIN32(ERROR_MOD_NOT_FOUND);
CoreLayout layout{};
if(!discoverCoreLayout(module,layout))return HRESULT_FROM_WIN32(ERROR_REVISION_MISMATCH);
MEMORY_BASIC_INFORMATION mbi;
uintptr_t address=0,scanned=0;
while(VirtualQuery((void*)address,&mbi,sizeof(mbi))) {
@@ -52,11 +55,11 @@ static HRESULT findCore() {
if(scanned+mbi.RegionSize>256*1024*1024)break;
scanned+=mbi.RegionSize;
for(uintptr_t p=(uintptr_t)mbi.BaseAddress;p+0x118<=end;p+=8) {
if(*(uintptr_t*)p!=module+0xa6ef0 || *(uintptr_t*)(p+0x108)!=module+0xa6eb0)continue;
if(table((void*)p)[0]!=(void*)(module+0x5ac50))continue;
if(*(uintptr_t*)p!=layout.primary || *(uintptr_t*)(p+0x108)!=layout.secondary)continue;
if(table((void*)p)[0]!=(void*)(layout.query))continue;
auto hr=((IUnknown*)p)->QueryInterface(CoreIID,(void**)&core);
if(FAILED(hr))continue;
if(table(core)[49]!=(void*)(module+0x59060)) { core->Release();core=nullptr;continue; }
if(!supportedExecute(table(core)[49])) { core->Release();core=nullptr;continue; }
return S_OK;
}
}
+6 -3
View File
@@ -6,6 +6,7 @@
#include <stdint.h>
#include <stdio.h>
#include <wchar.h>
#include "core_discovery.h"
struct Packet { volatile LONG state; LONG hr; DWORD thread; DWORD reserved;
wchar_t script[8192]; wchar_t result[32768]; };
static const UINT Wake = WM_APP + 0x497;
@@ -25,6 +26,8 @@ static void fail(HRESULT hr) { clearAsync();packet->hr=hr;InterlockedExchange(&p
static HRESULT findCore() {
auto module=(uintptr_t)GetModuleHandleW(L"Microsoft.Web.WebView2.Core.dll");
if(!module)return HRESULT_FROM_WIN32(ERROR_MOD_NOT_FOUND);
CoreLayout layout{};
if(!discoverCoreLayout(module,layout))return HRESULT_FROM_WIN32(ERROR_REVISION_MISMATCH);
MEMORY_BASIC_INFORMATION mbi;
uintptr_t address=0,scanned=0;
while(VirtualQuery((void*)address,&mbi,sizeof(mbi))) {
@@ -36,11 +39,11 @@ static HRESULT findCore() {
if(scanned+mbi.RegionSize>256*1024*1024)break;
scanned+=mbi.RegionSize;
for(uintptr_t p=(uintptr_t)mbi.BaseAddress;p+0x118<=end;p+=8) {
if(*(uintptr_t*)p!=module+0xa6ef0 || *(uintptr_t*)(p+0x108)!=module+0xa6eb0)continue;
if(table((void*)p)[0]!=(void*)(module+0x5ac50))continue;
if(*(uintptr_t*)p!=layout.primary || *(uintptr_t*)(p+0x108)!=layout.secondary)continue;
if(table((void*)p)[0]!=(void*)(layout.query))continue;
auto hr=((IUnknown*)p)->QueryInterface(CoreIID,(void**)&core);
if(FAILED(hr))continue;
if(table(core)[49]!=(void*)(module+0x59060)) { core->Release();core=nullptr;continue; }
if(!supportedExecute(table(core)[49])) { core->Release();core=nullptr;continue; }
return S_OK;
}
}
+154
View File
@@ -0,0 +1,154 @@
#define WIN32_LEAN_AND_MEAN
#include <windows.h>
#include <winhttp.h>
#include <inspectable.h>
#include <winstring.h>
#include <stdint.h>
#include <stdio.h>
#include <wchar.h>
#include <string>
#include "core_discovery.h"
struct Packet { volatile LONG state; LONG hr; DWORD thread,protocol; wchar_t script[8192],result[32768]; };
static const UINT Wake=WM_APP+0x499;
static const GUID CoreIID={0x3a3f559a,0xe5e9,0x5338,{0xbb,0x67,0x4e,0xb0,0x50,0x4a,0x4f,0x14}};
static const GUID InfoIID={0x00000036,0,0,{0xc0,0,0,0,0,0,0,0x46}};
static IUnknown *core=nullptr,*operation=nullptr,*info=nullptr;
static UINT_PTR timer=0;
static HMODULE resident=nullptr;
static DWORD uiThread=0;
static unsigned short port=0;
static bool secure=false;
static std::wstring endpoint,hostname=L"127.0.0.1",path=L"/whatsapp-webhook",token;
static volatile LONG wipeAfterWorker=0;
static HANDLE worker=nullptr;
static volatile LONG httpOK=0;
static ULONG delivered=0,attempts=0;
static std::wstring body,ack;
static bool acknowledging=false;
static ULONGLONG operationStarted=0;
static void** table(void* p){return *(void***)p;}
static void wipe(std::wstring& value){if(!value.empty())SecureZeroMemory(value.data(),value.size()*sizeof(wchar_t));value.clear();}
struct Destination {unsigned short port=0;bool secure=false;std::wstring endpoint,host,path,token;~Destination(){wipe(token);}};
static bool parseDestination(const wchar_t* text,Destination& out){
const wchar_t* split=wcschr(text,L'\n');
if(!split){wchar_t* end=nullptr;auto value=wcstoul(text,&end,10);if(!value||value>65535||!end||*end)return false;out.port=(unsigned short)value;out.host=L"127.0.0.1";out.path=L"/whatsapp-webhook";return true;}
out.endpoint.assign(text,split-text);out.token.assign(split+1);
if(out.token.size()<32||out.token.size()>128)return false;
for(auto ch:out.token)if(ch<33||ch>126)return false;
if(out.endpoint.find_first_of(L"\r\n?#")!=std::wstring::npos)return false;
URL_COMPONENTS url{};url.dwStructSize=sizeof(url);url.dwHostNameLength=url.dwUrlPathLength=url.dwUserNameLength=url.dwPasswordLength=url.dwExtraInfoLength=(DWORD)-1;
if(!WinHttpCrackUrl(out.endpoint.c_str(),(DWORD)out.endpoint.size(),0,&url)||url.nScheme!=INTERNET_SCHEME_HTTPS||!url.dwHostNameLength||url.dwUserNameLength||url.dwPasswordLength||url.dwExtraInfoLength)return false;
out.secure=true;out.port=url.nPort;out.host.assign(url.lpszHostName,url.dwHostNameLength);out.path.assign(url.lpszUrlPath,url.dwUrlPathLength);
return !out.path.empty()&&out.path[0]==L'/';
}
static void clearAsync(){
if(info){using Action=HRESULT(STDMETHODCALLTYPE*)(void*);((Action)table(info)[9])(info);((Action)table(info)[10])(info);info->Release();info=nullptr;}
if(operation){operation->Release();operation=nullptr;}
}
static HRESULT findCore(){
auto module=(uintptr_t)GetModuleHandleW(L"Microsoft.Web.WebView2.Core.dll");
CoreLayout layout{};
if(!module||!discoverCoreLayout(module,layout))return HRESULT_FROM_WIN32(ERROR_REVISION_MISMATCH);
MEMORY_BASIC_INFORMATION mbi;uintptr_t address=0,scanned=0;
while(VirtualQuery((void*)address,&mbi,sizeof(mbi))){
uintptr_t end=(uintptr_t)mbi.BaseAddress+mbi.RegionSize;if(end<=address)break;address=end;
if(mbi.State!=MEM_COMMIT||mbi.Type!=MEM_PRIVATE||mbi.Protect!=PAGE_READWRITE||mbi.RegionSize>64*1024*1024)continue;
if(scanned+mbi.RegionSize>256*1024*1024)break;scanned+=mbi.RegionSize;
for(uintptr_t p=(uintptr_t)mbi.BaseAddress;p+0x118<=end;p+=8){
if(*(uintptr_t*)p!=layout.primary||*(uintptr_t*)(p+0x108)!=layout.secondary||table((void*)p)[0]!=(void*)layout.query)continue;
if(FAILED(((IUnknown*)p)->QueryInterface(CoreIID,(void**)&core)))continue;
if(!supportedExecute(table(core)[49])){core->Release();core=nullptr;continue;}return S_OK;
}
}return HRESULT_FROM_WIN32(ERROR_NOT_FOUND);
}
static HRESULT execute(const std::wstring& script){
HSTRING text=nullptr;auto hr=WindowsCreateString(script.c_str(),(UINT32)script.size(),&text);
if(FAILED(hr))return hr;
using Execute=HRESULT(STDMETHODCALLTYPE*)(void*,HSTRING,IUnknown**);
hr=((Execute)table(core)[49])(core,text,&operation);WindowsDeleteString(text);
if(SUCCEEDED(hr))hr=operation->QueryInterface(InfoIID,(void**)&info);
if(FAILED(hr))clearAsync();else operationStarted=GetTickCount64();return hr;
}
static DWORD WINAPI post(void*){
int count=WideCharToMultiByte(CP_UTF8,WC_ERR_INVALID_CHARS,body.c_str(),(int)body.size(),nullptr,0,nullptr,nullptr);
if(count<=0||count>131072)return 0;
std::string bytes(count,'\0');WideCharToMultiByte(CP_UTF8,WC_ERR_INVALID_CHARS,body.c_str(),(int)body.size(),bytes.data(),count,nullptr,nullptr);
HINTERNET session=WinHttpOpen(L"WhatsAppLocalWebhook/1",WINHTTP_ACCESS_TYPE_NO_PROXY,nullptr,nullptr,0);
HINTERNET connection=nullptr,request=nullptr;
if(session){WinHttpSetTimeouts(session,2000,2000,2000,2000);connection=WinHttpConnect(session,hostname.c_str(),port,0);}
if(connection)request=WinHttpOpenRequest(connection,L"POST",path.c_str(),nullptr,nullptr,nullptr,secure?WINHTTP_FLAG_SECURE:0);
DWORD policy=WINHTTP_OPTION_REDIRECT_POLICY_NEVER;
if(request){WinHttpSetOption(request,WINHTTP_OPTION_REDIRECT_POLICY,&policy,sizeof(policy));
std::wstring headers=L"Content-Type: application/json\r\n";if(secure)headers+=L"X-Webhook-Token: "+token+L"\r\n";
if(WinHttpSendRequest(request,headers.c_str(),(DWORD)headers.size(),bytes.data(),count,count,0)&&WinHttpReceiveResponse(request,nullptr)){
DWORD code=0,size=sizeof(code);if(WinHttpQueryHeaders(request,WINHTTP_QUERY_STATUS_CODE|WINHTTP_QUERY_FLAG_NUMBER,nullptr,&code,&size,nullptr)&&code>=200&&code<300)InterlockedExchange(&httpOK,1);
}wipe(headers);WinHttpCloseHandle(request);
}if(connection)WinHttpCloseHandle(connection);if(session)WinHttpCloseHandle(session);
if(InterlockedExchange(&wipeAfterWorker,0))wipe(token);return 0;
}
static void tick(){
if(!timer)return; // KillTimer may leave an already queued callback.
if(worker){if(WaitForSingleObject(worker,0)!=WAIT_OBJECT_0)return;CloseHandle(worker);worker=nullptr;
if(InterlockedExchange(&httpOK,0)){++delivered;acknowledging=true;if(FAILED(execute(ack)))acknowledging=false;return;}body.clear();ack.clear();}
if(operation){using Status=HRESULT(STDMETHODCALLTYPE*)(void*,INT32*);INT32 status=0;
if(FAILED(((Status)table(info)[7])(info,&status))||status>1||GetTickCount64()-operationStarted>10000){clearAsync();acknowledging=false;return;}
if(status==0)return;
using Results=HRESULT(STDMETHODCALLTYPE*)(void*,HSTRING*);HSTRING value=nullptr;
auto hr=((Results)table(operation)[8])(operation,&value);std::wstring result;
if(SUCCEEDED(hr)){UINT32 length=0;auto raw=WindowsGetStringRawBuffer(value,&length);if(length<=32768)result.assign(raw,length);WindowsDeleteString(value);}clearAsync();
if(acknowledging){acknowledging=false;body.clear();ack.clear();return;}
// Script returns an object, so WebView returns JSON directly, not an escaped JSON string.
auto start=result.find(L"\"seqs\":[");if(start==std::wstring::npos)return;start+=8;auto end=result.find(L']',start);
if(end==std::wstring::npos||end==start)return;
auto seqs=result.substr(start,end-start);if(seqs.find_first_not_of(L"0123456789,")!=std::wstring::npos)return;
body=result;ack=L"(()=>{const w=window.__whatsappWebhookV1;if(!w)return false;const ids=new Set(["+seqs+L"]);const before=w.queue.length;w.queue=w.queue.filter(e=>!ids.has(e.seq));w.delivered=(w.delivered||0)+before-w.queue.length;w.last_error=null;return true;})()";
++attempts;worker=CreateThread(nullptr,0,post,nullptr,0,nullptr);return;
}
execute(L"(()=>{const w=window.__whatsappWebhookV1;if(!w||w.stopped)return {seqs:[],events:[]};const events=[];for(const e of w.queue.slice(0,16)){if(JSON.stringify([...events,e]).length>30000)break;events.push(e);}return {listener_id:w.listener_id,seqs:events.map(e=>e.seq),events};})()");
}
static void CALLBACK timerProc(HWND,UINT,UINT_PTR,DWORD){__try{tick();}__except(EXCEPTION_EXECUTE_HANDLER){clearAsync();}}
static void command(){
wchar_t name[96];swprintf_s(name,L"Local\\WhatsAppWebhook-v1-%lu",GetCurrentProcessId());
HANDLE mapping=OpenFileMappingW(FILE_MAP_ALL_ACCESS,FALSE,name);if(!mapping)return;
Packet* packet=(Packet*)MapViewOfFile(mapping,FILE_MAP_ALL_ACCESS,0,0,sizeof(Packet));if(!packet){CloseHandle(mapping);return;}
HRESULT hr=S_OK;LONG result=-1;
if(packet->protocol!=0x57484231||packet->thread!=GetCurrentThreadId())hr=E_INVALIDARG;
else if(packet->state==0){
if(worker&&WaitForSingleObject(worker,0)==WAIT_OBJECT_0){CloseHandle(worker);worker=nullptr;body.clear();ack.clear();InterlockedExchange(&httpOK,0);}
packet->script[8191]=0;Destination destination;
bool parsed=parseDestination(packet->script,destination);SecureZeroMemory(packet->script,sizeof(packet->script));
if(!parsed)hr=E_INVALIDARG;
else if(timer){if(port!=destination.port||secure!=destination.secure||endpoint!=destination.endpoint||token!=destination.token)hr=HRESULT_FROM_WIN32(ERROR_BUSY);}
else if(worker)hr=HRESULT_FROM_WIN32(ERROR_BUSY);
else{if(!core)hr=findCore();if(SUCCEEDED(hr)){
if(!resident)GetModuleHandleExW(GET_MODULE_HANDLE_EX_FLAG_FROM_ADDRESS,(LPCWSTR)&command,&resident);
if(!resident)hr=HRESULT_FROM_WIN32(GetLastError());else{wipe(token);port=destination.port;secure=destination.secure;endpoint=destination.endpoint;hostname=destination.host;path=destination.path;token=destination.token;uiThread=GetCurrentThreadId();timer=SetTimer(nullptr,0,500,timerProc);if(!timer){hr=HRESULT_FROM_WIN32(GetLastError());wipe(token);}}
}}result=2;
}else if(packet->state==6){if(timer){KillTimer(nullptr,timer);timer=0;}clearAsync();if(core){core->Release();core=nullptr;}
if(worker&&WaitForSingleObject(worker,0)!=WAIT_OBJECT_0){InterlockedExchange(&wipeAfterWorker,1);if(WaitForSingleObject(worker,0)==WAIT_OBJECT_0){InterlockedExchange(&wipeAfterWorker,0);wipe(token);}}
else wipe(token);result=7;
// Keep one module reference until process exit: a pending worker/callback may still execute.
}else if(packet->state==3)result=4;
else if(packet->state==2||packet->state==4||packet->state==7||packet->state==-1){
// The controller may queue several wake messages while discovery is running.
// Completed requests must retain their result when those messages arrive.
UnmapViewOfFile(packet);CloseHandle(mapping);return;
}else hr=E_INVALIDARG;
swprintf_s(packet->result,L"{\"running\":%s,\"port\":%u,\"delivered_batches\":%lu,\"attempts\":%lu}",timer?L"true":L"false",port,delivered,attempts);
packet->hr=hr;InterlockedExchange(&packet->state,FAILED(hr)?-1:result);UnmapViewOfFile(packet);CloseHandle(mapping);
}
extern "C" __declspec(dllexport) LRESULT CALLBACK ProbeHook(int code,WPARAM wParam,LPARAM lParam){
if(code>=0&&wParam==PM_REMOVE&&((MSG*)lParam)->message==Wake){((MSG*)lParam)->message=WM_NULL;__try{command();}__except(EXCEPTION_EXECUTE_HANDLER){}}
return CallNextHookEx(nullptr,code,wParam,lParam);
}
BOOL WINAPI DllMain(HINSTANCE,DWORD,LPVOID){return TRUE;}
#ifdef WEBHOOK_CONFIG_TEST
int main(){
const wchar_t* valid[]={L"8765",L"https://example.test/events\n0123456789abcdef0123456789abcdef",L"https://example.test:8443/events\n0123456789abcdef0123456789abcdef"};
const wchar_t* invalid[]={L"0",L"65536",L"8765x",L"http://example.test/events\n0123456789abcdef0123456789abcdef",L"https://user:[email protected]/events\n0123456789abcdef0123456789abcdef",L"https://example.test/events?secret=x\n0123456789abcdef0123456789abcdef",L"https://example.test/events#fragment\n0123456789abcdef0123456789abcdef",L"https://example.test/events\nshort",L"https://example.test/events\n0123456789abcdef0123456789abc\rdef",L"https://example.test/events\n0123456789abcdef0123456789abc\ndef"};
for(auto text:valid){Destination destination;if(!parseDestination(text,destination))return 1;}
for(auto text:invalid){Destination destination;if(parseDestination(text,destination))return 2;}
Destination remote;if(!parseDestination(valid[2],remote)||remote.port!=8443||!remote.secure||remote.host!=L"example.test"||remote.path!=L"/events")return 3;
puts("14 synthetic configuration checks passed");return 0;
}
#endif