From cb4a46e7fbe62d788e66ed6121c717a2d22a4d7c Mon Sep 17 00:00:00 2001 From: watcherhd Date: Thu, 21 Apr 2011 14:14:52 +0000 Subject: svn.miranda.im is moving to a new home! git-svn-id: http://miranda-plugins.googlecode.com/svn/trunk@7 e753b5eb-9565-29b2-b5c5-2cc6f99dfbcb --- miranda-wine/src/modules/netlib/netlib.c | 503 +++++++++++++++ miranda-wine/src/modules/netlib/netlib.h | 157 +++++ miranda-wine/src/modules/netlib/netlibbind.c | 247 ++++++++ miranda-wine/src/modules/netlib/netlibhttp.c | 729 ++++++++++++++++++++++ miranda-wine/src/modules/netlib/netlibhttpproxy.c | 610 ++++++++++++++++++ miranda-wine/src/modules/netlib/netliblog.c | 440 +++++++++++++ miranda-wine/src/modules/netlib/netlibopenconn.c | 569 +++++++++++++++++ miranda-wine/src/modules/netlib/netlibopts.c | 516 +++++++++++++++ miranda-wine/src/modules/netlib/netlibpktrecver.c | 85 +++ miranda-wine/src/modules/netlib/netlibsock.c | 166 +++++ miranda-wine/src/modules/netlib/netlibupnp.c | 495 +++++++++++++++ 11 files changed, 4517 insertions(+) create mode 100644 miranda-wine/src/modules/netlib/netlib.c create mode 100644 miranda-wine/src/modules/netlib/netlib.h create mode 100644 miranda-wine/src/modules/netlib/netlibbind.c create mode 100644 miranda-wine/src/modules/netlib/netlibhttp.c create mode 100644 miranda-wine/src/modules/netlib/netlibhttpproxy.c create mode 100644 miranda-wine/src/modules/netlib/netliblog.c create mode 100644 miranda-wine/src/modules/netlib/netlibopenconn.c create mode 100644 miranda-wine/src/modules/netlib/netlibopts.c create mode 100644 miranda-wine/src/modules/netlib/netlibpktrecver.c create mode 100644 miranda-wine/src/modules/netlib/netlibsock.c create mode 100644 miranda-wine/src/modules/netlib/netlibupnp.c (limited to 'miranda-wine/src/modules/netlib') diff --git a/miranda-wine/src/modules/netlib/netlib.c b/miranda-wine/src/modules/netlib/netlib.c new file mode 100644 index 0000000..72a0370 --- /dev/null +++ b/miranda-wine/src/modules/netlib/netlib.c @@ -0,0 +1,503 @@ +/* + +Miranda IM: the free IM client for Microsoft* Windows* + +Copyright 2000-2006 Miranda ICQ/IM project, +all portions of this codebase are copyrighted to the people +listed in contributors.txt. + +This program is free software; you can redistribute it and/or +modify it under the terms of the GNU General Public License +as published by the Free Software Foundation; either version 2 +of the License, or (at your option) any later version. + +This program is distributed in the hope that it will be useful, +but WITHOUT ANY WARRANTY; without even the implied warranty of +MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +GNU General Public License for more details. + +You should have received a copy of the GNU General Public License +along with this program; if not, write to the Free Software +Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. +*/ +#include "commonheaders.h" +#include "netlib.h" + +struct NetlibUser **netlibUser=NULL; +int netlibUserCount=0; +CRITICAL_SECTION csNetlibUser; +HANDLE hConnectionHeaderMutex; +DWORD g_LastConnectionTick; // protected by csNetlibUser + +void NetlibFreeUserSettingsStruct(NETLIBUSERSETTINGS *settings) +{ + if(settings->szIncomingPorts) mir_free(settings->szIncomingPorts); + if(settings->szOutgoingPorts) mir_free(settings->szOutgoingPorts); + if(settings->szProxyAuthPassword) mir_free(settings->szProxyAuthPassword); + if(settings->szProxyAuthUser) mir_free(settings->szProxyAuthUser); + if(settings->szProxyServer) mir_free(settings->szProxyServer); +} + +void NetlibInitializeNestedCS(struct NetlibNestedCriticalSection *nlncs) +{ + nlncs->dwOwningThreadId= 0; + nlncs->lockCount=0; + nlncs->hMutex=CreateMutex(NULL,FALSE,NULL); +} + +void NetlibDeleteNestedCS(struct NetlibNestedCriticalSection *nlncs) +{ + CloseHandle(nlncs->hMutex); +} + +int NetlibEnterNestedCS(struct NetlibConnection *nlc,int which) +{ + struct NetlibNestedCriticalSection *nlncs; + DWORD dwCurrentThreadId=GetCurrentThreadId(); + + WaitForSingleObject(hConnectionHeaderMutex,INFINITE); + if(nlc==NULL || nlc->handleType!=NLH_CONNECTION) { + ReleaseMutex(hConnectionHeaderMutex); + SetLastError(ERROR_INVALID_PARAMETER); + return 0; + } + nlncs=which==NLNCS_SEND?&nlc->ncsSend:&nlc->ncsRecv; + if(nlncs->lockCount && (nlc->ncsRecv.dwOwningThreadId==dwCurrentThreadId || nlc->ncsSend.dwOwningThreadId==dwCurrentThreadId)) { + nlncs->lockCount++; + ReleaseMutex(hConnectionHeaderMutex); + return 1; + } + InterlockedIncrement(&nlc->dontCloseNow); + ResetEvent(nlc->hOkToCloseEvent); + ReleaseMutex(hConnectionHeaderMutex); + WaitForSingleObject(nlncs->hMutex,INFINITE); + nlncs->dwOwningThreadId=dwCurrentThreadId; + nlncs->lockCount=1; + if(InterlockedDecrement(&nlc->dontCloseNow)==0) + SetEvent(nlc->hOkToCloseEvent); + return 1; +} + +void NetlibLeaveNestedCS(struct NetlibNestedCriticalSection *nlncs) +{ + if(--nlncs->lockCount==0) { + nlncs->dwOwningThreadId=0; + ReleaseMutex(nlncs->hMutex); + } +} + +static int GetNetlibUserSettingInt(const char *szUserModule,const char *szSetting,int defValue) +{ + DBVARIANT dbv; + if(DBGetContactSetting(NULL,szUserModule,szSetting,&dbv) + && DBGetContactSetting(NULL,"Netlib",szSetting,&dbv)) + return defValue; + if(dbv.type==DBVT_BYTE) return dbv.bVal; + if(dbv.type==DBVT_WORD) return dbv.wVal; + return dbv.dVal; +} + +static char *GetNetlibUserSettingString(const char *szUserModule,const char *szSetting,int decode) +{ + DBVARIANT dbv; + if(DBGetContactSetting(NULL,szUserModule,szSetting,&dbv) + && DBGetContactSetting(NULL,"Netlib",szSetting,&dbv)) { + return NULL; + } + else { + char *szRet; + if(decode) CallService(MS_DB_CRYPT_DECODESTRING, strlen(dbv.pszVal) + 1, (LPARAM)dbv.pszVal); + szRet=mir_strdup(dbv.pszVal); + DBFreeVariant(&dbv); + if(szRet==NULL) SetLastError(ERROR_OUTOFMEMORY); + return szRet; + } +} + +static int NetlibRegisterUser(WPARAM wParam,LPARAM lParam) +{ + NETLIBUSER *nlu=(NETLIBUSER*)lParam; + struct NetlibUser *thisUser; + int i; + + if(nlu==NULL || nlu->cbSize!=sizeof(NETLIBUSER) || nlu->szSettingsModule==NULL + || (!(nlu->flags&NUF_NOOPTIONS) && nlu->szDescriptiveName==NULL) + || (nlu->flags&NUF_HTTPGATEWAY && (nlu->pfnHttpGatewayInit==NULL))) { + SetLastError(ERROR_INVALID_PARAMETER); + return (int)(HANDLE)NULL; + } + + EnterCriticalSection(&csNetlibUser); + for(i=0;iuser.szSettingsModule,nlu->szSettingsModule)) { + LeaveCriticalSection(&csNetlibUser); + SetLastError(ERROR_DUP_NAME); + return (int)(HANDLE)NULL; + } + LeaveCriticalSection(&csNetlibUser); + + thisUser=(struct NetlibUser*)mir_calloc(sizeof(struct NetlibUser)); + thisUser->handleType=NLH_USER; + thisUser->user=*nlu; + if((thisUser->user.szSettingsModule=mir_strdup(nlu->szSettingsModule))==NULL + || (nlu->szDescriptiveName && (thisUser->user.szDescriptiveName=mir_strdup(nlu->szDescriptiveName))==NULL) + || (nlu->szHttpGatewayUserAgent && (thisUser->user.szHttpGatewayUserAgent=mir_strdup(nlu->szHttpGatewayUserAgent))==NULL)) { + SetLastError(ERROR_OUTOFMEMORY); + return (int)(HANDLE)NULL; + } + if (nlu->szHttpGatewayHello) + thisUser->user.szHttpGatewayHello=mir_strdup(nlu->szHttpGatewayHello); + else + thisUser->user.szHttpGatewayHello=NULL; + + thisUser->settings.cbSize=sizeof(NETLIBUSERSETTINGS); + thisUser->settings.useProxy=GetNetlibUserSettingInt(thisUser->user.szSettingsModule,"NLUseProxy",0); + thisUser->settings.proxyType=GetNetlibUserSettingInt(thisUser->user.szSettingsModule,"NLProxyType",PROXYTYPE_SOCKS5); + if(thisUser->user.flags&NUF_NOHTTPSOPTION && thisUser->settings.proxyType==PROXYTYPE_HTTPS) + thisUser->settings.proxyType=PROXYTYPE_HTTP; + if(!(thisUser->user.flags&(NUF_HTTPCONNS|NUF_HTTPGATEWAY)) && thisUser->settings.proxyType==PROXYTYPE_HTTP) { + thisUser->settings.useProxy=0; + thisUser->settings.proxyType=PROXYTYPE_SOCKS5; + } + thisUser->settings.szProxyServer=GetNetlibUserSettingString(thisUser->user.szSettingsModule,"NLProxyServer",0); + thisUser->settings.wProxyPort=GetNetlibUserSettingInt(thisUser->user.szSettingsModule,"NLProxyPort",1080); + thisUser->settings.useProxyAuth=GetNetlibUserSettingInt(thisUser->user.szSettingsModule,"NLUseProxyAuth",0); + thisUser->settings.szProxyAuthUser=GetNetlibUserSettingString(thisUser->user.szSettingsModule,"NLProxyAuthUser",0); + thisUser->settings.szProxyAuthPassword=GetNetlibUserSettingString(thisUser->user.szSettingsModule,"NLProxyAuthPassword",1); + thisUser->settings.useProxyAuthNtlm=GetNetlibUserSettingInt(thisUser->user.szSettingsModule,"NLUseProxyAuthNtlm",0); + thisUser->settings.dnsThroughProxy=GetNetlibUserSettingInt(thisUser->user.szSettingsModule,"NLDnsThroughProxy",1); + thisUser->settings.specifyIncomingPorts=GetNetlibUserSettingInt(thisUser->user.szSettingsModule,"NLSpecifyIncomingPorts",0); + thisUser->settings.szIncomingPorts=GetNetlibUserSettingString(thisUser->user.szSettingsModule,"NLIncomingPorts",0); + thisUser->settings.specifyOutgoingPorts=GetNetlibUserSettingInt(thisUser->user.szSettingsModule,"NLSpecifyOutgoingPorts",0); + thisUser->settings.szOutgoingPorts=GetNetlibUserSettingString(thisUser->user.szSettingsModule,"NLOutgoingPorts",0); + + EnterCriticalSection(&csNetlibUser); + netlibUser=(struct NetlibUser**)mir_realloc(netlibUser,sizeof(struct NetlibUser*)*++netlibUserCount); + netlibUser[netlibUserCount-1]=thisUser; + LeaveCriticalSection(&csNetlibUser); + return (int)thisUser; +} + +static int NetlibGetUserSettings(WPARAM wParam,LPARAM lParam) +{ + NETLIBUSERSETTINGS *nlus=(NETLIBUSERSETTINGS*)lParam; + struct NetlibUser *nlu=(struct NetlibUser*)wParam; + + if(GetNetlibHandleType(nlu)!=NLH_USER || nlus==NULL || nlus->cbSize!=sizeof(NETLIBUSERSETTINGS)) { + SetLastError(ERROR_INVALID_PARAMETER); + return 0; + } + *nlus=nlu->settings; + return 1; +} + +static int NetlibSetUserSettings(WPARAM wParam,LPARAM lParam) +{ + NETLIBUSERSETTINGS *nlus=(NETLIBUSERSETTINGS*)lParam; + struct NetlibUser *nlu=(struct NetlibUser*)wParam; + + if(GetNetlibHandleType(nlu)!=NLH_USER || nlus==NULL || nlus->cbSize!=sizeof(NETLIBUSERSETTINGS)) { + SetLastError(ERROR_INVALID_PARAMETER); + return 0; + } + NetlibFreeUserSettingsStruct(&nlu->settings); + NetlibSaveUserSettingsStruct(nlu->user.szSettingsModule,nlus); + return 1; +} + +int NetlibCloseHandle(WPARAM wParam,LPARAM lParam) +{ + switch(GetNetlibHandleType(wParam)) { + case NLH_USER: + { struct NetlibUser *nlu=(struct NetlibUser*)wParam; + int i; + EnterCriticalSection(&csNetlibUser); + for(i=0;iuser.szSettingsModule,nlu->user.szSettingsModule)) { + netlibUserCount--; + memmove(netlibUser+i,netlibUser+i+1,(netlibUserCount-i)*sizeof(struct NetlibUser*)); + break; + } + LeaveCriticalSection(&csNetlibUser); + NetlibFreeUserSettingsStruct(&nlu->settings); + if(nlu->user.szSettingsModule) mir_free(nlu->user.szSettingsModule); + if(nlu->user.szDescriptiveName) mir_free(nlu->user.szDescriptiveName); + if(nlu->user.szHttpGatewayHello) mir_free(nlu->user.szHttpGatewayHello); + if(nlu->user.szHttpGatewayUserAgent) mir_free(nlu->user.szHttpGatewayUserAgent); + if(nlu->szStickyHeaders) mir_free(nlu->szStickyHeaders); + break; + } + case NLH_CONNECTION: + { struct NetlibConnection *nlc=(struct NetlibConnection*)wParam; + HANDLE waitHandles[4]; + DWORD waitResult; + + WaitForSingleObject(hConnectionHeaderMutex,INFINITE); + if (nlc->usingHttpGateway) + { + struct NetlibHTTPProxyPacketQueue *p = nlc->pHttpProxyPacketQueue; + while (p != NULL) { + struct NetlibHTTPProxyPacketQueue *t = p; + + p = p->next; + + mir_free(t->dataBuffer); + mir_free(t); + } + } + else + { + if(nlc->handleType!=NLH_CONNECTION || nlc->s==INVALID_SOCKET) { + ReleaseMutex(hConnectionHeaderMutex); + SetLastError(ERROR_INVALID_PARAMETER); //already been closed + return 0; + } + closesocket(nlc->s); + nlc->s=INVALID_SOCKET; + } + ReleaseMutex(hConnectionHeaderMutex); + + waitHandles[0]=hConnectionHeaderMutex; + waitHandles[1]=nlc->hOkToCloseEvent; + waitHandles[2]=nlc->ncsRecv.hMutex; + waitHandles[3]=nlc->ncsSend.hMutex; + waitResult=WaitForMultipleObjects( SIZEOF(waitHandles),waitHandles,TRUE,INFINITE); + if(waitResult= WAIT_OBJECT_0 + SIZEOF(waitHandles)) { + ReleaseMutex(hConnectionHeaderMutex); + SetLastError(ERROR_INVALID_PARAMETER); //already been closed + return 0; + } + nlc->handleType=0; + if(nlc->nlhpi.szHttpPostUrl) mir_free(nlc->nlhpi.szHttpPostUrl); + if(nlc->nlhpi.szHttpGetUrl) mir_free(nlc->nlhpi.szHttpGetUrl); + if(nlc->dataBuffer) mir_free(nlc->dataBuffer); + if(nlc->hInstSecurityDll) FreeLibrary(nlc->hInstSecurityDll); + NetlibDeleteNestedCS(&nlc->ncsRecv); + NetlibDeleteNestedCS(&nlc->ncsSend); + CloseHandle(nlc->hOkToCloseEvent); + DeleteCriticalSection(&nlc->csHttpSequenceNums); + ReleaseMutex(hConnectionHeaderMutex); + Netlib_Logf(nlc->nlu,"(%p:%u) Connection closed",nlc,nlc->s); + break; + } + case NLH_BOUNDPORT: + return NetlibFreeBoundPort((struct NetlibBoundPort*)wParam); + case NLH_PACKETRECVER: + { struct NetlibPacketRecver *nlpr=(struct NetlibPacketRecver*)wParam; + mir_free(nlpr->packetRecver.buffer); + break; + } + default: + SetLastError(ERROR_INVALID_PARAMETER); + return 0; + } + mir_free((void*)wParam); + return 1; +} + +static int NetlibGetSocket(WPARAM wParam,LPARAM lParam) +{ + SOCKET s; + if((void*)wParam==NULL) { + s=INVALID_SOCKET; + SetLastError(ERROR_INVALID_PARAMETER); + } + else { + WaitForSingleObject(hConnectionHeaderMutex,INFINITE); + switch(GetNetlibHandleType(wParam)) { + case NLH_CONNECTION: + s=(int)((struct NetlibConnection*)wParam)->s; + break; + case NLH_BOUNDPORT: + s=(int)((struct NetlibBoundPort*)wParam)->s; + break; + default: + s=INVALID_SOCKET; + SetLastError(ERROR_INVALID_PARAMETER); + break; + } + ReleaseMutex(hConnectionHeaderMutex); + } + return s; +} + +static char szHexDigits[]="0123456789ABCDEF"; +int NetlibHttpUrlEncode(WPARAM wParam,LPARAM lParam) +{ + unsigned char *szOutput,*szInput=(unsigned char*)lParam; + unsigned char *pszIn,*pszOut; + int outputLen; + + if(szInput==NULL) { + SetLastError(ERROR_INVALID_PARAMETER); + return (int)(char*)NULL; + } + for(outputLen=0,pszIn=szInput;*pszIn;pszIn++) { + if(isalnum(*pszIn) || *pszIn==' ') outputLen++; + else outputLen+=3; + } + szOutput=(unsigned char*)HeapAlloc(GetProcessHeap(),0,outputLen+1); + if(szOutput==NULL) { + SetLastError(ERROR_OUTOFMEMORY); + return (int)(unsigned char*)NULL; + } + for(pszOut=szOutput,pszIn=szInput;*pszIn;pszIn++) { + if(isalnum(*pszIn)) *pszOut++=*pszIn; + else if(*pszIn==' ') *pszOut++='+'; + else { + *pszOut++='%'; + *pszOut++=szHexDigits[*pszIn>>4]; + *pszOut++=szHexDigits[*pszIn&0xF]; + } + } + *pszOut='\0'; + return (int)szOutput; +} + +static char base64chars[]="ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789+/"; +int NetlibBase64Encode(WPARAM wParam,LPARAM lParam) +{ + NETLIBBASE64 *nlb64=(NETLIBBASE64*)lParam; + int iIn; + char *pszOut; + PBYTE pbIn; + + if(nlb64==NULL || nlb64->pszEncoded==NULL || nlb64->pbDecoded==NULL) { + SetLastError(ERROR_INVALID_PARAMETER); + return 0; + } + if(nlb64->cchEncodedcbDecoded)) { + SetLastError(ERROR_BUFFER_OVERFLOW); + return 0; + } + nlb64->cchEncoded=Netlib_GetBase64EncodedBufferSize(nlb64->cbDecoded); + for(iIn=0,pbIn=nlb64->pbDecoded,pszOut=nlb64->pszEncoded;iIncbDecoded;iIn+=3,pbIn+=3,pszOut+=4) { + pszOut[0]=base64chars[pbIn[0]>>2]; + if(nlb64->cbDecoded-iIn==1) { + pszOut[1]=base64chars[(pbIn[0]&3)<<4]; + pszOut[2]='='; + pszOut[3]='='; + pszOut+=4; + break; + } + pszOut[1]=base64chars[((pbIn[0]&3)<<4)|(pbIn[1]>>4)]; + if(nlb64->cbDecoded-iIn==2) { + pszOut[2]=base64chars[(pbIn[1]&0xF)<<2]; + pszOut[3]='='; + pszOut+=4; + break; + } + pszOut[2]=base64chars[((pbIn[1]&0xF)<<2)|(pbIn[2]>>6)]; + pszOut[3]=base64chars[pbIn[2]&0x3F]; + } + pszOut[0]='\0'; + return 1; +} + +static BYTE Base64CharToInt(char c) +{ + if(c>='A' && c<='Z') return c-'A'; + if(c>='a' && c<='z') return c-'a'+26; + if(c>='0' && c<='9') return c-'0'+52; + if(c=='+') return 62; + if(c=='/') return 63; + if(c=='=') return 64; + return 255; +} + +int NetlibBase64Decode(WPARAM wParam,LPARAM lParam) +{ + NETLIBBASE64 *nlb64=(NETLIBBASE64*)lParam; + char *pszIn; + PBYTE pbOut; + BYTE b1,b2,b3,b4; + int iIn; + + if(nlb64==NULL || nlb64->pszEncoded==NULL || nlb64->pbDecoded==NULL) { + SetLastError(ERROR_INVALID_PARAMETER); + return 0; + } + if(nlb64->cchEncoded&3) { + SetLastError(ERROR_INVALID_DATA); + return 0; + } + if(nlb64->cbDecodedcchEncoded)) { + SetLastError(ERROR_BUFFER_OVERFLOW); + return 0; + } + nlb64->cbDecoded=Netlib_GetBase64DecodedBufferSize(nlb64->cchEncoded); + for(iIn=0,pszIn=nlb64->pszEncoded,pbOut=nlb64->pbDecoded;iIncchEncoded;iIn+=4,pszIn+=4,pbOut+=3) { + b1=Base64CharToInt(pszIn[0]); + b2=Base64CharToInt(pszIn[1]); + b3=Base64CharToInt(pszIn[2]); + b4=Base64CharToInt(pszIn[3]); + if(b1==255 || b1==64 || b2==255 || b2==64 || b3==255 || b4==255) { + SetLastError(ERROR_INVALID_DATA); + return 0; + } + pbOut[0]=(b1<<2)|(b2>>4); + if(b3==64) {nlb64->cbDecoded-=2; break;} + pbOut[1]=(b2<<4)|(b3>>2); + if(b4==64) {nlb64->cbDecoded--; break;} + pbOut[2]=b4|(b3<<6); + } + return 1; +} + +static int NetlibShutdown(WPARAM wParam,LPARAM lParam) +{ + int i; + + NetlibLogShutdown(); + for(i=netlibUserCount;i>0;i--) + NetlibCloseHandle((WPARAM)netlibUser[i-1],0); + if(netlibUser) mir_free(netlibUser); + CloseHandle(hConnectionHeaderMutex); + DeleteCriticalSection(&csNetlibUser); + WSACleanup(); + return 0; +} + +static int NetlibModulesLoaded(WPARAM wParam, LPARAM lParam) +{ + HookEvent(ME_SYSTEM_SHUTDOWN,NetlibShutdown); // get shutdown hook _after_ all the other plugins + return 0; +} + +int LoadNetlibModule(void) +{ + WSADATA wsadata; + + //HookEvent(ME_SYSTEM_SHUTDOWN,NetlibShutdown); // hooked later to be called last after plugins + HookEvent(ME_SYSTEM_MODULESLOADED, NetlibModulesLoaded); + HookEvent(ME_OPT_INITIALISE,NetlibOptInitialise); + WSAStartup(MAKEWORD(1,1), &wsadata); + InitializeCriticalSection(&csNetlibUser); + hConnectionHeaderMutex=CreateMutex(NULL,FALSE,NULL); + g_LastConnectionTick=GetTickCount(); + NetlibLogInit(); + CreateServiceFunction(MS_NETLIB_REGISTERUSER,NetlibRegisterUser); + CreateServiceFunction(MS_NETLIB_GETUSERSETTINGS,NetlibGetUserSettings); + CreateServiceFunction(MS_NETLIB_SETUSERSETTINGS,NetlibSetUserSettings); + CreateServiceFunction(MS_NETLIB_CLOSEHANDLE,NetlibCloseHandle); + CreateServiceFunction(MS_NETLIB_BINDPORT,NetlibBindPort); + CreateServiceFunction(MS_NETLIB_OPENCONNECTION,NetlibOpenConnection); + CreateServiceFunction(MS_NETLIB_SETHTTPPROXYINFO,NetlibHttpGatewaySetInfo); + CreateServiceFunction(MS_NETLIB_SETSTICKYHEADERS,NetlibHttpSetSticky); + CreateServiceFunction(MS_NETLIB_GETSOCKET,NetlibGetSocket); + CreateServiceFunction(MS_NETLIB_URLENCODE,NetlibHttpUrlEncode); + CreateServiceFunction(MS_NETLIB_BASE64ENCODE,NetlibBase64Encode); + CreateServiceFunction(MS_NETLIB_BASE64DECODE,NetlibBase64Decode); + CreateServiceFunction(MS_NETLIB_SENDHTTPREQUEST,NetlibHttpSendRequest); + CreateServiceFunction(MS_NETLIB_RECVHTTPHEADERS,NetlibHttpRecvHeaders); + CreateServiceFunction(MS_NETLIB_FREEHTTPREQUESTSTRUCT,NetlibHttpFreeRequestStruct); + CreateServiceFunction(MS_NETLIB_HTTPTRANSACTION,NetlibHttpTransaction); + CreateServiceFunction(MS_NETLIB_SEND,NetlibSend); + CreateServiceFunction(MS_NETLIB_RECV,NetlibRecv); + CreateServiceFunction(MS_NETLIB_SELECT,NetlibSelect); + CreateServiceFunction(MS_NETLIB_SELECTEX,NetlibSelectEx); + CreateServiceFunction(MS_NETLIB_CREATEPACKETRECVER,NetlibPacketRecverCreate); + CreateServiceFunction(MS_NETLIB_GETMOREPACKETS,NetlibPacketRecverGetMore); + CreateServiceFunction(MS_NETLIB_SETPOLLINGTIMEOUT,NetlibHttpSetPollingTimeout); + return 0; +} diff --git a/miranda-wine/src/modules/netlib/netlib.h b/miranda-wine/src/modules/netlib/netlib.h new file mode 100644 index 0000000..8314397 --- /dev/null +++ b/miranda-wine/src/modules/netlib/netlib.h @@ -0,0 +1,157 @@ +/* + +Miranda IM: the free IM client for Microsoft* Windows* + +Copyright 2000-2006 Miranda ICQ/IM project, +all portions of this codebase are copyrighted to the people +listed in contributors.txt. + +This program is free software; you can redistribute it and/or +modify it under the terms of the GNU General Public License +as published by the Free Software Foundation; either version 2 +of the License, or (at your option) any later version. + +This program is distributed in the hope that it will be useful, +but WITHOUT ANY WARRANTY; without even the implied warranty of +MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +GNU General Public License for more details. + +You should have received a copy of the GNU General Public License +along with this program; if not, write to the Free Software +Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. +*/ + +#define GetNetlibHandleType(h) (h?*(int*)h:NLH_INVALID) +#define NLH_INVALID 0 +#define NLH_USER 'USER' +#define NLH_CONNECTION 'CONN' +#define NLH_BOUNDPORT 'BIND' +#define NLH_PACKETRECVER 'PCKT' + +struct NetlibUser { + int handleType; + NETLIBUSER user; + NETLIBUSERSETTINGS settings; + char * szStickyHeaders; +}; + +struct NetlibNestedCriticalSection { + HANDLE hMutex; + DWORD dwOwningThreadId; + int lockCount; +}; + +struct NetlibHTTPProxyPacketQueue { + struct NetlibHTTPProxyPacketQueue *next; + PBYTE dataBuffer; + int dataBufferLen; +}; + +struct NetlibConnection { + int handleType; + SOCKET s; + int usingHttpGateway; + struct NetlibUser *nlu; + SOCKADDR_IN sinProxy; + NETLIBHTTPPROXYINFO nlhpi; + PBYTE dataBuffer; + int dataBufferLen; + DWORD dwLastGetSentTime; + CRITICAL_SECTION csHttpSequenceNums; + HANDLE hOkToCloseEvent; + LONG dontCloseNow; + struct NetlibNestedCriticalSection ncsSend,ncsRecv; + HINSTANCE hInstSecurityDll; + struct NetlibHTTPProxyPacketQueue * pHttpProxyPacketQueue; + int pollingTimeout; +}; + +struct NetlibBoundPort { + int handleType; + SOCKET s; + WORD wPort; + WORD wExPort; + struct NetlibUser *nlu; + NETLIBNEWCONNECTIONPROC_V2 pfnNewConnectionV2; + HANDLE hThread; + void *pExtra; +}; + +struct NetlibPacketRecver { + int handleType; + struct NetlibConnection *nlc; + NETLIBPACKETRECVER packetRecver; +}; + +//netlib.c +void NetlibFreeUserSettingsStruct(NETLIBUSERSETTINGS *settings); +int NetlibCloseHandle(WPARAM wParam,LPARAM lParam); +void NetlibInitializeNestedCS(struct NetlibNestedCriticalSection *nlncs); +void NetlibDeleteNestedCS(struct NetlibNestedCriticalSection *nlncs); +#define NLNCS_SEND 0 +#define NLNCS_RECV 1 +int NetlibEnterNestedCS(struct NetlibConnection *nlc,int which); +void NetlibLeaveNestedCS(struct NetlibNestedCriticalSection *nlncs); +int NetlibBase64Encode(WPARAM wParam,LPARAM lParam); +int NetlibBase64Decode(WPARAM wParam,LPARAM lParam); +int NetlibHttpUrlEncode(WPARAM wParam,LPARAM lParam); + +//netlibbind.c +int NetlibFreeBoundPort(struct NetlibBoundPort *nlbp); +int NetlibBindPort(WPARAM wParam,LPARAM lParam); +int StringToPortsMask(const char *szPorts,BYTE *mask); + +//netlibhttp.c +int NetlibHttpSendRequest(WPARAM wParam,LPARAM lParam); +int NetlibHttpRecvHeaders(WPARAM wParam,LPARAM lParam); +int NetlibHttpFreeRequestStruct(WPARAM wParam,LPARAM lParam); +int NetlibHttpTransaction(WPARAM wParam,LPARAM lParam); +void NetlibHttpSetLastErrorUsingHttpResult(int result); + +//netlibhttpproxy.c +int NetlibInitHttpConnection(struct NetlibConnection *nlc,struct NetlibUser *nlu,NETLIBOPENCONNECTION *nloc); +int NetlibHttpGatewaySetInfo(WPARAM wParam,LPARAM lParam); +int NetlibHttpSetPollingTimeout(WPARAM wParam,LPARAM lParam); +int NetlibHttpGatewayRecv(struct NetlibConnection *nlc,char *buf,int len,int flags); +int NetlibHttpGatewayPost(struct NetlibConnection *nlc,const char *buf,int len,int flags); +int NetlibHttpSetSticky(WPARAM wParam, LPARAM lParam); + +//netliblog.c +void NetlibLogShowOptions(void); +void NetlibDumpData(struct NetlibConnection *nlc,PBYTE buf,int len,int sent,int flags); +void NetlibLogInit(void); +void NetlibLogShutdown(void); + +//netlibopenconn.c +DWORD DnsLookup(struct NetlibUser *nlu,const char *szHost); +int WaitUntilReadable(SOCKET s,DWORD dwTimeout); +int NetlibOpenConnection(WPARAM wParam,LPARAM lParam); + +//netlibopts.c +int NetlibOptInitialise(WPARAM wParam,LPARAM lParam); +void NetlibSaveUserSettingsStruct(const char *szSettingsModule,NETLIBUSERSETTINGS *settings); + +//netlibpktrecver.c +int NetlibPacketRecverCreate(WPARAM wParam,LPARAM lParam); +int NetlibPacketRecverGetMore(WPARAM wParam,LPARAM lParam); + +//netlibsock.c +int NetlibSend(WPARAM wParam,LPARAM lParam); +int NetlibRecv(WPARAM wParam,LPARAM lParam); +int NetlibSelect(WPARAM wParam,LPARAM lParam); +int NetlibSelectEx(WPARAM wParam,LPARAM lParam); + +//netlibupnp.c +BOOL NetlibUPnPAddPortMapping(WORD intport, char *proto, + WORD *extport, DWORD *extip, BOOL search); +void NetlibUPnPDeletePortMapping(WORD extport, char* proto); + +static __inline int NLSend(struct NetlibConnection *nlc,const char *buf,int len,int flags) { + NETLIBBUFFER nlb={(char*)buf,len,flags}; + return NetlibSend((WPARAM)nlc,(LPARAM)&nlb); +} +static __inline int NLRecv(struct NetlibConnection *nlc,char *buf,int len,int flags) { + NETLIBBUFFER nlb={buf,len,flags}; + return NetlibRecv((WPARAM)nlc,(LPARAM)&nlb); +} + diff --git a/miranda-wine/src/modules/netlib/netlibbind.c b/miranda-wine/src/modules/netlib/netlibbind.c new file mode 100644 index 0000000..383159f --- /dev/null +++ b/miranda-wine/src/modules/netlib/netlibbind.c @@ -0,0 +1,247 @@ +/* + +Miranda IM: the free IM client for Microsoft* Windows* + +Copyright 2000-2006 Miranda ICQ/IM project, +all portions of this codebase are copyrighted to the people +listed in contributors.txt. + +This program is free software; you can redistribute it and/or +modify it under the terms of the GNU General Public License +as published by the Free Software Foundation; either version 2 +of the License, or (at your option) any later version. + +This program is distributed in the hope that it will be useful, +but WITHOUT ANY WARRANTY; without even the implied warranty of +MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +GNU General Public License for more details. + +You should have received a copy of the GNU General Public License +along with this program; if not, write to the Free Software +Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. +*/ +#include "commonheaders.h" +#include "netlib.h" + +//mask must be 8192 bytes, returns number of bits set +#define PortInMask(mask,p) ((mask)[((p)&0xFFFF)>>3]&(1<<((p)&7))) +int StringToPortsMask(const char *szPorts,BYTE *mask) +{ + const char *psz; + char *pszEnd; + int portMin,portMax,port; + int bitCount=0; + + ZeroMemory(mask,8192); + for(psz=szPorts;*psz;) { + while(*psz==' ' && *psz==',') psz++; + portMin=strtol(psz,&pszEnd,0); + if(pszEnd==psz) break; + while(*pszEnd==' ') pszEnd++; + if(*pszEnd=='-') { + psz=pszEnd+1; + portMax=strtol(psz,&pszEnd,0); + if(pszEnd==psz) portMax=65535; + if(portMin>portMax) { + port=portMin; + portMin=portMax; + portMax=port; + } + } + else portMax=portMin; + if(portMax>=1) { + if(portMin<=0) portMin=1; + for(port=portMin;port<=portMax;port++) { + if(port>65535) break; + if((port&7)==0 && portMax-port>=7) {mask[port>>3]=0xFF; port+=7; bitCount+=8;} + else {mask[port>>3]|=1<<(port&7); bitCount++;} + } + } + psz=pszEnd; + } + return bitCount; +} + +int NetlibFreeBoundPort(struct NetlibBoundPort *nlbp) +{ + closesocket(nlbp->s); + WaitForSingleObject(nlbp->hThread,INFINITE); + CloseHandle(nlbp->hThread); + NetlibUPnPDeletePortMapping(nlbp->wExPort, "TCP"); + mir_free(nlbp); + return 1; +} + +static DWORD __stdcall NetlibBindAcceptThread(struct NetlibBoundPort *nlbp) +{ + SOCKET s; + SOCKADDR_IN sin; + int sinLen; + struct NetlibConnection *nlc; + + srand((unsigned int)time(NULL)); + Netlib_Logf(nlbp->nlu,"(%d) Port %u opened for incoming connections",nlbp->s,nlbp->wPort); + for(;;) { + sinLen=sizeof(sin); + s=accept(nlbp->s,(struct sockaddr*)&sin,&sinLen); + if(s==INVALID_SOCKET) break; + Netlib_Logf(nlbp->nlu,"New incoming connection on port %u from %s (%d)",nlbp->wPort, inet_ntoa(sin.sin_addr),s); + nlc=(struct NetlibConnection*)mir_alloc(sizeof(struct NetlibConnection)); + memset(nlc,0,sizeof(struct NetlibConnection)); + nlc->handleType=NLH_CONNECTION; + nlc->nlu=nlbp->nlu; + nlc->s=s; + InitializeCriticalSection(&nlc->csHttpSequenceNums); + nlc->hOkToCloseEvent=CreateEvent(NULL,TRUE,TRUE,NULL); + nlc->dontCloseNow=0; + NetlibInitializeNestedCS(&nlc->ncsSend); + NetlibInitializeNestedCS(&nlc->ncsRecv); + nlbp->pfnNewConnectionV2((HANDLE)nlc,ntohl(sin.sin_addr.S_un.S_addr), nlbp->pExtra); + } + return 0; +} + +int NetlibBindPort(WPARAM wParam,LPARAM lParam) +{ + NETLIBBIND *nlb=(NETLIBBIND*)lParam; + struct NetlibUser *nlu=(struct NetlibUser*)wParam; + struct NetlibBoundPort *nlbp; + SOCKADDR_IN sin; + int foundPort=0; + DWORD dwThreadId; + + if(GetNetlibHandleType(nlu)!=NLH_USER || !(nlu->user.flags&NUF_INCOMING) || nlb==NULL || nlb->pfnNewConnection==NULL) { + SetLastError(ERROR_INVALID_PARAMETER); + return (int)(HANDLE)NULL; + } + if ( nlb->cbSize != sizeof(NETLIBBIND) && + nlb->cbSize != NETLIBBIND_SIZEOF_V2 && + nlb->cbSize != NETLIBBIND_SIZEOF_V1 ) + { + return (int)(HANDLE)NULL; + } + nlbp=(struct NetlibBoundPort*)mir_alloc(sizeof(struct NetlibBoundPort)); + nlbp->handleType=NLH_BOUNDPORT; + nlbp->nlu=nlu; + nlbp->pfnNewConnectionV2=nlb->pfnNewConnectionV2; + nlbp->s=socket(AF_INET,SOCK_STREAM,0); + nlbp->pExtra= (nlb->cbSize == sizeof(NETLIBBIND)) ? nlb->pExtra : NULL; + if(nlbp->s==INVALID_SOCKET) { + Netlib_Logf(nlu,"%s %d: %s() failed (%u)",__FILE__,__LINE__,"socket",WSAGetLastError()); + mir_free(nlbp); + return (int)(HANDLE)NULL; + } + sin.sin_family=AF_INET; + sin.sin_addr.s_addr=htonl(INADDR_ANY); + sin.sin_port=0; + /* if the netlib user wanted a free port given in the range, then + they better have given wPort==0, let's hope so */ + if(nlu->settings.specifyIncomingPorts && nlb->wPort==0) { + int startPort,portNum,i,j; + BYTE portsMask[8192]; + int portsCount; + + portsCount=StringToPortsMask(nlu->settings.szIncomingPorts,portsMask); + if(portsCount==0) { + closesocket(nlbp->s); + mir_free(nlbp); + SetLastError(WSAEADDRINUSE); + return (int)(HANDLE)NULL; + } + startPort=rand()%portsCount; + for(i=0;i<8192;i++) { + if(portsMask[i]==0) continue; + if(portsMask[i]==0xFF && startPort>=8) {startPort-=8; continue;} + for(j=0;j<8;j++) + if(portsMask[i]&(1<s,(SOCKADDR *)&sin,sizeof(sin))==0) { + foundPort=1; + break; + } + for(portNum++;!PortInMask(portsMask,portNum);portNum++) + if(portNum==65535) portNum=0; + } while(portNum!=startPort); + } + else { + /* if ->wPort==0 then they'll get any free port, otherwise they'll + be asking for whatever was in nlb->wPort*/ + if (nlb->wPort!=0) { + Netlib_Logf(nlu,"%s %d: trying to bind port %d, this 'feature' can be abused, please be sure you want to allow it.",__FILE__,__LINE__,nlb->wPort); + sin.sin_port=htons(nlb->wPort); + } + if(bind(nlbp->s,(SOCKADDR *)&sin,sizeof(sin))==0) foundPort=1; + } + if(!foundPort) { + Netlib_Logf(nlu,"%s %d: %s() failed (%u)",__FILE__,__LINE__,"bind",WSAGetLastError()); + closesocket(nlbp->s); + mir_free(nlbp); + return (int)(HANDLE)NULL; + } + + if(listen(nlbp->s,5)) { + Netlib_Logf(nlu,"%s %d: %s() failed (%u)",__FILE__,__LINE__,"listen",WSAGetLastError()); + closesocket(nlbp->s); + mir_free(nlbp); + return (int)(HANDLE)NULL; + } + + { int len; + DWORD extIP; + + ZeroMemory(&sin,sizeof(sin)); + len=sizeof(sin); + if(getsockname(nlbp->s,(SOCKADDR *)&sin,&len)) { + Netlib_Logf(nlu,"%s %d: %s() failed (%u)",__FILE__,__LINE__,"getsockname",WSAGetLastError()); + closesocket(nlbp->s); + mir_free(nlbp); + return (int)(HANDLE)NULL; + } + nlb->wPort=ntohs(sin.sin_port); + nlbp->wPort=nlb->wPort; + nlb->dwInternalIP=ntohl(sin.sin_addr.S_un.S_addr); + + if (nlb->dwInternalIP == 0) + { + char hostname[64]; + struct hostent *he; + + gethostname(hostname,SIZEOF(hostname)); + he=gethostbyname(hostname); + if(he->h_addr_list[0]) + nlb->dwInternalIP=ntohl(*(PDWORD)he->h_addr_list[0]); + } + if (NetlibUPnPAddPortMapping(nlb->wPort, "TCP", &nlbp->wExPort, + &extIP, nlb->cbSize > NETLIBBIND_SIZEOF_V2)) + { + if (nlb->cbSize > NETLIBBIND_SIZEOF_V2) + { + nlb->wExPort = nlbp->wExPort; + nlb->dwExternalIP = extIP; + } + } + else + { + nlbp->wExPort = 0; + if (nlb->cbSize > NETLIBBIND_SIZEOF_V2) + { + nlb->wExPort = nlb->wPort; + nlb->dwExternalIP = nlb->dwInternalIP; + } + } + + } + nlbp->hThread=(HANDLE)forkthreadex(NULL,0,NetlibBindAcceptThread,nlbp,0,&dwThreadId); + return (int)nlbp; +} + diff --git a/miranda-wine/src/modules/netlib/netlibhttp.c b/miranda-wine/src/modules/netlib/netlibhttp.c new file mode 100644 index 0000000..5e47116 --- /dev/null +++ b/miranda-wine/src/modules/netlib/netlibhttp.c @@ -0,0 +1,729 @@ +/* + +Miranda IM: the free IM client for Microsoft* Windows* + +Copyright 2000-2006 Miranda ICQ/IM project, +all portions of this codebase are copyrighted to the people +listed in contributors.txt. + +This program is free software; you can redistribute it and/or +modify it under the terms of the GNU General Public License +as published by the Free Software Foundation; either version 2 +of the License, or (at your option) any later version. + +This program is distributed in the hope that it will be useful, +but WITHOUT ANY WARRANTY; without even the implied warranty of +MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +GNU General Public License for more details. + +You should have received a copy of the GNU General Public License +along with this program; if not, write to the Free Software +Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. +*/ +#include "commonheaders.h" +#define SECURITY_WIN32 +#include +#include "netlib.h" + +// Old versions of ssapi.h defines "FreeCredentialHandle()", but the +// function has since then been redefined to "FreeCredentialsHandle()" +#ifndef FreeCredentialsHandle +#define FreeCredentialsHandle FreeCredentialHandle +#endif + +#define HTTPRECVHEADERSTIMEOUT 60000 //in ms + +struct ResizableCharBuffer { + char *sz; + int iEnd,cbAlloced; +}; + +static void AppendToCharBuffer(struct ResizableCharBuffer *rcb,const char *fmt,...) +{ + va_list va; + int charsDone; + + if(rcb->cbAlloced==0) { + rcb->cbAlloced=512; + rcb->sz=(char*)mir_alloc(rcb->cbAlloced); + } + va_start(va,fmt); + for(;;) { + charsDone=mir_vsnprintf(rcb->sz+rcb->iEnd,rcb->cbAlloced-rcb->iEnd,fmt,va); + if(charsDone>=0) break; + rcb->cbAlloced+=512; + rcb->sz=(char*)mir_realloc(rcb->sz,rcb->cbAlloced); + } + va_end(va); + rcb->iEnd+=charsDone; +} + + +static PSecurityFunctionTableA pSecurityFunctions=NULL; +static PSecPkgInfoA ntlmSecurityPackageInfo=NULL; +static CtxtHandle hNtlmClientContext; +static CredHandle hNtlmClientCredential; +//mir_free() the return value +static void NtlmDestroy(void) +{ + if (pSecurityFunctions) + { + pSecurityFunctions->DeleteSecurityContext(&hNtlmClientContext); + pSecurityFunctions->FreeCredentialsHandle(&hNtlmClientCredential); + } //if + if(ntlmSecurityPackageInfo) pSecurityFunctions->FreeContextBuffer(ntlmSecurityPackageInfo); + ntlmSecurityPackageInfo=NULL; +} + +static char *NtlmInitialiseAndGetDomainPacket(HINSTANCE hInstSecurityDll) +{ + PSecurityFunctionTableA (*MyInitSecurityInterface)(VOID); + SECURITY_STATUS securityStatus; + SecBufferDesc outputBufferDescriptor; + SecBuffer outputSecurityToken; + TimeStamp tokenExpiration; + ULONG contextAttributes; + NETLIBBASE64 nlb64; + + MyInitSecurityInterface=(PSecurityFunctionTableA (*)(VOID))GetProcAddress(hInstSecurityDll,"InitSecurityInterfaceA"); + if(MyInitSecurityInterface==NULL) {NtlmDestroy(); return NULL;} + pSecurityFunctions=MyInitSecurityInterface(); + if(pSecurityFunctions==NULL) {NtlmDestroy(); return NULL;} + + securityStatus=pSecurityFunctions->QuerySecurityPackageInfoA("NTLM",&ntlmSecurityPackageInfo); + if(securityStatus!=SEC_E_OK) {NtlmDestroy(); return NULL;} + securityStatus=pSecurityFunctions->AcquireCredentialsHandleA(NULL,"NTLM",SECPKG_CRED_OUTBOUND,NULL,NULL,NULL,NULL,&hNtlmClientCredential,&tokenExpiration); + if(securityStatus!=SEC_E_OK) {NtlmDestroy(); return NULL;} + + outputBufferDescriptor.cBuffers=1; + outputBufferDescriptor.pBuffers=&outputSecurityToken; + outputBufferDescriptor.ulVersion=SECBUFFER_VERSION; + outputSecurityToken.BufferType=SECBUFFER_TOKEN; + outputSecurityToken.cbBuffer=ntlmSecurityPackageInfo->cbMaxToken; + outputSecurityToken.pvBuffer=mir_alloc(outputSecurityToken.cbBuffer); + if(outputSecurityToken.pvBuffer==NULL) {NtlmDestroy(); SetLastError(ERROR_OUTOFMEMORY); return NULL;} + securityStatus=pSecurityFunctions->InitializeSecurityContextA(&hNtlmClientCredential,NULL,NULL,0,0,SECURITY_NATIVE_DREP,NULL,0,&hNtlmClientContext,&outputBufferDescriptor,&contextAttributes,&tokenExpiration); + if(securityStatus!=SEC_I_CONTINUE_NEEDED) {mir_free(outputSecurityToken.pvBuffer); NtlmDestroy(); return NULL;} + + nlb64.cbDecoded=outputSecurityToken.cbBuffer; + nlb64.pbDecoded=outputSecurityToken.pvBuffer; + nlb64.cchEncoded=Netlib_GetBase64EncodedBufferSize(nlb64.cbDecoded); + nlb64.pszEncoded=(char*)mir_alloc(nlb64.cchEncoded); + if(nlb64.pszEncoded==NULL) {mir_free(outputSecurityToken.pvBuffer); NtlmDestroy(); SetLastError(ERROR_OUTOFMEMORY); return NULL;} + if(!NetlibBase64Encode(0,(LPARAM)&nlb64)) + {mir_free(nlb64.pszEncoded); mir_free(outputSecurityToken.pvBuffer); NtlmDestroy(); return NULL;} + mir_free(outputSecurityToken.pvBuffer); + return nlb64.pszEncoded; +} + +//mir_free() the result value +static char *NtlmCreateResponseFromChallenge(char *szChallenge) +{ + SECURITY_STATUS securityStatus; + SecBufferDesc outputBufferDescriptor,inputBufferDescriptor; + SecBuffer outputSecurityToken,inputSecurityToken; + TimeStamp tokenExpiration; + ULONG contextAttributes; + NETLIBBASE64 nlb64; + + nlb64.cchEncoded=lstrlenA(szChallenge); + nlb64.pszEncoded=szChallenge; + nlb64.cbDecoded=Netlib_GetBase64DecodedBufferSize(nlb64.cchEncoded); + nlb64.pbDecoded=(PBYTE)mir_alloc(nlb64.cbDecoded); + if(nlb64.pbDecoded==NULL) {SetLastError(ERROR_OUTOFMEMORY); return NULL;} + if(!NetlibBase64Decode(0,(LPARAM)&nlb64)) + {mir_free(nlb64.pbDecoded); return NULL;} + + inputBufferDescriptor.cBuffers=1; + inputBufferDescriptor.pBuffers=&inputSecurityToken; + inputBufferDescriptor.ulVersion=SECBUFFER_VERSION; + inputSecurityToken.BufferType=SECBUFFER_TOKEN; + inputSecurityToken.cbBuffer=nlb64.cbDecoded; + inputSecurityToken.pvBuffer=nlb64.pbDecoded; + outputBufferDescriptor.cBuffers=1; + outputBufferDescriptor.pBuffers=&outputSecurityToken; + outputBufferDescriptor.ulVersion=SECBUFFER_VERSION; + outputSecurityToken.BufferType=SECBUFFER_TOKEN; + outputSecurityToken.cbBuffer=ntlmSecurityPackageInfo->cbMaxToken; + outputSecurityToken.pvBuffer=mir_alloc(outputSecurityToken.cbBuffer); + if(outputSecurityToken.pvBuffer==NULL) {mir_free(nlb64.pbDecoded); SetLastError(ERROR_OUTOFMEMORY); return NULL;} + securityStatus=pSecurityFunctions->InitializeSecurityContextA(&hNtlmClientCredential,&hNtlmClientContext,NULL,0,0,SECURITY_NATIVE_DREP,&inputBufferDescriptor,0,&hNtlmClientContext,&outputBufferDescriptor,&contextAttributes,&tokenExpiration); + mir_free(nlb64.pbDecoded); + if(securityStatus!=SEC_E_OK) {mir_free(outputSecurityToken.pvBuffer); return NULL;} + + nlb64.cbDecoded=outputSecurityToken.cbBuffer; + nlb64.pbDecoded=outputSecurityToken.pvBuffer; + nlb64.cchEncoded=Netlib_GetBase64EncodedBufferSize(nlb64.cbDecoded); + nlb64.pszEncoded=(char*)mir_alloc(nlb64.cchEncoded); + if(nlb64.pszEncoded==NULL) {mir_free(outputSecurityToken.pvBuffer); SetLastError(ERROR_OUTOFMEMORY); return NULL;} + if(!NetlibBase64Encode(0,(LPARAM)&nlb64)) + {mir_free(nlb64.pszEncoded); mir_free(outputSecurityToken.pvBuffer); return NULL;} + mir_free(outputSecurityToken.pvBuffer); + return nlb64.pszEncoded; +} + +static int RecvWithTimeoutTime(struct NetlibConnection *nlc,DWORD dwTimeoutTime,char *buf,int len,int flags) +{ + DWORD dwTimeNow; + + dwTimeNow=GetTickCount(); + if(dwTimeNow>=dwTimeoutTime + || !WaitUntilReadable(nlc->s,dwTimeoutTime-dwTimeNow)) { + if(dwTimeNow>=dwTimeoutTime) SetLastError(ERROR_TIMEOUT); + return SOCKET_ERROR; + } + return NLRecv(nlc,buf,len,flags); +} + +static int HttpPeekFirstResponseLine(struct NetlibConnection *nlc,DWORD dwTimeoutTime,DWORD recvFlags,int *resultCode,char **ppszResultDescr,int *length) +{ + int bytesPeeked=0; + char buffer[1024]; + char *peol; + + for(;;) { + bytesPeeked=RecvWithTimeoutTime(nlc,dwTimeoutTime,buffer,SIZEOF(buffer)-1,MSG_PEEK|recvFlags); + if(bytesPeeked==0 || bytesPeeked==SOCKET_ERROR) { + if(bytesPeeked==0) SetLastError(ERROR_HANDLE_EOF); + return 0; + } + buffer[bytesPeeked]='\0'; + peol=strchr(buffer,'\n'); + if(peol==NULL) { + if(lstrlenA(buffer)1 + || *pHttpMinor++!='.' + || (httpMinorVer=strtol(pHttpMinor,&pResultCode,10))<0 + || pResultCode==pHttpMinor + || (tokenLen=strspn(pResultCode," \t"))==0 + || (*resultCode=strtol(pResultCode+=tokenLen,&pResultDescr,10))==0 + || pResultDescr==pResultCode + || (tokenLen=strspn(pResultDescr," \t"))==0 + || *(pResultDescr+=tokenLen)=='\0') { + SetLastError(peol==buffer?ERROR_BAD_FORMAT:ERROR_INVALID_DATA); + return 0; + } + if(ppszResultDescr) *ppszResultDescr=mir_strdup(pResultDescr); + if(length) *length=peol-buffer+2; + } + return 1; + } +} + +static int SendHttpRequestAndData(struct NetlibConnection *nlc,struct ResizableCharBuffer *httpRequest,NETLIBHTTPREQUEST *nlhr,int sendContentLengthHeader) +{ + int bytesSent; + + if((nlhr->requestType==REQUEST_POST)) { + if(sendContentLengthHeader) + AppendToCharBuffer(httpRequest,"Content-Length: %d\r\n\r\n",nlhr->dataLength); + else + AppendToCharBuffer(httpRequest,"\r\n"); + bytesSent=NLSend(nlc,httpRequest->sz,httpRequest->iEnd,MSG_DUMPASTEXT|(nlhr->flags&(NLHRF_NODUMP|NLHRF_NODUMPHEADERS)?MSG_NODUMP:(nlhr->flags&NLHRF_DUMPPROXY?MSG_DUMPPROXY:0))); + mir_free(httpRequest->sz); + if (nlhr->dataLength) { + int sendResult; + + if(bytesSent==SOCKET_ERROR + || SOCKET_ERROR==(sendResult=NLSend(nlc,nlhr->pData,nlhr->dataLength,(nlhr->flags&NLHRF_DUMPASTEXT?MSG_DUMPASTEXT:0)|(nlhr->flags&NLHRF_NODUMP?MSG_NODUMP:(nlhr->flags&NLHRF_DUMPPROXY?MSG_DUMPPROXY:0))))) { + return SOCKET_ERROR; + } + bytesSent+=sendResult; + } + } + else { + AppendToCharBuffer(httpRequest,"\r\n"); + bytesSent=NLSend(nlc,httpRequest->sz,httpRequest->iEnd,MSG_DUMPASTEXT|(nlhr->flags&(NLHRF_NODUMP|NLHRF_NODUMPHEADERS)?MSG_NODUMP:(nlhr->flags&NLHRF_DUMPPROXY?MSG_DUMPPROXY:0))); + mir_free(httpRequest->sz); + } + return bytesSent; +} + +int NetlibHttpSendRequest(WPARAM wParam,LPARAM lParam) +{ + struct NetlibConnection *nlc=(struct NetlibConnection*)wParam; + NETLIBHTTPREQUEST *nlhr=(NETLIBHTTPREQUEST*)lParam; + struct ResizableCharBuffer httpRequest={0}; + char *pszRequest,*szHost,*pszUrl; + char *pszProxyAuthorizationHeader; + int i,doneHostHeader,doneContentLengthHeader,doneProxyAuthHeader,usingNtlmAuthentication; + int useProxyHttpAuth,bytesSent; + + if(nlhr==NULL || nlhr->cbSize!=sizeof(NETLIBHTTPREQUEST) || nlhr->szUrl==NULL || nlhr->szUrl[0]=='\0') { + SetLastError(ERROR_INVALID_PARAMETER); + return SOCKET_ERROR; + } + switch(nlhr->requestType) { + case REQUEST_GET: pszRequest="GET"; break; + case REQUEST_POST: pszRequest="POST"; break; + case REQUEST_CONNECT: pszRequest="CONNECT"; break; + case REQUEST_HEAD:pszRequest="HEAD"; break; + default: + SetLastError(ERROR_INVALID_PARAMETER); + return SOCKET_ERROR; + } + + if(!NetlibEnterNestedCS(nlc,NLNCS_SEND)) return SOCKET_ERROR; + + //first line: "GET /index.html HTTP/1.0\r\n" + szHost=NULL; + if(nlhr->flags&(NLHRF_SMARTREMOVEHOST|NLHRF_REMOVEHOST|NLHRF_GENERATEHOST)){ + char *ppath,*phost; + phost=strstr(nlhr->szUrl,"://"); + if(phost==NULL) phost=nlhr->szUrl; + else phost+=3; + ppath=strchr(phost,'/'); + if(ppath==NULL) ppath=phost+lstrlenA(phost); + if(nlhr->flags&NLHRF_GENERATEHOST) { + szHost=(char*)mir_alloc(ppath-phost+1); + lstrcpynA(szHost,phost,ppath-phost+1); + } + if(nlhr->flags&NLHRF_REMOVEHOST + || (nlhr->flags&NLHRF_SMARTREMOVEHOST + && (!nlc->nlu->settings.useProxy + || !(nlc->nlu->settings.proxyType==PROXYTYPE_HTTP || nlc->nlu->settings.proxyType==PROXYTYPE_HTTPS)))) { + pszUrl=ppath; + } + else pszUrl=nlhr->szUrl; + } + else pszUrl=nlhr->szUrl; + AppendToCharBuffer(&httpRequest, "%s %s HTTP/1.%d\r\n", pszRequest, pszUrl, (nlhr->flags & NLHRF_HTTP11) != 0); + + //if (nlhr->dataLength > 0) + // AppendToCharBuffer(&httpRequest,"Content-Length: %d\r\n",nlhr->dataLength); + + //proxy auth initialization + useProxyHttpAuth=nlhr->flags&NLHRF_SMARTAUTHHEADER && nlc->nlu->settings.useProxy && nlc->nlu->settings.useProxyAuth && (nlc->nlu->settings.proxyType==PROXYTYPE_HTTP || nlc->nlu->settings.proxyType==PROXYTYPE_HTTPS); + usingNtlmAuthentication=0; + if(useProxyHttpAuth) { + if(nlc->nlu->settings.useProxyAuthNtlm) { + char *pszNtlmAuth; + pszNtlmAuth=NtlmInitialiseAndGetDomainPacket(nlc->hInstSecurityDll); + if(pszNtlmAuth==NULL) {useProxyHttpAuth=0; pszProxyAuthorizationHeader=NULL;} + else { + pszProxyAuthorizationHeader=(char*)mir_alloc(lstrlenA(pszNtlmAuth)+6); + lstrcpyA(pszProxyAuthorizationHeader,"NTLM "); + lstrcatA(pszProxyAuthorizationHeader,pszNtlmAuth); + mir_free(pszNtlmAuth); + usingNtlmAuthentication=1; + } + } + else { + NETLIBBASE64 nlb64; + char szAuth[512]; + mir_snprintf(szAuth,SIZEOF(szAuth),"%s:%s",nlc->nlu->settings.szProxyAuthUser,nlc->nlu->settings.szProxyAuthPassword); + nlb64.cbDecoded=lstrlenA(szAuth); + nlb64.pbDecoded=szAuth; + nlb64.cchEncoded=Netlib_GetBase64EncodedBufferSize(nlb64.cbDecoded); + nlb64.pszEncoded=(char*)mir_alloc(nlb64.cchEncoded); + NetlibBase64Encode(0,(LPARAM)&nlb64); + pszProxyAuthorizationHeader=(char*)mir_alloc(lstrlenA(nlb64.pszEncoded)+7); + lstrcpyA(pszProxyAuthorizationHeader,"Basic "); + lstrcatA(pszProxyAuthorizationHeader,nlb64.pszEncoded); + mir_free(nlb64.pszEncoded); + } + } + else pszProxyAuthorizationHeader=NULL; + + //HTTP headers + doneHostHeader=doneContentLengthHeader=doneProxyAuthHeader=0; + for(i=0;iheadersCount;i++) { + if(!lstrcmpiA(nlhr->headers[i].szName,"Host")) doneHostHeader=1; + else if(!lstrcmpiA(nlhr->headers[i].szName,"Content-Length")) doneContentLengthHeader=1; + else if(!lstrcmpiA(nlhr->headers[i].szName,"Proxy-Authorization")) doneProxyAuthHeader=1; + else if(!lstrcmpiA(nlhr->headers[i].szName,"Connection") && usingNtlmAuthentication) continue; + if(nlhr->headers[i].szValue==NULL) continue; + AppendToCharBuffer(&httpRequest,"%s: %s\r\n",nlhr->headers[i].szName,nlhr->headers[i].szValue); + } + if(szHost && !doneHostHeader) AppendToCharBuffer(&httpRequest,"%s: %s\r\n","Host",szHost); + if(pszProxyAuthorizationHeader) { + if(!doneProxyAuthHeader) AppendToCharBuffer(&httpRequest,"%s: %s\r\n","Proxy-Authorization",pszProxyAuthorizationHeader); + mir_free(pszProxyAuthorizationHeader); + if(usingNtlmAuthentication) AppendToCharBuffer(&httpRequest,"%s: %s\r\n","Connection","Keep-Alive"); + } + + // Add Sticky Headers + if (nlc->nlu->szStickyHeaders != NULL) { + AppendToCharBuffer(&httpRequest,"%s\r\n", nlc->nlu->szStickyHeaders); + } + + //send it + bytesSent=SendHttpRequestAndData(nlc,&httpRequest,nlhr,!doneContentLengthHeader); + if(bytesSent==SOCKET_ERROR) { + if(usingNtlmAuthentication) NtlmDestroy(); + if(szHost) mir_free(szHost); + NetlibLeaveNestedCS(&nlc->ncsSend); + return SOCKET_ERROR; + } + + //ntlm reply + if(usingNtlmAuthentication) { + int resultCode=0; + + if(!HttpPeekFirstResponseLine(nlc,GetTickCount()+5000,MSG_PEEK|MSG_DUMPASTEXT|(nlhr->flags&(NLHRF_NODUMP|NLHRF_NODUMPHEADERS)?MSG_NODUMP:(nlhr->flags&NLHRF_DUMPPROXY?MSG_DUMPPROXY:0)),&resultCode,NULL,NULL) + || ((resultCode<200 || resultCode>=300) && resultCode!=407)) { + NtlmDestroy(); + if(szHost) mir_free(szHost); + NetlibLeaveNestedCS(&nlc->ncsSend); + if(resultCode) NetlibHttpSetLastErrorUsingHttpResult(resultCode); + return SOCKET_ERROR; + } + if(resultCode==407) { //proxy auth required + NETLIBHTTPREQUEST *nlhrReply; + int i,error,contentLength=0; + + nlhrReply=(NETLIBHTTPREQUEST*)NetlibHttpRecvHeaders((WPARAM)nlc,nlhr->flags&(NLHRF_NODUMP|NLHRF_NODUMPHEADERS)?MSG_NODUMP:(nlhr->flags&NLHRF_DUMPPROXY?MSG_DUMPPROXY:0)); + if(nlhrReply==NULL) { + NtlmDestroy(); + if(szHost) mir_free(szHost); + NetlibLeaveNestedCS(&nlc->ncsSend); + if(resultCode) NetlibHttpSetLastErrorUsingHttpResult(resultCode); + return SOCKET_ERROR; + } + pszProxyAuthorizationHeader=NULL; + error=ERROR_SUCCESS; + for(i=0;iheadersCount;i++) { + if(!lstrcmpiA(nlhrReply->headers[i].szName,"Proxy-Authenticate")) { + if(!_strnicmp(nlhrReply->headers[i].szValue,"NTLM ",5)) + pszProxyAuthorizationHeader=NtlmCreateResponseFromChallenge(nlhrReply->headers[i].szValue+5); + else error=ERROR_ACCESS_DENIED; + } + else if(!lstrcmpiA(nlhrReply->headers[i].szName,"Content-Length")) + contentLength=atoi(nlhrReply->headers[i].szValue); + } + NetlibHttpFreeRequestStruct(0,(LPARAM)nlhrReply); + NtlmDestroy(); + if(pszProxyAuthorizationHeader==NULL) { + if(error!=ERROR_SUCCESS) SetLastError(error); + if(szHost) mir_free(szHost); + NetlibLeaveNestedCS(&nlc->ncsSend); + return SOCKET_ERROR; + } + + //receive content and throw away + { BYTE trashBuf[512]; + int recvResult; + + while(contentLength) { + recvResult=NLRecv(nlc,trashBuf,SIZEOF(trashBuf),nlhr->flags&NLHRF_NODUMP?MSG_NODUMP:MSG_DUMPASTEXT|MSG_DUMPPROXY); + if(recvResult==0 || recvResult==SOCKET_ERROR) { + if(recvResult==0) SetLastError(ERROR_HANDLE_EOF); + if(szHost) mir_free(szHost); + NetlibLeaveNestedCS(&nlc->ncsSend); + return SOCKET_ERROR; + } + contentLength-=recvResult; + } + } + + httpRequest.cbAlloced=httpRequest.iEnd=0; + httpRequest.sz=NULL; + AppendToCharBuffer(&httpRequest,"%s %s HTTP/1.0\r\n",pszRequest,pszUrl); + + //HTTP headers + doneHostHeader=doneContentLengthHeader=0; + for(i=0;iheadersCount;i++) { + if(!lstrcmpiA(nlhr->headers[i].szName,"Host")) doneHostHeader=1; + else if(!lstrcmpiA(nlhr->headers[i].szName,"Content-Length")) doneContentLengthHeader=1; + if(nlhr->headers[i].szValue==NULL) continue; + AppendToCharBuffer(&httpRequest,"%s: %s\r\n",nlhr->headers[i].szName,nlhr->headers[i].szValue); + } + if(szHost) { + if(!doneHostHeader) AppendToCharBuffer(&httpRequest,"%s: %s\r\n","Host",szHost); + mir_free(szHost); szHost=NULL; + } + AppendToCharBuffer(&httpRequest,"%s: NTLM %s\r\n","Proxy-Authorization",pszProxyAuthorizationHeader); + + //send it + bytesSent=SendHttpRequestAndData(nlc,&httpRequest,nlhr,!doneContentLengthHeader); + if(bytesSent==SOCKET_ERROR) { + NetlibLeaveNestedCS(&nlc->ncsSend); + return SOCKET_ERROR; + } + } + else NtlmDestroy(); + } + + //clean up + if(szHost) mir_free(szHost); + NetlibLeaveNestedCS(&nlc->ncsSend); + return bytesSent; +} + +int NetlibHttpFreeRequestStruct(WPARAM wParam,LPARAM lParam) +{ + NETLIBHTTPREQUEST *nlhr=(NETLIBHTTPREQUEST*)lParam; + + if(nlhr==NULL || nlhr->cbSize!=sizeof(NETLIBHTTPREQUEST) || nlhr->requestType!=REQUEST_RESPONSE) { + SetLastError(ERROR_INVALID_PARAMETER); + return 0; + } + if(nlhr->headers) { + int i; + for(i=0;iheadersCount;i++) { + if(nlhr->headers[i].szName) mir_free(nlhr->headers[i].szName); + if(nlhr->headers[i].szValue) mir_free(nlhr->headers[i].szValue); + } + mir_free(nlhr->headers); + } + if(nlhr->pData) mir_free(nlhr->pData); + if(nlhr->szResultDescr) mir_free(nlhr->szResultDescr); + if(nlhr->szUrl) mir_free(nlhr->szUrl); + mir_free(nlhr); + return 1; +} + +int NetlibHttpRecvHeaders(WPARAM wParam,LPARAM lParam) +{ + struct NetlibConnection *nlc=(struct NetlibConnection*)wParam; + NETLIBHTTPREQUEST *nlhr; + char buffer[4096]; + int bytesPeeked; + DWORD dwRequestTimeoutTime; + char *peol,*pbuffer; + int headersDone=0,firstLineLength; + + if(!NetlibEnterNestedCS(nlc,NLNCS_RECV)) + return (int)(NETLIBHTTPREQUEST*)NULL; + dwRequestTimeoutTime=GetTickCount()+HTTPRECVHEADERSTIMEOUT; + nlhr=(NETLIBHTTPREQUEST*)mir_calloc(sizeof(NETLIBHTTPREQUEST)); + nlhr->cbSize=sizeof(NETLIBHTTPREQUEST); + nlhr->nlc=nlc; + nlhr->requestType=REQUEST_RESPONSE; + if(!HttpPeekFirstResponseLine(nlc,dwRequestTimeoutTime,lParam|MSG_PEEK,&nlhr->resultCode,&nlhr->szResultDescr,&firstLineLength)) { + NetlibLeaveNestedCS(&nlc->ncsRecv); + NetlibHttpFreeRequestStruct(0,(LPARAM)nlhr); + return (int)(NETLIBHTTPREQUEST*)NULL; + } + bytesPeeked=NLRecv(nlc,buffer,firstLineLength,lParam|MSG_DUMPASTEXT); + if(bytesPeekedncsRecv); + NetlibHttpFreeRequestStruct(0,(LPARAM)nlhr); + if(bytesPeeked!=SOCKET_ERROR) SetLastError(ERROR_HANDLE_EOF); + return (int)(NETLIBHTTPREQUEST*)NULL; + } + for(;;) { + bytesPeeked=RecvWithTimeoutTime(nlc,dwRequestTimeoutTime,buffer,SIZEOF(buffer)-1,MSG_PEEK|lParam); + if(bytesPeeked==0 || bytesPeeked==SOCKET_ERROR) { + NetlibLeaveNestedCS(&nlc->ncsRecv); + NetlibHttpFreeRequestStruct(0,(LPARAM)nlhr); + if(bytesPeeked==0) SetLastError(ERROR_HANDLE_EOF); + return (int)(NETLIBHTTPREQUEST*)NULL; + } + buffer[bytesPeeked]='\0'; + for(pbuffer=buffer;;) { + peol=strchr(pbuffer,'\n'); + if(peol==NULL) { + if(lstrlenA(buffer)ncsRecv); + NetlibHttpFreeRequestStruct(0,(LPARAM)nlhr); + SetLastError(ERROR_BAD_FORMAT); + return (int)(NETLIBHTTPREQUEST*)NULL; + } + if((bytesPeeked == SIZEOF(buffer)-1 && pbuffer==buffer) //buffer overflow + || (pbuffer!=buffer && NLRecv(nlc,buffer,pbuffer-buffer,lParam|MSG_DUMPASTEXT)==SOCKET_ERROR)) { //error removing read bytes from buffer + NetlibLeaveNestedCS(&nlc->ncsRecv); + NetlibHttpFreeRequestStruct(0,(LPARAM)nlhr); + if(pbuffer==buffer) SetLastError(ERROR_BUFFER_OVERFLOW); + return (int)(NETLIBHTTPREQUEST*)NULL; + } + Sleep(100); + break; + } + if(peol==pbuffer || *--peol!='\r') { + NetlibLeaveNestedCS(&nlc->ncsRecv); + NetlibHttpFreeRequestStruct(0,(LPARAM)nlhr); + SetLastError(ERROR_BAD_FORMAT); + return (int)(NETLIBHTTPREQUEST*)NULL; + } + *peol='\0'; + { + char *pColon; + int len; + if(peol==pbuffer) { //blank line: end of headers + if(NLRecv(nlc,buffer,peol+2-buffer,lParam|MSG_DUMPASTEXT)==SOCKET_ERROR) { + NetlibLeaveNestedCS(&nlc->ncsRecv); + NetlibHttpFreeRequestStruct(0,(LPARAM)nlhr); + return (int)(NETLIBHTTPREQUEST*)NULL; + } + headersDone=1; + break; + } + pColon=strchr(pbuffer,':'); + if(pColon==NULL) { + NetlibLeaveNestedCS(&nlc->ncsRecv); + NetlibHttpFreeRequestStruct(0,(LPARAM)nlhr); + SetLastError(ERROR_INVALID_DATA); + return (int)(NETLIBHTTPREQUEST*)NULL; + } + nlhr->headersCount++; + nlhr->headers=(NETLIBHTTPHEADER*)mir_realloc(nlhr->headers,sizeof(NETLIBHTTPHEADER)*nlhr->headersCount); + nlhr->headers[nlhr->headersCount-1].szName=(char*)mir_alloc(pColon-pbuffer+1); + lstrcpynA(nlhr->headers[nlhr->headersCount-1].szName,pbuffer,pColon-pbuffer+1); + len=lstrlenA(nlhr->headers[nlhr->headersCount-1].szName); + while(len && (nlhr->headers[nlhr->headersCount-1].szName[len-1]==' ' || nlhr->headers[nlhr->headersCount-1].szName[len-1]=='\t')) + nlhr->headers[nlhr->headersCount-1].szName[--len]='\0'; + pColon++; + while(*pColon==' ' || *pColon=='\t') pColon++; + nlhr->headers[nlhr->headersCount-1].szValue=mir_strdup(pColon); + } + pbuffer=peol+2; + } + if(headersDone) break; + } + NetlibLeaveNestedCS(&nlc->ncsRecv); + return (int)nlhr; +} + +int NetlibHttpTransaction(WPARAM wParam,LPARAM lParam) +{ + struct NetlibUser *nlu=(struct NetlibUser*)wParam; + NETLIBHTTPREQUEST *nlhr=(NETLIBHTTPREQUEST*)lParam,*nlhrReply; + HANDLE hConnection; + + if(GetNetlibHandleType(nlu)!=NLH_USER || !(nlu->user.flags&NUF_OUTGOING) || nlhr==NULL || nlhr->cbSize!=sizeof(NETLIBHTTPREQUEST) || nlhr->szUrl==NULL || nlhr->szUrl[0]=='\0') { + SetLastError(ERROR_INVALID_PARAMETER); + return (int)(HANDLE)NULL; + } + + { + NETLIBOPENCONNECTION nloc={0}; + char szHost[128]; + char *ppath,*phost,*pcolon; + + phost=strstr(nlhr->szUrl,"://"); + if(phost==NULL) phost=nlhr->szUrl; + else phost+=3; + lstrcpynA(szHost,phost,SIZEOF(szHost)); + ppath=strchr(szHost,'/'); + if(ppath) *ppath='\0'; + nloc.cbSize=sizeof(nloc); + nloc.szHost=szHost; + pcolon=strrchr(szHost,':'); + if(pcolon) { + *pcolon='\0'; + nloc.wPort=(WORD)strtol(pcolon+1,NULL,10); + } + else nloc.wPort=80; + nloc.flags=NLOCF_HTTP; + hConnection=(HANDLE)NetlibOpenConnection((WPARAM)nlu,(LPARAM)&nloc); + if(hConnection==NULL) return (int)(HANDLE)NULL; + } + + { + NETLIBHTTPREQUEST nlhrSend; + int i,doneUserAgentHeader=0; + char szUserAgent[64]; + + nlhrSend=*nlhr; + nlhrSend.flags&=~NLHRF_REMOVEHOST; + nlhrSend.flags|=NLHRF_GENERATEHOST|NLHRF_SMARTREMOVEHOST|NLHRF_SMARTAUTHHEADER; + for(i=0;iheadersCount;i++) { + if(!lstrcmpiA(nlhr->headers[i].szName,"User-Agent")) + doneUserAgentHeader=1; + } + if(!doneUserAgentHeader) { + char *pspace,szMirandaVer[32]; + + nlhrSend.headersCount++; + nlhrSend.headers=(NETLIBHTTPHEADER*)mir_alloc(sizeof(NETLIBHTTPHEADER)*nlhrSend.headersCount); + CopyMemory(nlhrSend.headers,nlhr->headers,sizeof(NETLIBHTTPHEADER)*nlhr->headersCount); + nlhrSend.headers[nlhrSend.headersCount-1].szName="User-Agent"; + nlhrSend.headers[nlhrSend.headersCount-1].szValue=szUserAgent; + CallService(MS_SYSTEM_GETVERSIONTEXT,SIZEOF(szMirandaVer),(LPARAM)szMirandaVer); + pspace=strchr(szMirandaVer,' '); + if(pspace) { + *pspace++='\0'; + mir_snprintf(szUserAgent,SIZEOF(szUserAgent),"Miranda/%s (%s)",szMirandaVer,pspace); + } + else mir_snprintf(szUserAgent,SIZEOF(szUserAgent),"Miranda/%s",szMirandaVer); + } + if(NetlibHttpSendRequest((WPARAM)hConnection,(LPARAM)&nlhrSend)==SOCKET_ERROR) { + if(!doneUserAgentHeader) mir_free(nlhrSend.headers); + NetlibCloseHandle((WPARAM)hConnection,0); + return (int)(HANDLE)NULL; + } + if(!doneUserAgentHeader) mir_free(nlhrSend.headers); + } + + { + nlhrReply=(NETLIBHTTPREQUEST*)NetlibHttpRecvHeaders((WPARAM)hConnection,0); + if(nlhrReply==NULL) { + NetlibCloseHandle((WPARAM)hConnection,0); + return (int)(HANDLE)NULL; + } } + + if (nlhr->requestType != REQUEST_HEAD){ + int recvResult; + int dataBufferAlloced=0; + + for(;;) { + if(dataBufferAlloced-nlhrReply->dataLength<1024) { + dataBufferAlloced+=2048; + nlhrReply->pData=(PBYTE)mir_realloc(nlhrReply->pData,dataBufferAlloced); + if(nlhrReply->pData==NULL) { + SetLastError(ERROR_OUTOFMEMORY); + NetlibHttpFreeRequestStruct(0,(LPARAM)nlhrReply); + NetlibCloseHandle((WPARAM)hConnection,0); + return (int)(HANDLE)NULL; + } + } + recvResult=NLRecv((struct NetlibConnection*)hConnection,nlhrReply->pData+nlhrReply->dataLength,dataBufferAlloced-nlhrReply->dataLength-1,(nlhr->flags&NLHRF_DUMPASTEXT?MSG_DUMPASTEXT:0)|(nlhr->flags&NLHRF_NODUMP?MSG_NODUMP:(nlhr->flags&NLHRF_DUMPPROXY?MSG_DUMPPROXY:0))); + if(recvResult==0) break; + if(recvResult==SOCKET_ERROR) { + NetlibHttpFreeRequestStruct(0,(LPARAM)nlhrReply); + NetlibCloseHandle((WPARAM)hConnection,0); + return (int)(HANDLE)NULL; + } + nlhrReply->dataLength+=recvResult; + //TODO: Keep-alive replies are measured by content-length, not by when the connection closes + } + nlhrReply->pData[nlhrReply->dataLength]='\0'; + } + + NetlibCloseHandle((WPARAM)hConnection,0); + return (int)nlhrReply; +} + +void NetlibHttpSetLastErrorUsingHttpResult(int result) +{ + if(result>=200 && result<300) { + SetLastError(ERROR_SUCCESS); + return; + } + switch(result) { + case 400: SetLastError(ERROR_BAD_FORMAT); break; + case 401: + case 402: + case 403: + case 407: SetLastError(ERROR_ACCESS_DENIED); break; + case 404: SetLastError(ERROR_FILE_NOT_FOUND); break; + case 405: + case 406: SetLastError(ERROR_INVALID_FUNCTION); break; + case 408: SetLastError(ERROR_TIMEOUT); break; + default: SetLastError(ERROR_GEN_FAILURE); break; + } +} diff --git a/miranda-wine/src/modules/netlib/netlibhttpproxy.c b/miranda-wine/src/modules/netlib/netlibhttpproxy.c new file mode 100644 index 0000000..f3fc82e --- /dev/null +++ b/miranda-wine/src/modules/netlib/netlibhttpproxy.c @@ -0,0 +1,610 @@ +/* + +Miranda IM: the free IM client for Microsoft* Windows* + +Copyright 2000-2006 Miranda ICQ/IM project, +all portions of this codebase are copyrighted to the people +listed in contributors.txt. + +This program is free software; you can redistribute it and/or +modify it under the terms of the GNU General Public License +as published by the Free Software Foundation; either version 2 +of the License, or (at your option) any later version. + +This program is distributed in the hope that it will be useful, +but WITHOUT ANY WARRANTY; without even the implied warranty of +MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +GNU General Public License for more details. + +You should have received a copy of the GNU General Public License +along with this program; if not, write to the Free Software +Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. +*/ +#include "commonheaders.h" +#include "netlib.h" + +#define HTTPGETTIMEOUT 55000 //in ms. http GETs through most proxies will give up after a while so the request needs to be re-sent + +static int HttpGatewaySendGet(struct NetlibConnection *nlc) +{ + NETLIBHTTPREQUEST nlhrSend={0}; + NETLIBHTTPHEADER httpHeaders[3]; + char szUrl[512]; + struct NetlibConnection nlcSend; + + nlc->s=socket(AF_INET,SOCK_STREAM,0); + if(nlc->s==INVALID_SOCKET) { + Netlib_Logf(nlc->nlu,"%s %d: %s() failed (%u)",__FILE__,__LINE__,"socket",WSAGetLastError()); + return 0; + } + + if(connect(nlc->s,(SOCKADDR *)&nlc->sinProxy,sizeof(nlc->sinProxy))==SOCKET_ERROR) { + Netlib_Logf(nlc->nlu,"%s %d: %s() failed (%u)",__FILE__,__LINE__,"connect",WSAGetLastError()); + return 0; + } + + nlhrSend.cbSize=sizeof(nlhrSend); + nlhrSend.nlc=nlc; + + /* + * Gena01 - one small change here, just in case there is a timeout or a problem and we died while + * receiving + */ + nlhrSend.requestType=(nlc->nlhpi.szHttpGetUrl == NULL) ? REQUEST_POST : REQUEST_GET; + nlhrSend.flags=NLHRF_GENERATEHOST|NLHRF_DUMPPROXY|NLHRF_SMARTAUTHHEADER; + if (nlc->nlhpi.flags & NLHPIF_HTTP11) nlhrSend.flags |= NLHRF_HTTP11; + + /* + * Gena01 - fixing a possible crash, can't use GET Sequence if there is no GET URL + */ + if ((nlc->nlhpi.flags&NLHPIF_USEGETSEQUENCE) && (nlc->nlhpi.szHttpGetUrl != NULL)) { + EnterCriticalSection(&nlc->csHttpSequenceNums); + + mir_snprintf(szUrl,SIZEOF(szUrl),"%s%u",nlc->nlhpi.szHttpGetUrl,nlc->nlhpi.firstGetSequence); + nlc->nlhpi.firstGetSequence++; + if(nlc->nlhpi.flags&NLHPIF_GETPOSTSAMESEQUENCE) nlc->nlhpi.firstPostSequence++; + LeaveCriticalSection(&nlc->csHttpSequenceNums); + nlhrSend.szUrl=szUrl; + } + else nlhrSend.szUrl=(nlc->nlhpi.szHttpGetUrl == NULL) ? nlc->nlhpi.szHttpPostUrl : nlc->nlhpi.szHttpGetUrl; + nlhrSend.headers=httpHeaders; + nlhrSend.headersCount= 3; + httpHeaders[0].szName="User-Agent"; + httpHeaders[0].szValue=nlc->nlu->user.szHttpGatewayUserAgent; + httpHeaders[1].szName="Cache-Control"; + httpHeaders[1].szValue="no-store, no-cache"; + httpHeaders[2].szName="Pragma"; + httpHeaders[2].szValue="no-cache"; + + nlcSend=*nlc; + nlcSend.usingHttpGateway=0; + + if (nlc->nlhpi.szHttpGetUrl != NULL) { + + Netlib_Logf(nlc->nlu,"%s %d: Sending data.[ICQ GET] ",__FILE__,__LINE__); + if(NetlibHttpSendRequest((WPARAM)&nlcSend,(LPARAM)&nlhrSend)==SOCKET_ERROR) { + nlc->usingHttpGateway=1; + return 0; + } + nlc->dwLastGetSentTime=GetTickCount(); + return 1; + } + + /* + * Gena01 - small addition here, if we doing a POST then insert our packet here + */ + if (nlc->pHttpProxyPacketQueue != NULL) { + struct NetlibHTTPProxyPacketQueue *p = nlc->pHttpProxyPacketQueue; + + nlc->pHttpProxyPacketQueue = nlc->pHttpProxyPacketQueue->next; + + nlhrSend.dataLength=p->dataBufferLen; + nlhrSend.pData=(char*)p->dataBuffer; + + mir_free(p); + } + + if(NetlibHttpSendRequest((WPARAM)&nlcSend,(LPARAM)&nlhrSend)==SOCKET_ERROR) { + struct NetlibHTTPProxyPacketQueue *p = nlc->pHttpProxyPacketQueue; + + mir_free(nlhrSend.pData); + + nlc->usingHttpGateway=1; + + /* + * Gena01 - we need to drop ALL pending packets. Connection died! + */ + while (p != NULL) { + struct NetlibHTTPProxyPacketQueue *t = p; + + p = p->next; + + mir_free(t->dataBuffer); + mir_free(t); + } + + nlc->pHttpProxyPacketQueue = NULL; /* empty Queue */ + + return 0; + } + mir_free(nlhrSend.pData); + nlc->dwLastGetSentTime=GetTickCount(); + return 1; +} + +/* + * Gena01 - this is the old POST method, I renamed it and left it intact for ICQ support. it's called + * when we have both GET and POST URLs specified. + */ +int NetlibHttpGatewayOLDPost(struct NetlibConnection *nlc,const char *buf,int len,int flags) +{ + NETLIBHTTPREQUEST nlhrSend={0},*nlhrReply; + NETLIBHTTPHEADER httpHeaders[4]; + char szUrl[512]; + struct NetlibConnection nlcSend={0}; + + nlcSend.handleType=NLH_CONNECTION; + nlcSend.nlu=nlc->nlu; + nlcSend.hInstSecurityDll=nlc->hInstSecurityDll; + nlcSend.s=socket(AF_INET,SOCK_STREAM,0); + if(nlcSend.s==INVALID_SOCKET) { + Netlib_Logf(nlc->nlu,"%s %d: %s() failed (%u)",__FILE__,__LINE__,"socket",WSAGetLastError()); + return SOCKET_ERROR; + } + nlcSend.hOkToCloseEvent=CreateEvent(NULL,TRUE,TRUE,NULL); + nlcSend.dontCloseNow=0; + NetlibInitializeNestedCS(&nlcSend.ncsRecv); + NetlibInitializeNestedCS(&nlcSend.ncsSend); + + if(connect(nlcSend.s,(SOCKADDR *)&nlc->sinProxy,sizeof(nlc->sinProxy))==SOCKET_ERROR) { + Netlib_Logf(nlc->nlu,"%s %d: %s() failed (%u)",__FILE__,__LINE__,"connect",WSAGetLastError()); + NetlibDeleteNestedCS(&nlcSend.ncsRecv); + NetlibDeleteNestedCS(&nlcSend.ncsSend); + CloseHandle(nlcSend.hOkToCloseEvent); + closesocket(nlcSend.s); + return SOCKET_ERROR; + } + + nlhrSend.cbSize=sizeof(nlhrSend); + nlhrSend.nlc=nlc; + nlhrSend.requestType=REQUEST_POST; + nlhrSend.flags=NLHRF_GENERATEHOST|NLHRF_DUMPPROXY|NLHRF_SMARTAUTHHEADER; + if(flags&MSG_NODUMP) nlhrSend.flags|=NLHRF_NODUMP; + if(nlc->nlhpi.flags&NLHPIF_USEPOSTSEQUENCE) { + EnterCriticalSection(&nlc->csHttpSequenceNums); + mir_snprintf(szUrl,SIZEOF(szUrl),"%s%u",nlc->nlhpi.szHttpPostUrl,nlc->nlhpi.firstPostSequence); + nlc->nlhpi.firstPostSequence++; + if(nlc->nlhpi.flags&NLHPIF_GETPOSTSAMESEQUENCE) nlc->nlhpi.firstGetSequence++; + LeaveCriticalSection(&nlc->csHttpSequenceNums); + nlhrSend.szUrl=szUrl; + } + else nlhrSend.szUrl=nlc->nlhpi.szHttpPostUrl; + nlhrSend.headers=httpHeaders; + nlhrSend.headersCount=3; + httpHeaders[0].szName="User-Agent"; + httpHeaders[0].szValue=nlc->nlu->user.szHttpGatewayUserAgent; + httpHeaders[1].szName="Cache-Control"; + httpHeaders[1].szValue="no-store, no-cache"; + httpHeaders[2].szName="Connection"; + httpHeaders[2].szValue="close"; + httpHeaders[3].szName="Pragma"; + httpHeaders[3].szValue="no-cache"; + nlhrSend.dataLength=len; + nlhrSend.pData=(char*)buf; + if(NetlibHttpSendRequest((WPARAM)&nlcSend,(LPARAM)&nlhrSend)==SOCKET_ERROR) { + NetlibDeleteNestedCS(&nlcSend.ncsRecv); + NetlibDeleteNestedCS(&nlcSend.ncsSend); + CloseHandle(nlcSend.hOkToCloseEvent); + closesocket(nlcSend.s); + return SOCKET_ERROR; + } + + nlhrReply=(NETLIBHTTPREQUEST*)NetlibHttpRecvHeaders((WPARAM)&nlcSend,flags&MSG_NODUMP?MSG_NODUMP:MSG_DUMPPROXY); + if(nlhrReply==NULL + || nlhrReply->resultCode<200 || nlhrReply->resultCode>=300) { + NetlibDeleteNestedCS(&nlcSend.ncsRecv); + NetlibDeleteNestedCS(&nlcSend.ncsSend); + CloseHandle(nlcSend.hOkToCloseEvent); + closesocket(nlcSend.s); + if(nlhrReply) { + NetlibHttpFreeRequestStruct(0,(LPARAM)nlhrReply); + NetlibHttpSetLastErrorUsingHttpResult(nlhrReply->resultCode); + } + return SOCKET_ERROR; + } + NetlibHttpFreeRequestStruct(0,(LPARAM)nlhrReply); + + NetlibDeleteNestedCS(&nlcSend.ncsRecv); + NetlibDeleteNestedCS(&nlcSend.ncsSend); + CloseHandle(nlcSend.hOkToCloseEvent); + closesocket(nlcSend.s); + return len; +} + +int NetlibHttpGatewayPost(struct NetlibConnection *nlc,const char *buf,int len,int flags) +{ + struct NetlibHTTPProxyPacketQueue *p; + + if (nlc->nlhpi.szHttpGetUrl != NULL) + return NetlibHttpGatewayOLDPost(nlc, buf, len, flags); + + /* + * Gena01 - many changes here, do compare against the other version. + * + * Change #1: simplify to use similar code to GET + * Change #2: we need to allow to parse POST reply if szHttpGetUrl is NULL + * Change #3: Keep connection open if we need to. + * + * Impact: NONE! Since currently miranda doesn't allow szHttpGetUrl to be NULL, it will not connect + * with the new plugins that use this code. + */ + + p = mir_alloc(sizeof(struct NetlibHTTPProxyPacketQueue)); + p->dataBuffer = mir_alloc(len); + memcpy(p->dataBuffer, buf, len); + p->dataBufferLen = len; + p->next = NULL; + + /* + * Now check to see where to insert this in our queue + */ + if (nlc->pHttpProxyPacketQueue == NULL) { + nlc->pHttpProxyPacketQueue = p; + } else { + struct NetlibHTTPProxyPacketQueue *t = nlc->pHttpProxyPacketQueue; + + while (t->next != NULL) + t = t->next; + + t->next = p; + } + + + /* + * Gena01 - fake a Send!! tell 'em all is ok. We catch errors in Recv. + */ + return len; +} + +#define NETLIBHTTP_RETRYCOUNT 3 +#define NETLIBHTTP_RETRYTIMEOUT 5000 + +int NetlibHttpGatewayRecv(struct NetlibConnection *nlc,char *buf,int len,int flags) +{ + DWORD dwTimeNow; + int timedout; + NETLIBHTTPREQUEST *nlhrReply; + PBYTE dataBuffer; + int contentLength,i,bytesRecved; + int recvResult; + int retryCount; + + /* + * Gena01 - we need to send packet here, since we didn't do it before. + */ + if ((nlc->nlhpi.szHttpGetUrl == NULL) && (nlc->s == INVALID_SOCKET) && nlc->dataBuffer == NULL ) { + + if ( nlc->pollingTimeout == 0 ) + nlc->pollingTimeout = 30; + + /* We Need to sleep/wait for the data to send before we do receive */ + for ( retryCount = 0; retryCount < nlc->pollingTimeout; retryCount++ ) + { + if ( nlc->pHttpProxyPacketQueue != NULL ) + break; + + if ( SleepEx( 1000, TRUE )) + return SOCKET_ERROR; + } + +/* if ( retryCount == nlc->pollingTimeout ) + { SetLastError( ERROR_TIMEOUT ); + return SOCKET_ERROR; + } +*/ + if ( nlc->pHttpProxyPacketQueue == 0 && nlc->nlu->user.pfnHttpGatewayWrapSend != NULL ) + nlc->nlu->user.pfnHttpGatewayWrapSend((HANDLE)nlc,"",0,MSG_NOHTTPGATEWAYWRAP,NetlibSend); + + if(!HttpGatewaySendGet(nlc)) { + return SOCKET_ERROR; + } + } + /********************/ + if(nlc->dataBuffer) { + if(nlc->dataBufferLen<=len) { + contentLength=nlc->dataBufferLen; + CopyMemory(buf,nlc->dataBuffer,nlc->dataBufferLen); + if(!(flags&MSG_PEEK)) { + mir_free(nlc->dataBuffer); + nlc->dataBuffer=NULL; + nlc->dataBufferLen=0; + } + return contentLength; + } + CopyMemory(buf,nlc->dataBuffer,len); + if(!(flags&MSG_PEEK)) { + nlc->dataBufferLen-=len; + MoveMemory(nlc->dataBuffer,nlc->dataBuffer+len,nlc->dataBufferLen); + nlc->dataBuffer=(PBYTE)mir_realloc(nlc->dataBuffer,nlc->dataBufferLen); + } + return len; + } + for( retryCount = 0;;) { + timedout=0; + dwTimeNow=GetTickCount(); + if(dwTimeNow>=nlc->dwLastGetSentTime+HTTPGETTIMEOUT) timedout=1; + else if(!WaitUntilReadable(nlc->s,nlc->dwLastGetSentTime+HTTPGETTIMEOUT-dwTimeNow)) { + if(GetLastError()==ERROR_TIMEOUT) timedout=1; + else return SOCKET_ERROR; + } + if(timedout) { + closesocket(nlc->s); + nlc->s=INVALID_SOCKET; + if(!HttpGatewaySendGet(nlc)) return SOCKET_ERROR; + retryCount = 0; + continue; + } + nlhrReply=(NETLIBHTTPREQUEST*)NetlibHttpRecvHeaders((WPARAM)nlc,flags|MSG_RAW|MSG_DUMPPROXY); + if(nlhrReply==NULL) return SOCKET_ERROR; + // ignore 1xx result codes + if (nlhrReply->resultCode < 200) + { + NetlibHttpFreeRequestStruct(0,(LPARAM)nlhrReply); + continue; + } + // 0.3.1+ + // Attempt to retry NETLIBHTTP_RETRYCOUNT times if the result code is >300 + if (nlhrReply->resultCode >= 300) + { + if (retryCount < NETLIBHTTP_RETRYCOUNT) { + NetlibHttpFreeRequestStruct(0,(LPARAM)nlhrReply); + Netlib_Logf(nlc->nlu, "Error received from proxy, retrying"); + retryCount++; + closesocket(nlc->s); + nlc->s = INVALID_SOCKET; + Sleep(NETLIBHTTP_RETRYTIMEOUT); // wait 5 seconds + // retry the connection + Netlib_Logf(nlc->nlu,"%s %d: ResultCode?? Doing GET.",__FILE__,__LINE__); + if(HttpGatewaySendGet(nlc)) + continue; + SetLastError(ERROR_GEN_FAILURE); + return SOCKET_ERROR; + } + } + retryCount = 0; + contentLength=-1; + for(i=0;iheadersCount;i++) + { + if(!lstrcmpiA(nlhrReply->headers[i].szName,"Content-Length")) { + contentLength=atoi(nlhrReply->headers[i].szValue); + break; + } + } + + /* + if(contentLength<0) { + NetlibHttpFreeRequestStruct(0,(LPARAM)nlhrReply); + SetLastError(ERROR_INVALID_DATA); + return SOCKET_ERROR; + }*/ + if(contentLength==0 && nlc->nlu->user.szHttpGatewayHello != NULL) + { + NetlibHttpFreeRequestStruct(0,(LPARAM)nlhrReply); + continue; + } + + + if (contentLength < 0) { + /* create initial buffer */ + contentLength = 2048; + + dataBuffer=(PBYTE)mir_alloc(contentLength); + + /* error and exit */ + if(dataBuffer==NULL) { + NetlibHttpFreeRequestStruct(0,(LPARAM)nlhrReply); + SetLastError(ERROR_NOT_ENOUGH_MEMORY); + return SOCKET_ERROR; + } + + /* now we need to get the bytes and add them to our buffer */ + bytesRecved = 0; + + do { + recvResult=NLRecv(nlc,dataBuffer+bytesRecved,contentLength-bytesRecved,MSG_RAW|MSG_DUMPPROXY); + if(recvResult==SOCKET_ERROR) { + mir_free(dataBuffer); + NetlibHttpFreeRequestStruct(0,(LPARAM)nlhrReply); + if(recvResult==0) SetLastError(ERROR_HANDLE_EOF); + return SOCKET_ERROR; + } + bytesRecved+=recvResult; + } while (recvResult > 0); + contentLength = bytesRecved; + } else { + if(contentLength > 0) { + dataBuffer=(PBYTE)mir_alloc(contentLength); + if(dataBuffer==NULL) { + NetlibHttpFreeRequestStruct(0,(LPARAM)nlhrReply); + SetLastError(ERROR_NOT_ENOUGH_MEMORY); + return SOCKET_ERROR; + } + for(bytesRecved=0;bytesRecveds); + nlc->s=INVALID_SOCKET; + + /* + * Gena01 - ok, ICQ does it here so that when we enter this function again we have reply + * pending. This is quite clever, since GET always gets replies from ICQ server + * + */ + if (nlc->nlhpi.szHttpGetUrl != NULL) { + Netlib_Logf(nlc->nlu,"%s %d: Doing GET, Again????",__FILE__,__LINE__); + + if(!HttpGatewaySendGet(nlc)) { + mir_free(dataBuffer); + NetlibHttpFreeRequestStruct(0,(LPARAM)nlhrReply); + return SOCKET_ERROR; + } + } + + if(nlc->nlu->user.pfnHttpGatewayUnwrapRecv && !(flags&MSG_NOHTTPGATEWAYWRAP)) { + PBYTE newBuffer; + newBuffer=nlc->nlu->user.pfnHttpGatewayUnwrapRecv(nlhrReply,dataBuffer,contentLength,&contentLength,mir_realloc); + if(newBuffer==NULL) { + mir_free(dataBuffer); + NetlibHttpFreeRequestStruct(0,(LPARAM)nlhrReply); + return SOCKET_ERROR; + } + dataBuffer=newBuffer; + } + NetlibHttpFreeRequestStruct(0,(LPARAM)nlhrReply); + if(contentLength>0) break; + if((contentLength==0)&&(nlc->nlhpi.szHttpGetUrl==NULL)) + break; + mir_free(dataBuffer); + } + if(contentLength<=len) { + if(flags&MSG_PEEK) { + nlc->dataBuffer=dataBuffer; + nlc->dataBufferLen=contentLength; + } + CopyMemory(buf,dataBuffer,contentLength); + if(!(flags&MSG_PEEK)) mir_free(dataBuffer); + return contentLength; + } + CopyMemory(buf,dataBuffer,len); + if(!(flags&MSG_PEEK)) { + MoveMemory(dataBuffer,dataBuffer+len,contentLength-len); + dataBuffer=(PBYTE)mir_realloc(dataBuffer,contentLength-len); + nlc->dataBufferLen=contentLength-len; + } + else nlc->dataBufferLen=contentLength; + nlc->dataBuffer=dataBuffer; + + Netlib_Logf(nlc->nlu,"%s %d: NetlibHTTPGatewayRecv EXIT!",__FILE__,__LINE__); + return len; +} + +int NetlibInitHttpConnection(struct NetlibConnection *nlc,struct NetlibUser *nlu,NETLIBOPENCONNECTION *nloc) +{ + NETLIBHTTPREQUEST nlhrSend={0},*nlhrReply=NULL; + NETLIBHTTPHEADER httpHeaders[3]; + + nlc->nlhpi.firstGetSequence=nlc->nlhpi.firstPostSequence=1; + + /* + * Gena01 - ok we set nlhrReply to be null, also if the szHttpGatewayHello is NULL, then + * we don't send any requests/replies. We do have a socket open though. Could we + * re-use it maybe? + */ + if (nlu->user.szHttpGatewayHello != NULL) { + nlhrSend.cbSize=sizeof(nlhrSend); + nlhrSend.nlc=nlc; + nlhrSend.requestType=REQUEST_GET; + nlhrSend.flags=NLHRF_GENERATEHOST|NLHRF_DUMPPROXY|NLHRF_SMARTAUTHHEADER; + if (nlc->nlhpi.flags & NLHPIF_HTTP11) nlhrSend.flags |= NLHRF_HTTP11; + + nlhrSend.szUrl=nlu->user.szHttpGatewayHello; + nlhrSend.headers=httpHeaders; + nlhrSend.headersCount=3; + httpHeaders[0].szName="User-Agent"; + httpHeaders[0].szValue=nlu->user.szHttpGatewayUserAgent; + httpHeaders[1].szName="Cache-Control"; + httpHeaders[1].szValue="no-store, no-cache"; + httpHeaders[2].szName="Pragma"; + httpHeaders[2].szValue="no-cache"; + if(NetlibHttpSendRequest((WPARAM)nlc,(LPARAM)&nlhrSend)==SOCKET_ERROR) + return 0; + + nlhrReply=(NETLIBHTTPREQUEST*)NetlibHttpRecvHeaders((WPARAM)nlc,MSG_DUMPPROXY); + if(nlhrReply==NULL) return 0; + + if(nlhrReply->resultCode<200 || nlhrReply->resultCode>=300) { + NetlibHttpFreeRequestStruct(0,(LPARAM)nlhrReply); + NetlibHttpSetLastErrorUsingHttpResult(nlhrReply->resultCode); + return 0; + } + } + if(!nlu->user.pfnHttpGatewayInit(nlc,nloc,nlhrReply)) { + NetlibHttpFreeRequestStruct(0,(LPARAM)nlhrReply); + return 0; + } + NetlibHttpFreeRequestStruct(0,(LPARAM)nlhrReply); + + /* + * Gena01 - Ok, we should be able to use just POST. Needed for Yahoo, NO GET requests + */ + if(nlc->nlhpi.szHttpPostUrl==NULL) { + SetLastError(ERROR_BAD_FORMAT); + return 0; + } + closesocket(nlc->s); + nlc->s=INVALID_SOCKET; + + nlc->usingHttpGateway=1; + + /* don't send anything if only using POST? */ + if(nlc->nlhpi.szHttpGetUrl!= NULL) + if(!HttpGatewaySendGet(nlc)) + return 0; + + //now properly connected + if(nlu->user.pfnHttpGatewayBegin) + if(!nlu->user.pfnHttpGatewayBegin(nlc,nloc)) + return 0; + return 1; +} + +int NetlibHttpGatewaySetInfo(WPARAM wParam,LPARAM lParam) +{ + NETLIBHTTPPROXYINFO *nlhpi=(NETLIBHTTPPROXYINFO*)lParam; + struct NetlibConnection *nlc=(struct NetlibConnection*)wParam; + + if(GetNetlibHandleType(nlc)!=NLH_CONNECTION || nlhpi==NULL || nlhpi->cbSize!=sizeof(NETLIBHTTPPROXYINFO) || nlhpi->szHttpPostUrl==NULL) { + SetLastError(ERROR_INVALID_PARAMETER); + return 0; + } + if(nlc->nlhpi.szHttpGetUrl) mir_free(nlc->nlhpi.szHttpGetUrl); + if(nlc->nlhpi.szHttpPostUrl) mir_free(nlc->nlhpi.szHttpPostUrl); + nlc->nlhpi=*nlhpi; + + if (nlc->nlhpi.szHttpGetUrl) + nlc->nlhpi.szHttpGetUrl=mir_strdup(nlc->nlhpi.szHttpGetUrl); + + nlc->nlhpi.szHttpPostUrl=mir_strdup(nlc->nlhpi.szHttpPostUrl); + return 1; +} + +int NetlibHttpSetSticky(WPARAM wParam, LPARAM lParam) +{ + struct NetlibUser * nu = (struct NetlibUser*)wParam; + if (GetNetlibHandleType(nu)!=NLH_USER) return ERROR_INVALID_PARAMETER; + if (nu->szStickyHeaders) { mir_free(nu->szStickyHeaders); nu->szStickyHeaders=NULL; } + if (lParam) { + nu->szStickyHeaders=mir_strdup((char*)lParam); // pointer is ours + } + return 0; +} + +int NetlibHttpSetPollingTimeout(WPARAM wParam, LPARAM lParam) +{ + int oldTimeout; + struct NetlibConnection *nlc=(struct NetlibConnection*)wParam; + if (GetNetlibHandleType(nlc)!=NLH_CONNECTION) return -1; + oldTimeout = nlc->pollingTimeout; + nlc->pollingTimeout = lParam; + return oldTimeout; +} diff --git a/miranda-wine/src/modules/netlib/netliblog.c b/miranda-wine/src/modules/netlib/netliblog.c new file mode 100644 index 0000000..9d07f29 --- /dev/null +++ b/miranda-wine/src/modules/netlib/netliblog.c @@ -0,0 +1,440 @@ +/* + +Miranda IM: the free IM client for Microsoft* Windows* + +Copyright 2000-2006 Miranda ICQ/IM project, +all portions of this codebase are copyrighted to the people +listed in contributors.txt. + +This program is free software; you can redistribute it and/or +modify it under the terms of the GNU General Public License +as published by the Free Software Foundation; either version 2 +of the License, or (at your option) any later version. + +This program is distributed in the hope that it will be useful, +but WITHOUT ANY WARRANTY; without even the implied warranty of +MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +GNU General Public License for more details. + +You should have received a copy of the GNU General Public License +along with this program; if not, write to the Free Software +Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. +*/ + +#include "commonheaders.h" +#include "netlib.h" + +#define MS_NETLIB_LOGWIN "Netlib/Log/Win" + +extern HANDLE hConnectionHeaderMutex; + +#define TIMEFORMAT_NONE 0 +#define TIMEFORMAT_HHMMSS 1 +#define TIMEFORMAT_MILLISECONDS 2 +#define TIMEFORMAT_MICROSECONDS 3 +struct { + HWND hwndOpts; + int toOutputDebugString; + int toFile; + TCHAR* szFile; + int timeFormat; + int showUser; + int dumpSent,dumpRecv,dumpProxy; + int textDumps,autoDetectText; + CRITICAL_SECTION cs; +} logOptions; +static __int64 mirandaStartTime,perfCounterFreq; + +static const TCHAR* szTimeFormats[] = +{ + _T( "No times" ), + _T( "Standard hh:mm:ss times" ), + _T( "Times in milliseconds" ), + _T( "Times in microseconds" ) +}; + +static BOOL CALLBACK LogOptionsDlgProc(HWND hwndDlg,UINT message,WPARAM wParam,LPARAM lParam) +{ + switch(message) { + case WM_INITDIALOG: + logOptions.hwndOpts=hwndDlg; + TranslateDialogDefault(hwndDlg); + CheckDlgButton(hwndDlg,IDC_DUMPRECV,logOptions.dumpRecv?BST_CHECKED:BST_UNCHECKED); + CheckDlgButton(hwndDlg,IDC_DUMPSENT,logOptions.dumpSent?BST_CHECKED:BST_UNCHECKED); + CheckDlgButton(hwndDlg,IDC_DUMPPROXY,logOptions.dumpProxy?BST_CHECKED:BST_UNCHECKED); + CheckDlgButton(hwndDlg,IDC_TEXTDUMPS,logOptions.textDumps?BST_CHECKED:BST_UNCHECKED); + CheckDlgButton(hwndDlg,IDC_AUTODETECTTEXT,logOptions.autoDetectText?BST_CHECKED:BST_UNCHECKED); + { int i; + for( i=0; i < SIZEOF(szTimeFormats); i++ ) + SendDlgItemMessage(hwndDlg,IDC_TIMEFORMAT,CB_ADDSTRING,0,(LPARAM)TranslateTS( szTimeFormats[i] )); + } + SendDlgItemMessage(hwndDlg,IDC_TIMEFORMAT,CB_SETCURSEL,logOptions.timeFormat,0); + CheckDlgButton(hwndDlg,IDC_SHOWNAMES,logOptions.showUser?BST_CHECKED:BST_UNCHECKED); + CheckDlgButton(hwndDlg,IDC_TOOUTPUTDEBUGSTRING,logOptions.toOutputDebugString?BST_CHECKED:BST_UNCHECKED); + CheckDlgButton(hwndDlg,IDC_TOFILE,logOptions.toFile?BST_CHECKED:BST_UNCHECKED); + SetDlgItemText(hwndDlg,IDC_FILENAME,logOptions.szFile); + CheckDlgButton(hwndDlg,IDC_SHOWTHISDLGATSTART,DBGetContactSettingByte(NULL,"Netlib","ShowLogOptsAtStart",0)?BST_CHECKED:BST_UNCHECKED); + { DBVARIANT dbv; + if(!DBGetContactSetting(NULL,"Netlib","RunAtStart",&dbv)) { + SetDlgItemTextA(hwndDlg,IDC_RUNATSTART,dbv.pszVal); + DBFreeVariant(&dbv); + } + } + return TRUE; + case WM_COMMAND: + switch(LOWORD(wParam)) { + case IDC_DUMPRECV: + logOptions.dumpRecv=IsDlgButtonChecked(hwndDlg,LOWORD(wParam)); + break; + case IDC_DUMPSENT: + logOptions.dumpSent=IsDlgButtonChecked(hwndDlg,LOWORD(wParam)); + break; + case IDC_DUMPPROXY: + logOptions.dumpProxy=IsDlgButtonChecked(hwndDlg,LOWORD(wParam)); + break; + case IDC_TEXTDUMPS: + logOptions.textDumps=IsDlgButtonChecked(hwndDlg,LOWORD(wParam)); + break; + case IDC_AUTODETECTTEXT: + logOptions.autoDetectText=IsDlgButtonChecked(hwndDlg,LOWORD(wParam)); + break; + case IDC_TIMEFORMAT: + logOptions.timeFormat=SendDlgItemMessage(hwndDlg,IDC_TIMEFORMAT,CB_GETCURSEL,0,0); + break; + case IDC_SHOWNAMES: + logOptions.showUser=IsDlgButtonChecked(hwndDlg,LOWORD(wParam)); + break; + case IDC_TOOUTPUTDEBUGSTRING: + logOptions.toOutputDebugString=IsDlgButtonChecked(hwndDlg,LOWORD(wParam)); + break; + case IDC_TOFILE: + logOptions.toFile=IsDlgButtonChecked(hwndDlg,LOWORD(wParam)); + break; + case IDC_FILENAME: + if(HIWORD(wParam)!=EN_CHANGE) break; + if((HWND)lParam==GetFocus()) { + CheckDlgButton(hwndDlg,IDC_TOFILE,BST_CHECKED); + logOptions.toFile=0; + } + EnterCriticalSection(&logOptions.cs); + if(logOptions.szFile) mir_free(logOptions.szFile); + { int len; + len=GetWindowTextLength((HWND)lParam); + logOptions.szFile = ( TCHAR* )mir_alloc( sizeof( TCHAR )*( len+1 )); + GetWindowText((HWND)lParam, logOptions.szFile, len+1 ); + } + LeaveCriticalSection(&logOptions.cs); + break; + case IDC_FILENAMEBROWSE: + case IDC_RUNATSTARTBROWSE: + { TCHAR str[MAX_PATH+2]; + OPENFILENAME ofn={0}; + TCHAR filter[512],*pfilter; + + GetWindowText(GetWindow((HWND)lParam,GW_HWNDPREV),str,SIZEOF(str)); + ofn.lStructSize=OPENFILENAME_SIZE_VERSION_400; + ofn.hwndOwner=hwndDlg; + ofn.Flags=OFN_HIDEREADONLY; + if (LOWORD(wParam)==IDC_FILENAMEBROWSE) { + ofn.lpstrTitle=TranslateT("Select where log file will be created"); + } else { + ofn.Flags|=OFN_PATHMUSTEXIST|OFN_FILEMUSTEXIST; + ofn.lpstrTitle=TranslateT("Select program to be run"); + } + _tcscpy(filter,TranslateT("All Files")); + _tcscat(filter,_T(" (*)")); + pfilter=filter+lstrlen(filter)+1; + _tcscpy(pfilter,_T("*")); + pfilter=pfilter+lstrlen(pfilter)+1; + *pfilter='\0'; + ofn.lpstrFilter=filter; + ofn.lpstrFile=str; + ofn.nMaxFile=SIZEOF(str)-2; + ofn.nMaxFileTitle=MAX_PATH; + if (LOWORD(wParam)==IDC_FILENAMEBROWSE) { + if(!GetSaveFileName(&ofn)) return 1; + } else { + if(!GetOpenFileName(&ofn)) return 1; + } + if(LOWORD(wParam)==IDC_RUNATSTARTBROWSE && _tcschr(str,' ')!=NULL) { + MoveMemory(str+1,str,SIZEOF(str)-2); + str[0]='"'; + lstrcat(str,_T("\"")); + } + SetWindowText(GetWindow((HWND)lParam,GW_HWNDPREV),str); + break; + } + case IDC_SHOWTHISDLGATSTART: + DBWriteContactSettingByte(NULL,"Netlib","ShowLogOptsAtStart",(BYTE)IsDlgButtonChecked(hwndDlg,LOWORD(wParam))); + break; + case IDC_RUNATSTART: + if(HIWORD(wParam)!=EN_CHANGE) break; + { int len; + char *str; + len=GetWindowTextLength((HWND)lParam); + str=(char*)mir_alloc(len+1); + GetWindowTextA((HWND)lParam,str,len+1); + DBWriteContactSettingString(NULL,"Netlib","RunAtStart",str); + mir_free(str); + } + break; + case IDC_RUNNOW: + { int len; + char *str; + STARTUPINFOA si={0}; + PROCESS_INFORMATION pi; + len=GetWindowTextLength(GetDlgItem(hwndDlg,IDC_RUNATSTART)); + str=(char*)mir_alloc(len+1); + GetDlgItemTextA(hwndDlg,IDC_RUNATSTART,str,len+1); + si.cb=sizeof(si); + if(str[0]) CreateProcessA(NULL,str,NULL,NULL,FALSE,0,NULL,NULL,&si,&pi); + } + break; + case IDC_SAVE: + DBWriteContactSettingByte(NULL,"Netlib","DumpRecv",(BYTE)logOptions.dumpRecv); + DBWriteContactSettingByte(NULL,"Netlib","DumpSent",(BYTE)logOptions.dumpSent); + DBWriteContactSettingByte(NULL,"Netlib","DumpProxy",(BYTE)logOptions.dumpProxy); + DBWriteContactSettingByte(NULL,"Netlib","TextDumps",(BYTE)logOptions.textDumps); + DBWriteContactSettingByte(NULL,"Netlib","AutoDetectText",(BYTE)logOptions.autoDetectText); + DBWriteContactSettingByte(NULL,"Netlib","TimeFormat",(BYTE)logOptions.timeFormat); + DBWriteContactSettingByte(NULL,"Netlib","ShowUser",(BYTE)logOptions.showUser); + DBWriteContactSettingByte(NULL,"Netlib","ToOutputDebugString",(BYTE)logOptions.toOutputDebugString); + DBWriteContactSettingByte(NULL,"Netlib","ToFile",(BYTE)logOptions.toFile); + DBWriteContactSettingTString(NULL,"Netlib","File", logOptions.szFile ? logOptions.szFile: _T("")); + break; + case IDOK: + case IDCANCEL: + DestroyWindow(hwndDlg); + break; + } + break; + case WM_CLOSE: + DestroyWindow(hwndDlg); + break; + case WM_DESTROY: + logOptions.hwndOpts=NULL; + break; + } + return FALSE; +} + +void NetlibLogShowOptions(void) +{ + if(logOptions.hwndOpts==NULL) + logOptions.hwndOpts=CreateDialog(GetModuleHandle(NULL),MAKEINTRESOURCE(IDD_NETLIBLOGOPTS),NULL,LogOptionsDlgProc); + SetForegroundWindow(logOptions.hwndOpts); +} + +static void CreateDirectoryTree( TCHAR* szDir) +{ + DWORD dwAttributes; + TCHAR* pszLastBackslash,szTestDir[MAX_PATH]; + + lstrcpyn(szTestDir, szDir, SIZEOF(szTestDir)); + if ((dwAttributes = GetFileAttributes(szTestDir))!=0xffffffff && dwAttributes&FILE_ATTRIBUTE_DIRECTORY) return; + pszLastBackslash = _tcsrchr( szTestDir, '\\' ); + if ( pszLastBackslash == NULL ) return; + *pszLastBackslash = '\0'; + CreateDirectoryTree( szTestDir ); + CreateDirectory( szTestDir, NULL ); +} + +static int NetlibLog(WPARAM wParam,LPARAM lParam) +{ + struct NetlibUser *nlu=(struct NetlibUser*)wParam; + struct NetlibUser nludummy; + const char *pszMsg=(const char*)lParam; + char *szLine; + char szTime[32]; + LARGE_INTEGER liTimeNow; + DWORD dwOriginalLastError; + + if( (nlu != NULL && GetNetlibHandleType(nlu)!=NLH_USER) || pszMsg==NULL) { + SetLastError(ERROR_INVALID_PARAMETER); + return 0; + } + if (nlu==NULL) { /* if the Netlib user handle is NULL, just pretend its not */ + nlu=&nludummy; + nlu->user.szSettingsModule="(NULL)"; + } + dwOriginalLastError=GetLastError(); + QueryPerformanceCounter(&liTimeNow); + liTimeNow.QuadPart-=mirandaStartTime; + switch(logOptions.timeFormat) { + case TIMEFORMAT_HHMMSS: + GetTimeFormatA(LOCALE_USER_DEFAULT,TIME_FORCE24HOURFORMAT|TIME_NOTIMEMARKER,NULL,NULL,szTime,SIZEOF(szTime)-1); + break; + case TIMEFORMAT_MILLISECONDS: + mir_snprintf(szTime,SIZEOF(szTime)-1,"%I64u.%03I64u",liTimeNow.QuadPart/perfCounterFreq,1000*(liTimeNow.QuadPart%perfCounterFreq)/perfCounterFreq); + break; + case TIMEFORMAT_MICROSECONDS: + mir_snprintf(szTime,SIZEOF(szTime)-1,"%I64u.%06I64u",liTimeNow.QuadPart/perfCounterFreq,1000000*(liTimeNow.QuadPart%perfCounterFreq)/perfCounterFreq); + break; + default: + szTime[0]='\0'; + break; + } + EnterCriticalSection(&logOptions.cs); + if(logOptions.showUser) lstrcatA(szTime," "); + szLine=(char*)alloca(lstrlenA(pszMsg)+lstrlenA(nlu->user.szSettingsModule)+5+lstrlenA(szTime)); + if(logOptions.timeFormat || logOptions.showUser) + sprintf(szLine,"[%s%s] %s\n",szTime,logOptions.showUser?nlu->user.szSettingsModule:"",pszMsg); + else + sprintf(szLine,"%s\n",pszMsg); + if(logOptions.toOutputDebugString) OutputDebugStringA(szLine); + if(logOptions.toFile && logOptions.szFile[0]) { + FILE *fp; + fp = _tfopen(logOptions.szFile, _T("at")); + if(!fp) { + CreateDirectoryTree(logOptions.szFile); + fp = _tfopen(logOptions.szFile, _T("at")); + } + if(fp) { + fputs(szLine,fp); + fclose(fp); + } + } + LeaveCriticalSection(&logOptions.cs); + SetLastError(dwOriginalLastError); + return 1; +} + +void NetlibDumpData(struct NetlibConnection *nlc,PBYTE buf,int len,int sent,int flags) +{ + int isText=1; + char szTitleLine[128]; + char *szBuf; + int titleLineLen; + struct NetlibUser *nlu; + + // This section checks a number of conditions and aborts + // the dump if the data should not be written to the log + + // Check packet flags + if ((flags&MSG_PEEK) || (flags&MSG_NODUMP)) + return; + + // Check user's log settings + if (!(logOptions.toOutputDebugString || + (logOptions.toFile && logOptions.szFile[0]))) + return; + if ((sent && !logOptions.dumpSent) || + (!sent && !logOptions.dumpRecv)) + return; + if ((flags&MSG_DUMPPROXY) && !logOptions.dumpProxy) + return; + + + if (!logOptions.textDumps) + isText = 0; + else if (!(flags&MSG_DUMPASTEXT)) { + if (logOptions.autoDetectText) { + int i; + for(i = 0; i=0x80) + { + isText = 0; + break; + } + } + else + isText = 0; + } + + WaitForSingleObject(hConnectionHeaderMutex, INFINITE); + nlu = nlc ? nlc->nlu : NULL; + titleLineLen = mir_snprintf(szTitleLine,SIZEOF(szTitleLine), "(%p:%u) Data %s%s\n", nlc, nlc?nlc->s:0, sent?"sent":"received", flags & MSG_DUMPPROXY?" (proxy)":""); + ReleaseMutex(hConnectionHeaderMutex); + + // Text data + if (isText) + { + szBuf = (char*)alloca(titleLineLen + len + 1); + CopyMemory(szBuf, szTitleLine, titleLineLen); + CopyMemory(szBuf + titleLineLen, (const char*)buf, len); + szBuf[titleLineLen + len] = '\0'; + } + // Binary data + else + { + int line, col, colsInLine; + char *pszBuf; + + szBuf = (char*)alloca(titleLineLen + ((len+16)>>4) * 76 + 1); + CopyMemory(szBuf, szTitleLine, titleLineLen); + pszBuf = szBuf + titleLineLen; + for (line = 0; ; line += 16) + { + colsInLine = min(16, len - line); + pszBuf += wsprintfA(pszBuf, "%08X: ", line); + // Dump data as hex + for (col = 0; col < colsInLine; col++) + pszBuf += wsprintfA(pszBuf, "%02X%c", buf[line + col], ((col&3)==3 && col != 15)?'-':' '); + // Fill out last line with blanks + for ( ; col<16; col++) + { + lstrcpyA(pszBuf, " "); + pszBuf += 3; + } + *pszBuf++ = ' '; + for (col = 0; col < colsInLine; col++) + *pszBuf++ = buf[line+col]<' '?'.':(char)buf[line+col]; + if (len-line<=16) + break; + *pszBuf++ = '\n'; // End each line with a break + } + *pszBuf = '\0'; + } + + NetlibLog((WPARAM)nlu,(LPARAM)szBuf); + +} + +void NetlibLogInit(void) +{ + DBVARIANT dbv; + LARGE_INTEGER li; + + CreateServiceFunction(MS_NETLIB_LOG,NetlibLog); + QueryPerformanceFrequency(&li); + perfCounterFreq=li.QuadPart; + QueryPerformanceCounter(&li); + mirandaStartTime=li.QuadPart; + InitializeCriticalSection(&logOptions.cs); + logOptions.dumpRecv=DBGetContactSettingByte(NULL,"Netlib","DumpRecv",1); + logOptions.dumpSent=DBGetContactSettingByte(NULL,"Netlib","DumpSent",1); + logOptions.dumpProxy=DBGetContactSettingByte(NULL,"Netlib","DumpProxy",0); + logOptions.textDumps=DBGetContactSettingByte(NULL,"Netlib","TextDumps",1); + logOptions.autoDetectText=DBGetContactSettingByte(NULL,"Netlib","AutoDetectText",1); + logOptions.timeFormat=DBGetContactSettingByte(NULL,"Netlib","TimeFormat",TIMEFORMAT_HHMMSS); + logOptions.showUser=DBGetContactSettingByte(NULL,"Netlib","ShowUser",1); + logOptions.toOutputDebugString=DBGetContactSettingByte(NULL,"Netlib","ToOutputDebugString",0); + logOptions.toFile=DBGetContactSettingByte(NULL,"Netlib","ToFile",0); + + if(!DBGetContactSettingTString(NULL, "Netlib", "File", &dbv)) { + logOptions.szFile = mir_tstrdup(dbv.ptszVal); + DBFreeVariant(&dbv); + } + if(logOptions.toFile && logOptions.szFile[0]) { + FILE *fp; + fp = _tfopen(logOptions.szFile, _T("wt")); + if(fp) fclose(fp); + } + if(DBGetContactSettingByte(NULL,"Netlib","ShowLogOptsAtStart",0)) + NetlibLogShowOptions(); + if(!DBGetContactSetting(NULL,"Netlib","RunAtStart",&dbv)) { + STARTUPINFOA si={0}; + PROCESS_INFORMATION pi; + si.cb=sizeof(si); + if(dbv.pszVal[0]) CreateProcessA(NULL,dbv.pszVal,NULL,NULL,FALSE,0,NULL,NULL,&si,&pi); + DBFreeVariant(&dbv); + } +} + +void NetlibLogShutdown(void) +{ + if(IsWindow(logOptions.hwndOpts)) DestroyWindow(logOptions.hwndOpts); + DeleteCriticalSection(&logOptions.cs); + if(logOptions.szFile) mir_free(logOptions.szFile); +} + diff --git a/miranda-wine/src/modules/netlib/netlibopenconn.c b/miranda-wine/src/modules/netlib/netlibopenconn.c new file mode 100644 index 0000000..6d19e1c --- /dev/null +++ b/miranda-wine/src/modules/netlib/netlibopenconn.c @@ -0,0 +1,569 @@ +/* + +Miranda IM: the free IM client for Microsoft* Windows* + +Copyright 2000-2006 Miranda ICQ/IM project, +all portions of this codebase are copyrighted to the people +listed in contributors.txt. + +This program is free software; you can redistribute it and/or +modify it under the terms of the GNU General Public License +as published by the Free Software Foundation; either version 2 +of the License, or (at your option) any later version. + +This program is distributed in the hope that it will be useful, +but WITHOUT ANY WARRANTY; without even the implied warranty of +MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +GNU General Public License for more details. + +You should have received a copy of the GNU General Public License +along with this program; if not, write to the Free Software +Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. +*/ +#include "commonheaders.h" +#include "netlib.h" + +extern CRITICAL_SECTION csNetlibUser; +extern DWORD g_LastConnectionTick; // protected by csNetlibUser + +//returns in network byte order +DWORD DnsLookup(struct NetlibUser *nlu,const char *szHost) +{ + DWORD ip; + HOSTENT *host; + + ip=inet_addr(szHost); + if(ip!=INADDR_NONE) return ip; + host=gethostbyname(szHost); + if(host) return *(u_long *)host->h_addr_list[0]; + Netlib_Logf(nlu,"%s %d: %s() failed (%u)",__FILE__,__LINE__,"gethostbyname",WSAGetLastError()); + return 0; +} + +int WaitUntilReadable(SOCKET s,DWORD dwTimeout) +{ + fd_set readfd; + TIMEVAL tv; + + tv.tv_sec=dwTimeout/1000; + tv.tv_usec=(dwTimeout%1000)*1000; + FD_ZERO(&readfd); + FD_SET(s,&readfd); + switch(select(0,&readfd,0,0,&tv)) { + case 0: + SetLastError(ERROR_TIMEOUT); + case SOCKET_ERROR: + return 0; + } + return 1; +} + +static int WaitUntilWritable(SOCKET s,DWORD dwTimeout) +{ + fd_set writefd; + TIMEVAL tv; + + tv.tv_sec=dwTimeout/1000; + tv.tv_usec=(dwTimeout%1000)*1000; + FD_ZERO(&writefd); + FD_SET(s,&writefd); + switch(select(0,0,&writefd,0,&tv)) { + case 0: + SetLastError(ERROR_TIMEOUT); + case SOCKET_ERROR: + return 0; + } + return 1; +} + +static int NetlibInitSocks4Connection(struct NetlibConnection *nlc,struct NetlibUser *nlu,NETLIBOPENCONNECTION *nloc) +{ //http://www.socks.nec.com/protocol/socks4.protocol and http://www.socks.nec.com/protocol/socks4a.protocol + PBYTE pInit; + int nUserLen,nHostLen,len; + BYTE reply[8]; + + nUserLen=lstrlenA(nlu->settings.szProxyAuthUser); + nHostLen=lstrlenA(nloc->szHost); + pInit=(PBYTE)mir_alloc(10+nUserLen+nHostLen); + pInit[0]=4; //SOCKS4 + pInit[1]=1; //connect + *(PWORD)(pInit+2)=htons(nloc->wPort); + if(nlu->settings.szProxyAuthUser==NULL) pInit[8]=0; + else lstrcpyA(pInit+8,nlu->settings.szProxyAuthUser); + if(nlu->settings.dnsThroughProxy) { + if((*(PDWORD)(pInit+4)=inet_addr(nloc->szHost))==INADDR_NONE) { + *(PDWORD)(pInit+4)=0x01000000; + lstrcpyA(pInit+9+nUserLen,nloc->szHost); + len=10+nUserLen+nHostLen; + } + else len=9+nUserLen; + } + else { + *(PDWORD)(pInit+4)=DnsLookup(nlu,nloc->szHost); + if(*(PDWORD)(pInit+4)==0) { + mir_free(pInit); + return 0; + } + len=9+nUserLen; + } + if(NLSend(nlc,pInit,len,MSG_DUMPPROXY)==SOCKET_ERROR) { + Netlib_Logf(nlu,"%s %d: %s() failed (%u)",__FILE__,__LINE__,"NLSend",GetLastError()); + mir_free(pInit); + return 0; + } + mir_free(pInit); + + if(!WaitUntilReadable(nlc->s,30000)) { + Netlib_Logf(nlu,"%s %d: %s() failed (%u)",__FILE__,__LINE__,"WaitUntilReadable",GetLastError()); + return 0; + } + + len=NLRecv(nlc,reply,SIZEOF(reply),MSG_DUMPPROXY); + if(len < sizeof(reply) || reply[1]!=90) { + if(len != SOCKET_ERROR) { + if (len < SIZEOF(reply)) SetLastError(ERROR_BAD_FORMAT); + else switch(reply[1]) { + case 91: SetLastError(ERROR_ACCESS_DENIED); break; + case 92: SetLastError(ERROR_CONNECTION_UNAVAIL); break; + case 93: SetLastError(ERROR_INVALID_ACCESS); break; + default: SetLastError(ERROR_INVALID_DATA); break; + } + } + Netlib_Logf(nlu,"%s %d: %s() failed (%u)",__FILE__,__LINE__,"NLRecv",GetLastError()); + return 0; + } + //connected + return 1; +} + +static int NetlibInitSocks5Connection(struct NetlibConnection *nlc,struct NetlibUser *nlu,NETLIBOPENCONNECTION *nloc) +{ //rfc1928 + int len; + BYTE buf[256]; + + buf[0]=5; //yep, socks5 + buf[1]=1; //one auth method + buf[2]=nlu->settings.useProxyAuth?2:0; + if(NLSend(nlc,buf,3,MSG_DUMPPROXY)==SOCKET_ERROR) { + Netlib_Logf(nlu,"%s %d: %s() failed (%u)",__FILE__,__LINE__,"NLSend",GetLastError()); + return 0; + } + + if(!WaitUntilReadable(nlc->s,10000)) { + Netlib_Logf(nlu,"%s %d: %s() failed (%u)",__FILE__,__LINE__,"WaitUntilReadable",GetLastError()); + return 0; + } + + len=NLRecv(nlc,buf,2,MSG_DUMPPROXY); //confirmation of auth method + if(len<2 || (buf[1]!=0 && buf[1]!=2)) { + if(len!=SOCKET_ERROR) { + if(len<2) SetLastError(ERROR_BAD_FORMAT); + else SetLastError(ERROR_INVALID_ID_AUTHORITY); + } + Netlib_Logf(nlu,"%s %d: %s() failed (%u)",__FILE__,__LINE__,"NLRecv",GetLastError()); + return 0; + } + + if(buf[1]==2) { //rfc1929 + int nUserLen,nPassLen; + PBYTE pAuthBuf; + + nUserLen=lstrlenA(nlu->settings.szProxyAuthUser); + nPassLen=lstrlenA(nlu->settings.szProxyAuthPassword); + pAuthBuf=(PBYTE)mir_alloc(3+nUserLen+nPassLen); + pAuthBuf[0]=1; //auth version + pAuthBuf[1]=nUserLen; + memcpy(pAuthBuf+2,nlu->settings.szProxyAuthUser,nUserLen); + pAuthBuf[2+nUserLen]=nPassLen; + memcpy(pAuthBuf+3+nUserLen,nlu->settings.szProxyAuthPassword,nPassLen); + if(NLSend(nlc,pAuthBuf,3+nUserLen+nPassLen,MSG_DUMPPROXY)==SOCKET_ERROR) { + Netlib_Logf(nlu,"%s %d: %s() failed (%u)",__FILE__,__LINE__,"NLSend",GetLastError()); + mir_free(pAuthBuf); + return 0; + } + mir_free(pAuthBuf); + + if(!WaitUntilReadable(nlc->s,10000)) { + Netlib_Logf(nlu,"%s %d: %s() failed (%u)",__FILE__,__LINE__,"WaitUntilReadable",GetLastError()); + return 0; + } + + len=NLRecv(nlc,buf,SIZEOF(buf),MSG_DUMPPROXY); + if(len<2 || buf[1]) { + if(len!=SOCKET_ERROR) { + if(len<2) SetLastError(ERROR_BAD_FORMAT); + else SetLastError(ERROR_ACCESS_DENIED); + } + Netlib_Logf(nlu,"%s %d: %s() failed (%u)",__FILE__,__LINE__,"NLRecv",GetLastError()); + return 0; + } + } + + { PBYTE pInit; + int nHostLen; + DWORD hostIP; + + if(nlu->settings.dnsThroughProxy) { + if((hostIP=inet_addr(nloc->szHost))==INADDR_NONE) + nHostLen=lstrlenA(nloc->szHost)+1; + else nHostLen=4; + } + else { + if((hostIP=DnsLookup(nlu,nloc->szHost))==0) + return 0; + nHostLen=4; + } + pInit=(PBYTE)mir_alloc(6+nHostLen); + pInit[0]=5; //SOCKS5 + pInit[1]=1; //connect + pInit[2]=0; //reserved + if(hostIP==INADDR_NONE) { //DNS lookup through proxy + pInit[3]=3; + pInit[4]=nHostLen-1; + memcpy(pInit+5,nloc->szHost,nHostLen-1); + } + else { + pInit[3]=1; + *(PDWORD)(pInit+4)=hostIP; + } + *(PWORD)(pInit+4+nHostLen)=htons(nloc->wPort); + if(NLSend(nlc,pInit,6+nHostLen,MSG_DUMPPROXY)==SOCKET_ERROR) { + Netlib_Logf(nlu,"%s %d: %s() failed (%u)",__FILE__,__LINE__,"NLSend",GetLastError()); + mir_free(pInit); + return 0; + } + mir_free(pInit); + } + + if(!WaitUntilReadable(nlc->s,30000)) { + Netlib_Logf(nlu,"%s %d: %s() failed (%u)",__FILE__,__LINE__,"WaitUntilReadable",GetLastError()); + return 0; + } + + len=NLRecv(nlc,buf,SIZEOF(buf),MSG_DUMPPROXY); + if(len<7 || buf[0]!=5 || buf[1]) { + if(len!=SOCKET_ERROR) { + if(len<7 || buf[0]!=5) SetLastError(ERROR_BAD_FORMAT); + else switch(buf[1]) { + case 1: SetLastError(ERROR_GEN_FAILURE); break; + case 2: SetLastError(ERROR_ACCESS_DENIED); break; + case 3: SetLastError(WSAENETUNREACH); break; + case 4: SetLastError(WSAEHOSTUNREACH); break; + case 5: SetLastError(WSAECONNREFUSED); break; + case 6: SetLastError(WSAETIMEDOUT); break; + case 7: SetLastError(ERROR_CALL_NOT_IMPLEMENTED); break; + case 8: SetLastError(ERROR_INVALID_ADDRESS); break; + default: SetLastError(ERROR_INVALID_DATA); break; + } + } + Netlib_Logf(nlu,"%s %d: %s() failed (%u)",__FILE__,__LINE__,"NLRecv",GetLastError()); + return 0; + } + //connected + return 1; +} + +static int NetlibInitHttpsConnection(struct NetlibConnection *nlc,struct NetlibUser *nlu,NETLIBOPENCONNECTION *nloc) +{ //rfc2817 + NETLIBHTTPHEADER httpHeaders[3]; + NETLIBHTTPREQUEST nlhrSend={0},*nlhrReply; + char szUrl[512]; + + memset(httpHeaders,0,sizeof(httpHeaders)); + + nlhrSend.cbSize=sizeof(nlhrSend); + nlhrSend.requestType=REQUEST_CONNECT; + nlhrSend.flags=NLHRF_DUMPPROXY|NLHRF_SMARTAUTHHEADER|NLHRF_HTTP11; + if(nlu->settings.dnsThroughProxy) { + mir_snprintf(szUrl,SIZEOF(szUrl),"%s:%u",nloc->szHost,nloc->wPort); + if(inet_addr(nloc->szHost)==INADDR_NONE) { + httpHeaders[0].szName="Host"; + httpHeaders[0].szValue=szUrl; + nlhrSend.headersCount++; + } + } + else { + struct in_addr addr; + DWORD ip=DnsLookup(nlu,nloc->szHost); + if(ip==0) return 0; + addr.S_un.S_addr=ip; + mir_snprintf(szUrl,SIZEOF(szUrl),"%s:%u",inet_ntoa(addr),nloc->wPort); + } + nlhrSend.szUrl=szUrl; + nlhrSend.headers=httpHeaders; + nlhrSend.headersCount=0; + if(NetlibHttpSendRequest((WPARAM)nlc,(LPARAM)&nlhrSend)==SOCKET_ERROR) + return 0; + nlhrReply=(NETLIBHTTPREQUEST*)NetlibHttpRecvHeaders((WPARAM)nlc,MSG_DUMPPROXY); + if(nlhrReply==NULL) return 0; + if(nlhrReply->resultCode<200 || nlhrReply->resultCode>=300) { + NetlibHttpSetLastErrorUsingHttpResult(nlhrReply->resultCode); + Netlib_Logf(nlu,"%s %d: %s request failed (%u %s)",__FILE__,__LINE__,nlu->settings.proxyType==PROXYTYPE_HTTP?"HTTP":"HTTPS",nlhrReply->resultCode,nlhrReply->szResultDescr); + NetlibHttpFreeRequestStruct(0,(LPARAM)nlhrReply); + return 0; + } + NetlibHttpFreeRequestStruct(0,(LPARAM)nlhrReply); + //connected + return 1; +} + +static void FreePartiallyInitedConnection(struct NetlibConnection *nlc) +{ + DWORD dwOriginalLastError=GetLastError(); + + if(nlc->s!=INVALID_SOCKET) closesocket(nlc->s); + if(nlc->nlhpi.szHttpPostUrl) mir_free(nlc->nlhpi.szHttpPostUrl); + if(nlc->nlhpi.szHttpGetUrl) mir_free(nlc->nlhpi.szHttpGetUrl); + if(nlc->hInstSecurityDll) FreeLibrary(nlc->hInstSecurityDll); + NetlibDeleteNestedCS(&nlc->ncsSend); + NetlibDeleteNestedCS(&nlc->ncsRecv); + CloseHandle(nlc->hOkToCloseEvent); + DeleteCriticalSection(&nlc->csHttpSequenceNums); + mir_free(nlc); + SetLastError(dwOriginalLastError); +} + +#define PortInMask(mask,p) ((mask)[((p)&0xFFFF)>>3]&(1<<((p)&7))) + +static int my_connect(SOCKET s, const struct sockaddr * name, int namelen, NETLIBOPENCONNECTION * nloc) +{ + int rc=0; + unsigned int dwTimeout=( nloc->cbSize==sizeof(NETLIBOPENCONNECTION) && nloc->flags&NLOCF_V2 ) ? nloc->timeout : 0; + u_long notblocking=1; + TIMEVAL tv; + DWORD lasterr = 0; + int waitdiff; + // if dwTimeout is zero then its an old style connection or new with a 0 timeout, select() will error quicker anyway + if ( dwTimeout == 0 ) + dwTimeout += 60; + // return the socket to non blocking + if ( ioctlsocket(s, FIONBIO, ¬blocking) != 0 ) { + return SOCKET_ERROR; + } + // this is for XP SP2 where there is a default connection attempt limit of 10/second + EnterCriticalSection(&csNetlibUser); + waitdiff=GetTickCount() - g_LastConnectionTick; + g_LastConnectionTick=GetTickCount(); + LeaveCriticalSection(&csNetlibUser); + if ( waitdiff < 1000 ) { + // last connection was less than 1 second ago, wait 1.2 seconds + SleepEx(1200,TRUE); + } + // might of died in between the wait + if ( Miranda_Terminated() ) { + rc=SOCKET_ERROR; + lasterr=ERROR_TIMEOUT; + goto unblock; + } + // try a connect + if ( connect(s, name, namelen) == 0 ) { + goto unblock; + } + // didn't work, was it cos of nonblocking? + if ( WSAGetLastError() != WSAEWOULDBLOCK ) { + rc=SOCKET_ERROR; + lasterr=WSAGetLastError(); + goto unblock; + } + // setup select() + tv.tv_sec=1; + tv.tv_usec=0; + for (;;) { + fd_set r, w, e; + FD_ZERO(&r); FD_ZERO(&w); FD_ZERO(&e); + FD_SET(s, &r); + FD_SET(s, &w); + FD_SET(s, &e); + if ( (rc=select(0, &r, &w, &e, &tv)) == SOCKET_ERROR ) { + break; + } + if ( rc > 0 ) { + if ( FD_ISSET(s, &r) ) { + // connection was closed + rc=SOCKET_ERROR; + lasterr=WSAECONNRESET; + } + if ( FD_ISSET(s, &w) ) { + // connection was successful + rc=0; + } + if ( FD_ISSET(s, &e) ) { + // connection failed. + int len=sizeof(lasterr); + rc=SOCKET_ERROR; + getsockopt(s,SOL_SOCKET,SO_ERROR,(char*)&lasterr,&len); + } + goto unblock; + } else if ( Miranda_Terminated() ) { + rc=SOCKET_ERROR; + lasterr=ERROR_TIMEOUT; + goto unblock; + } else if ( nloc->cbSize==sizeof(NETLIBOPENCONNECTION) && nloc->flags&NLOCF_V2 && nloc->waitcallback != NULL + && nloc->waitcallback(&dwTimeout) == 0) { + rc=SOCKET_ERROR; + lasterr=ERROR_TIMEOUT; + goto unblock; + } + if ( --dwTimeout == 0 ) { + rc=SOCKET_ERROR; + lasterr=ERROR_TIMEOUT; + goto unblock; + } + } +unblock: + notblocking=0; + ioctlsocket(s, FIONBIO, ¬blocking); + SetLastError(lasterr); + return rc; +} + +int NetlibOpenConnection(WPARAM wParam,LPARAM lParam) +{ + NETLIBOPENCONNECTION *nloc=(NETLIBOPENCONNECTION*)lParam; + struct NetlibUser *nlu=(struct NetlibUser*)wParam; + struct NetlibConnection *nlc; + SOCKADDR_IN sin; + + if(GetNetlibHandleType(nlu)!=NLH_USER || !(nlu->user.flags&NUF_OUTGOING) || nloc==NULL + || !(nloc->cbSize==NETLIBOPENCONNECTION_V1_SIZE||nloc->cbSize==sizeof(NETLIBOPENCONNECTION)) || nloc->szHost==NULL || nloc->wPort==0) { + SetLastError(ERROR_INVALID_PARAMETER); + return (int)(HANDLE)NULL; + } + nlc=(struct NetlibConnection*)mir_calloc(sizeof(struct NetlibConnection)); + nlc->handleType=NLH_CONNECTION; + nlc->nlu=nlu; + nlc->s=socket(AF_INET,SOCK_STREAM,0); + if(nlc->s==INVALID_SOCKET) { + Netlib_Logf(nlu,"%s %d: %s() failed (%u)",__FILE__,__LINE__,"socket",WSAGetLastError()); + mir_free(nlc); + return (int)(HANDLE)NULL; + } + if (nlu->settings.specifyOutgoingPorts && nlu->settings.szOutgoingPorts) + { + BYTE portsMask[0x2000]; + int startPort,portNum,i,j,portsCount; + + sin.sin_family=AF_INET; + sin.sin_addr.s_addr=htonl(INADDR_ANY); + sin.sin_port=0; + + portsCount=StringToPortsMask(nlu->settings.szOutgoingPorts,portsMask); + if (portsCount) { + startPort=rand() % portsCount; + for (i=0;i<0x02000;i++) { + if(portsMask[i]==0) continue; + if(portsMask[i]==0xFF && startPort>=8) {startPort-=8; continue;} + for(j=0;j<8;j++) + if(portsMask[i]&(1<s,(SOCKADDR*)&sin,sizeof(sin))==0) break; + for(portNum++;!PortInMask(portsMask,portNum);portNum++) + if(portNum==0xFFFF) portNum=0; + } while (portNum!=startPort); + } //if + } //if + } + InitializeCriticalSection(&nlc->csHttpSequenceNums); + nlc->hOkToCloseEvent=CreateEvent(NULL,TRUE,TRUE,NULL); + nlc->dontCloseNow=0; + NetlibInitializeNestedCS(&nlc->ncsSend); + NetlibInitializeNestedCS(&nlc->ncsRecv); + if(nlu->settings.useProxy && (nlu->settings.proxyType==PROXYTYPE_HTTP || nlu->settings.proxyType==PROXYTYPE_HTTPS) && nlu->settings.useProxyAuth && nlu->settings.useProxyAuthNtlm) + nlc->hInstSecurityDll=LoadLibraryA("security.dll"); + + nlc->sinProxy.sin_family=AF_INET; + if(nlu->settings.useProxy) { + nlc->sinProxy.sin_port=htons((short)nlu->settings.wProxyPort); + nlc->sinProxy.sin_addr.S_un.S_addr=DnsLookup(nlu,nlu->settings.szProxyServer); + } + else { + nlc->sinProxy.sin_port=htons((short)nloc->wPort); + nlc->sinProxy.sin_addr.S_un.S_addr=DnsLookup(nlu,nloc->szHost); + } + if(nlc->sinProxy.sin_addr.S_un.S_addr==0 + || my_connect(nlc->s,(SOCKADDR *)&nlc->sinProxy,sizeof(nlc->sinProxy), nloc)==SOCKET_ERROR) { + if(nlc->sinProxy.sin_addr.S_un.S_addr) + Netlib_Logf(nlu,"%s %d: %s() failed (%u)",__FILE__,__LINE__,"connect",WSAGetLastError()); + FreePartiallyInitedConnection(nlc); + return (int)(HANDLE)NULL; + } + + if(nlu->settings.useProxy + && !(nloc->flags&NLOCF_HTTP + && (nlu->settings.proxyType==PROXYTYPE_HTTP || nlu->settings.proxyType==PROXYTYPE_HTTPS))) + { + if(!WaitUntilWritable(nlc->s,30000)) { + FreePartiallyInitedConnection(nlc); + return (int)(HANDLE)NULL; + } + + switch(nlu->settings.proxyType) { + case PROXYTYPE_SOCKS4: + if(!NetlibInitSocks4Connection(nlc,nlu,nloc)) { + FreePartiallyInitedConnection(nlc); + return (int)(HANDLE)NULL; + } + break; + + case PROXYTYPE_SOCKS5: + if(!NetlibInitSocks5Connection(nlc,nlu,nloc)) { + FreePartiallyInitedConnection(nlc); + return (int)(HANDLE)NULL; + } + break; + + case PROXYTYPE_HTTPS: + if(!NetlibInitHttpsConnection(nlc,nlu,nloc)) { + FreePartiallyInitedConnection(nlc); + return (int)(HANDLE)NULL; + } + break; + + case PROXYTYPE_HTTP: + if(!(nlu->user.flags&NUF_HTTPGATEWAY)) { + //NLOCF_HTTP not specified and no HTTP gateway available: try HTTPS + if(!NetlibInitHttpsConnection(nlc,nlu,nloc)) { + //can't do HTTPS: try direct + if(nlc->s!=INVALID_SOCKET) closesocket(nlc->s); + + nlc->s=socket(AF_INET,SOCK_STREAM,0); + if(nlc->s==INVALID_SOCKET) { + FreePartiallyInitedConnection(nlc); + return (int)(HANDLE)NULL; + } + nlc->sinProxy.sin_family=AF_INET; + nlc->sinProxy.sin_port=htons((short)nloc->wPort); + nlc->sinProxy.sin_addr.S_un.S_addr=DnsLookup(nlu,nloc->szHost); + if(nlc->sinProxy.sin_addr.S_un.S_addr==0 + || my_connect(nlc->s,(SOCKADDR *)&nlc->sinProxy,sizeof(nlc->sinProxy), nloc)==SOCKET_ERROR) { + if(nlc->sinProxy.sin_addr.S_un.S_addr) + Netlib_Logf(nlu,"%s %d: %s() failed (%u)",__FILE__,__LINE__,"connect",WSAGetLastError()); + FreePartiallyInitedConnection(nlc); + return (int)(HANDLE)NULL; + } + } + } + else if(!NetlibInitHttpConnection(nlc,nlu,nloc)) { + FreePartiallyInitedConnection(nlc); + return (int)(HANDLE)NULL; + } + break; + + default: + SetLastError(ERROR_INVALID_PARAMETER); + FreePartiallyInitedConnection(nlc); + return (int)(HANDLE)NULL; + } + } + Netlib_Logf(nlu,"(%d) Connected to %s:%d",nlc->s,nloc->szHost,nloc->wPort); + return (int)nlc; +} diff --git a/miranda-wine/src/modules/netlib/netlibopts.c b/miranda-wine/src/modules/netlib/netlibopts.c new file mode 100644 index 0000000..918b940 --- /dev/null +++ b/miranda-wine/src/modules/netlib/netlibopts.c @@ -0,0 +1,516 @@ +/* + +Miranda IM: the free IM client for Microsoft* Windows* + +Copyright 2000-2006 Miranda ICQ/IM project, +all portions of this codebase are copyrighted to the people +listed in contributors.txt. + +This program is free software; you can redistribute it and/or +modify it under the terms of the GNU General Public License +as published by the Free Software Foundation; either version 2 +of the License, or (at your option) any later version. + +This program is distributed in the hope that it will be useful, +but WITHOUT ANY WARRANTY; without even the implied warranty of +MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +GNU General Public License for more details. + +You should have received a copy of the GNU General Public License +along with this program; if not, write to the Free Software +Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. +*/ +#include "commonheaders.h" +#include "netlib.h" + +extern struct NetlibUser **netlibUser; +extern int netlibUserCount; +extern CRITICAL_SECTION csNetlibUser; + +struct NetlibTempSettings { + DWORD flags; + char *szSettingsModule; + NETLIBUSERSETTINGS settings; +} static *tempSettings; +static int tempSettingsCount; + +static const UINT outgoingConnectionsControls[]={ + IDC_STATIC12, + IDC_USEPROXY, + IDC_STATIC21,IDC_PROXYTYPE, + IDC_STATIC22,IDC_PROXYHOST,IDC_STATIC23,IDC_PROXYPORT,IDC_STOFTENPORT, + IDC_PROXYAUTH, + IDC_STATIC31,IDC_PROXYUSER,IDC_STATIC32,IDC_PROXYPASS, + IDC_PROXYAUTHNTLM, + IDC_PROXYDNS, + IDC_SPECIFYPORTSO, + IDC_STATIC53,IDC_PORTSRANGEO, + IDC_STATIC54}; +static const UINT useProxyControls[]={ + IDC_STATIC21,IDC_PROXYTYPE, + IDC_STATIC22,IDC_PROXYHOST,IDC_STATIC23,IDC_PROXYPORT,IDC_STOFTENPORT, + IDC_PROXYAUTH, + IDC_STATIC31,IDC_PROXYUSER,IDC_STATIC32,IDC_PROXYPASS, + IDC_PROXYAUTHNTLM, + IDC_PROXYDNS}; +static const UINT specifyOPortsControls[]={ + IDC_STATIC53,IDC_PORTSRANGEO, + IDC_STATIC54 +}; +static const UINT incomingConnectionsControls[]={ + IDC_STATIC43, + IDC_SPECIFYPORTS, + IDC_STATIC51,IDC_PORTSRANGE, + IDC_STATIC52}; +static const UINT specifyPortsControls[]={ + IDC_STATIC51,IDC_PORTSRANGE, + IDC_STATIC52}; +static const TCHAR* szProxyTypes[]={_T(""),_T("SOCKS4"),_T("SOCKS5"),_T("HTTP"),_T("HTTPS")}; +static const WORD oftenProxyPorts[]={1080,1080,1080,8080,8080}; + +#define M_REFRESHALL (WM_USER+100) +#define M_REFRESHENABLING (WM_USER+101) + +static void ShowMultipleControls(HWND hwndDlg,const UINT *controls,int cControls,int state) +{ + int i; + for(i=0;iszIncomingPorts) dest->szIncomingPorts=mir_strdup(dest->szIncomingPorts); + if(dest->szOutgoingPorts) dest->szOutgoingPorts=mir_strdup(dest->szOutgoingPorts); + if(dest->szProxyAuthPassword) dest->szProxyAuthPassword=mir_strdup(dest->szProxyAuthPassword); + if(dest->szProxyAuthUser) dest->szProxyAuthUser=mir_strdup(dest->szProxyAuthUser); + if(dest->szProxyServer) dest->szProxyServer=mir_strdup(dest->szProxyServer); +} + +static void CombineSettingsStrings(char **dest,char **source) +{ + if(*dest!=NULL && (*source==NULL || lstrcmpiA(*dest,*source))) {mir_free(*dest); *dest=NULL;} +} + +static void CombineSettingsStructs(NETLIBUSERSETTINGS *dest,DWORD *destFlags,NETLIBUSERSETTINGS *source,DWORD sourceFlags) +{ + if(sourceFlags&NUF_OUTGOING) { + if(*destFlags&NUF_OUTGOING) { + if(dest->useProxy!=source->useProxy) dest->useProxy=2; + if(dest->proxyType!=source->proxyType) dest->proxyType=0; + CombineSettingsStrings(&dest->szProxyServer,&source->szProxyServer); + if(dest->wProxyPort!=source->wProxyPort) dest->wProxyPort=0; + if(dest->useProxyAuth!=source->useProxyAuth) dest->useProxyAuth=2; + CombineSettingsStrings(&dest->szProxyAuthUser,&source->szProxyAuthUser); + CombineSettingsStrings(&dest->szProxyAuthPassword,&source->szProxyAuthPassword); + if(dest->useProxyAuthNtlm!=source->useProxyAuthNtlm) dest->useProxyAuthNtlm=2; + if(dest->dnsThroughProxy!=source->dnsThroughProxy) dest->dnsThroughProxy=2; + if(dest->specifyOutgoingPorts!=source->specifyOutgoingPorts) dest->specifyOutgoingPorts=2; + CombineSettingsStrings(&dest->szOutgoingPorts,&source->szOutgoingPorts); + } + else { + dest->useProxy=source->useProxy; + dest->proxyType=source->proxyType; + dest->szProxyServer=source->szProxyServer; + if(dest->szProxyServer) dest->szProxyServer=mir_strdup(dest->szProxyServer); + dest->wProxyPort=source->wProxyPort; + dest->useProxyAuth=source->useProxyAuth; + dest->szProxyAuthUser=source->szProxyAuthUser; + if(dest->szProxyAuthUser) dest->szProxyAuthUser=mir_strdup(dest->szProxyAuthUser); + dest->szProxyAuthPassword=source->szProxyAuthPassword; + if(dest->szProxyAuthPassword) dest->szProxyAuthPassword=mir_strdup(dest->szProxyAuthPassword); + dest->useProxyAuthNtlm=source->useProxyAuthNtlm; + dest->dnsThroughProxy=source->dnsThroughProxy; + dest->specifyOutgoingPorts=source->specifyOutgoingPorts; + dest->szOutgoingPorts=source->szOutgoingPorts; + if(dest->szOutgoingPorts) dest->szOutgoingPorts=mir_strdup(dest->szOutgoingPorts); + } + } + if(sourceFlags&NUF_INCOMING) { + if(*destFlags&NUF_INCOMING) { + if(dest->specifyIncomingPorts!=source->specifyIncomingPorts) dest->specifyIncomingPorts=2; + CombineSettingsStrings(&dest->szIncomingPorts,&source->szIncomingPorts); + } + else { + dest->specifyIncomingPorts=source->specifyIncomingPorts; + dest->szIncomingPorts=source->szIncomingPorts; + if(dest->szIncomingPorts) dest->szIncomingPorts=mir_strdup(dest->szIncomingPorts); + } + } + if((*destFlags&NUF_NOHTTPSOPTION)!=(sourceFlags&NUF_NOHTTPSOPTION)) + *destFlags=(*destFlags|sourceFlags)&~NUF_NOHTTPSOPTION; + else *destFlags|=sourceFlags; +} + +static void ChangeSettingIntByCheckbox(HWND hwndDlg,UINT ctrlId,int iUser,int memberOffset) +{ + int newValue,i; + + newValue=IsDlgButtonChecked(hwndDlg,ctrlId)!=BST_CHECKED; + CheckDlgButton(hwndDlg,ctrlId,newValue?BST_CHECKED:BST_UNCHECKED); + if(iUser==-1) { + for(i=0;iuseProxy); + DBWriteContactSettingByte(NULL,szSettingsModule,"NLProxyType",(BYTE)settings->proxyType); + DBWriteContactSettingString(NULL,szSettingsModule,"NLProxyServer",settings->szProxyServer?settings->szProxyServer:""); + DBWriteContactSettingWord(NULL,szSettingsModule,"NLProxyPort",(WORD)settings->wProxyPort); + DBWriteContactSettingByte(NULL,szSettingsModule,"NLUseProxyAuth",(BYTE)settings->useProxyAuth); + DBWriteContactSettingString(NULL,szSettingsModule,"NLProxyAuthUser",settings->szProxyAuthUser?settings->szProxyAuthUser:""); + lstrcpynA(szEncodedPassword,settings->szProxyAuthPassword?settings->szProxyAuthPassword:"",SIZEOF(szEncodedPassword)); + CallService(MS_DB_CRYPT_ENCODESTRING,SIZEOF(szEncodedPassword),(LPARAM)szEncodedPassword); + DBWriteContactSettingString(NULL,szSettingsModule,"NLProxyAuthPassword",szEncodedPassword); + DBWriteContactSettingByte(NULL,szSettingsModule,"NLUseProxyAuthNtlm",(BYTE)settings->useProxyAuthNtlm); + DBWriteContactSettingByte(NULL,szSettingsModule,"NLDnsThroughProxy",(BYTE)settings->dnsThroughProxy); + DBWriteContactSettingByte(NULL,szSettingsModule,"NLSpecifyOutgoingPorts",(BYTE)settings->specifyOutgoingPorts); + DBWriteContactSettingString(NULL,szSettingsModule,"NLOutgoingPorts",settings->szOutgoingPorts?settings->szOutgoingPorts:""); + } + if(flags&NUF_INCOMING) { + DBWriteContactSettingByte(NULL,szSettingsModule,"NLSpecifyIncomingPorts",(BYTE)settings->specifyIncomingPorts); + DBWriteContactSettingString(NULL,szSettingsModule,"NLIncomingPorts",settings->szIncomingPorts?settings->szIncomingPorts:""); + } +} + +void NetlibSaveUserSettingsStruct(const char *szSettingsModule,NETLIBUSERSETTINGS *settings) +{ + int iUser,i; + NETLIBUSERSETTINGS combinedSettings={0}; + DWORD flags; + + EnterCriticalSection(&csNetlibUser); + for(iUser=0;iUseruser.szSettingsModule)) break; + if(iUser==netlibUserCount) { + LeaveCriticalSection(&csNetlibUser); + return; + } + NetlibFreeUserSettingsStruct(&netlibUser[iUser]->settings); + CopySettingsStruct(&netlibUser[iUser]->settings,settings); + WriteSettingsStructToDb(netlibUser[iUser]->user.szSettingsModule,&netlibUser[iUser]->settings,netlibUser[iUser]->user.flags); + combinedSettings.cbSize=sizeof(combinedSettings); + for(i=0,flags=0;iuser.flags&NUF_NOOPTIONS) continue; + CombineSettingsStructs(&combinedSettings,&flags,&netlibUser[iUser]->settings,netlibUser[iUser]->user.flags); + } + if(combinedSettings.useProxy==2) combinedSettings.useProxy=0; + if(combinedSettings.proxyType==0) combinedSettings.proxyType=PROXYTYPE_SOCKS5; + if(combinedSettings.useProxyAuth==2) combinedSettings.useProxyAuth=0; + if(combinedSettings.useProxyAuthNtlm==2) combinedSettings.useProxyAuthNtlm=0; + if(combinedSettings.dnsThroughProxy==2) combinedSettings.dnsThroughProxy=1; + if(combinedSettings.specifyIncomingPorts==2) combinedSettings.specifyIncomingPorts=0; + WriteSettingsStructToDb("Netlib",&combinedSettings,flags); + NetlibFreeUserSettingsStruct(&combinedSettings); + LeaveCriticalSection(&csNetlibUser); +} + +static BOOL CALLBACK DlgProcNetlibOpts(HWND hwndDlg, UINT msg, WPARAM wParam, LPARAM lParam) +{ + switch (msg) + { + case WM_INITDIALOG: + { int iUser,iItem; + + TranslateDialogDefault(hwndDlg); + iItem=SendDlgItemMessage(hwndDlg,IDC_NETLIBUSERS,CB_ADDSTRING,0,(LPARAM)TranslateT("")); + SendDlgItemMessage(hwndDlg,IDC_NETLIBUSERS,CB_SETITEMDATA,iItem,(LPARAM)-1); + SendDlgItemMessage(hwndDlg,IDC_NETLIBUSERS,CB_SETCURSEL,iItem,0); + EnterCriticalSection(&csNetlibUser); + tempSettingsCount=netlibUserCount; + tempSettings=(struct NetlibTempSettings*)mir_alloc(sizeof(struct NetlibTempSettings)*tempSettingsCount); + for(iUser=0;iUseruser.flags; + tempSettings[iUser].szSettingsModule=mir_strdup(netlibUser[iUser]->user.szSettingsModule); + CopySettingsStruct(&tempSettings[iUser].settings,&netlibUser[iUser]->settings); + if(netlibUser[iUser]->user.flags&NUF_NOOPTIONS) continue; + iItem=SendDlgItemMessageA(hwndDlg,IDC_NETLIBUSERS,CB_ADDSTRING,0,(LPARAM)netlibUser[iUser]->user.szDescriptiveName); + SendDlgItemMessage(hwndDlg,IDC_NETLIBUSERS,CB_SETITEMDATA,iItem,iUser); + } + LeaveCriticalSection(&csNetlibUser); + SendMessage(hwndDlg,M_REFRESHALL,0,0); + return TRUE; + } + case M_REFRESHALL: + { int iUser=SendDlgItemMessage(hwndDlg,IDC_NETLIBUSERS,CB_GETITEMDATA,SendDlgItemMessage(hwndDlg,IDC_NETLIBUSERS,CB_GETCURSEL,0,0),0); + NETLIBUSERSETTINGS settings={0}; + DWORD flags; + + if(iUser==-1) { + int i; + settings.cbSize=sizeof(settings); + for(i=0,flags=0;iidFrom) { + case 0: + switch (((LPNMHDR)lParam)->code) + { + case PSN_APPLY: + { int iUser; + for(iUser=0;iUseruser.flags&NUF_NOOPTIONS)) optionsCount++; + LeaveCriticalSection(&csNetlibUser); + if ( optionsCount == 0 ) + return 0; + + odp.cbSize = sizeof(odp); + odp.position = 900000000; + odp.hInstance = GetModuleHandle(NULL); + odp.pszTemplate = MAKEINTRESOURCEA(IDD_OPT_NETLIB); + odp.pszTitle = "Network"; + odp.pfnDlgProc = DlgProcNetlibOpts; + odp.flags = ODPF_BOLDGROUPS; + odp.expertOnlyControls = expertOnlyControls; + odp.nExpertOnlyControls = SIZEOF( expertOnlyControls ); + CallService( MS_OPT_ADDPAGE, wParam, ( LPARAM )&odp ); + return 0; +} diff --git a/miranda-wine/src/modules/netlib/netlibpktrecver.c b/miranda-wine/src/modules/netlib/netlibpktrecver.c new file mode 100644 index 0000000..7bc6eb4 --- /dev/null +++ b/miranda-wine/src/modules/netlib/netlibpktrecver.c @@ -0,0 +1,85 @@ +/* + +Miranda IM: the free IM client for Microsoft* Windows* + +Copyright 2000-2006 Miranda ICQ/IM project, +all portions of this codebase are copyrighted to the people +listed in contributors.txt. + +This program is free software; you can redistribute it and/or +modify it under the terms of the GNU General Public License +as published by the Free Software Foundation; either version 2 +of the License, or (at your option) any later version. + +This program is distributed in the hope that it will be useful, +but WITHOUT ANY WARRANTY; without even the implied warranty of +MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +GNU General Public License for more details. + +You should have received a copy of the GNU General Public License +along with this program; if not, write to the Free Software +Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. +*/ +#include "commonheaders.h" +#include "netlib.h" + +int NetlibPacketRecverCreate(WPARAM wParam,LPARAM lParam) +{ + struct NetlibConnection *nlc=(struct NetlibConnection*)wParam; + struct NetlibPacketRecver *nlpr; + + if(GetNetlibHandleType(nlc)!=NLH_CONNECTION || lParam==0) { + SetLastError(ERROR_INVALID_PARAMETER); + return (int)(struct NetlibPacketRecver*)NULL; + } + nlpr=(struct NetlibPacketRecver*)mir_calloc(sizeof(struct NetlibPacketRecver)); + if(nlpr==NULL) { + SetLastError(ERROR_OUTOFMEMORY); + return (int)(struct NetlibPacketRecver*)NULL; + } + nlpr->handleType=NLH_PACKETRECVER; + nlpr->nlc=nlc; + nlpr->packetRecver.cbSize=sizeof(nlpr->packetRecver); + nlpr->packetRecver.bufferSize=lParam; + nlpr->packetRecver.buffer=(PBYTE)mir_alloc(nlpr->packetRecver.bufferSize); + nlpr->packetRecver.bytesUsed=0; + nlpr->packetRecver.bytesAvailable=0; + return (int)nlpr; +} + +int NetlibPacketRecverGetMore(WPARAM wParam,LPARAM lParam) +{ + struct NetlibPacketRecver *nlpr=(struct NetlibPacketRecver*)wParam; + NETLIBPACKETRECVER *nlprParam=(NETLIBPACKETRECVER*)lParam; + int recvResult; + + if(GetNetlibHandleType(nlpr)!=NLH_PACKETRECVER || nlprParam==NULL || nlprParam->cbSize!=sizeof(NETLIBPACKETRECVER) || nlprParam->bytesUsed>nlpr->packetRecver.bytesAvailable) { + SetLastError(ERROR_INVALID_PARAMETER); + return SOCKET_ERROR; + } + if (Miranda_Terminated()) { /* HACK: Lame, break while loops of protocols that can't kill their while loops, (cough, ICQ, cough) */ + SetLastError(ERROR_TIMEOUT); + return SOCKET_ERROR; + } + nlpr->packetRecver.dwTimeout=nlprParam->dwTimeout; + if(nlprParam->bytesUsed==0) { + if(nlpr->packetRecver.bytesAvailable==nlpr->packetRecver.bufferSize) { + nlpr->packetRecver.bytesAvailable=0; + Netlib_Logf(nlpr->nlc->nlu,"Packet recver: packet overflowed buffer, ditching"); + } + } + else { + MoveMemory(nlpr->packetRecver.buffer,nlpr->packetRecver.buffer+nlprParam->bytesUsed,nlpr->packetRecver.bytesAvailable-nlprParam->bytesUsed); + nlpr->packetRecver.bytesAvailable-=nlprParam->bytesUsed; + } + if(nlprParam->dwTimeout!=INFINITE) { + if(!WaitUntilReadable(nlpr->nlc->s,nlprParam->dwTimeout)) { + *nlprParam=nlpr->packetRecver; + return SOCKET_ERROR; + } + } + recvResult=NLRecv(nlpr->nlc,nlpr->packetRecver.buffer+nlpr->packetRecver.bytesAvailable,nlpr->packetRecver.bufferSize-nlpr->packetRecver.bytesAvailable,0); + if(recvResult>0) nlpr->packetRecver.bytesAvailable+=recvResult; + *nlprParam=nlpr->packetRecver; + return recvResult; +} diff --git a/miranda-wine/src/modules/netlib/netlibsock.c b/miranda-wine/src/modules/netlib/netlibsock.c new file mode 100644 index 0000000..acedd8e --- /dev/null +++ b/miranda-wine/src/modules/netlib/netlibsock.c @@ -0,0 +1,166 @@ +/* + +Miranda IM: the free IM client for Microsoft* Windows* + +Copyright 2000-2006 Miranda ICQ/IM project, +all portions of this codebase are copyrighted to the people +listed in contributors.txt. + +This program is free software; you can redistribute it and/or +modify it under the terms of the GNU General Public License +as published by the Free Software Foundation; either version 2 +of the License, or (at your option) any later version. + +This program is distributed in the hope that it will be useful, +but WITHOUT ANY WARRANTY; without even the implied warranty of +MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +GNU General Public License for more details. + +You should have received a copy of the GNU General Public License +along with this program; if not, write to the Free Software +Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. +*/ +#include "commonheaders.h" +#include "netlib.h" + +extern CRITICAL_SECTION csNetlibCloseHandle; +extern HANDLE hConnectionHeaderMutex; + +int NetlibSend(WPARAM wParam,LPARAM lParam) +{ + struct NetlibConnection *nlc=(struct NetlibConnection*)wParam; + NETLIBBUFFER *nlb=(NETLIBBUFFER*)lParam; + int result; + + if(nlb==NULL) { + SetLastError(ERROR_INVALID_PARAMETER); + return SOCKET_ERROR; + } + + if(!NetlibEnterNestedCS(nlc,NLNCS_SEND)) return SOCKET_ERROR; + if(nlc->usingHttpGateway && !(nlb->flags&MSG_RAW)) { + if(!(nlb->flags&MSG_NOHTTPGATEWAYWRAP) && nlc->nlu->user.pfnHttpGatewayWrapSend) { + NetlibDumpData(nlc,nlb->buf,nlb->len,1,nlb->flags); + result=nlc->nlu->user.pfnHttpGatewayWrapSend((HANDLE)nlc,nlb->buf,nlb->len,nlb->flags|MSG_NOHTTPGATEWAYWRAP,NetlibSend); + } + else result=NetlibHttpGatewayPost(nlc,nlb->buf,nlb->len,nlb->flags); + } + else { + NetlibDumpData(nlc,nlb->buf,nlb->len,1,nlb->flags); + result=send(nlc->s,nlb->buf,nlb->len,nlb->flags&0xFFFF); + } + NetlibLeaveNestedCS(&nlc->ncsSend); + return result; +} + +int NetlibRecv(WPARAM wParam,LPARAM lParam) +{ + struct NetlibConnection *nlc=(struct NetlibConnection*)wParam; + NETLIBBUFFER *nlb=(NETLIBBUFFER*)lParam; + int recvResult; + + if(nlb==NULL) { + SetLastError(ERROR_INVALID_PARAMETER); + return SOCKET_ERROR; + } + if(!NetlibEnterNestedCS(nlc,NLNCS_RECV)) return SOCKET_ERROR; + if(nlc->usingHttpGateway && !(nlb->flags&MSG_RAW)) + recvResult=NetlibHttpGatewayRecv(nlc,nlb->buf,nlb->len,nlb->flags); + else + recvResult=recv(nlc->s,nlb->buf,nlb->len,nlb->flags&0xFFFF); + NetlibLeaveNestedCS(&nlc->ncsRecv); + if(recvResult<=0) return recvResult; + NetlibDumpData(nlc,nlb->buf,recvResult,0,nlb->flags); + return recvResult; +} + +static int ConnectionListToSocketList(HANDLE *hConns,fd_set *fd) +{ + struct NetlibConnection *nlcCheck; + int i; + + FD_ZERO(fd); + for(i=0;hConns[i] && hConns[i]!=INVALID_HANDLE_VALUE && ihandleType!=NLH_CONNECTION && nlcCheck->handleType!=NLH_BOUNDPORT) { + SetLastError(ERROR_INVALID_DATA); + return 0; + } + FD_SET(nlcCheck->s,fd); + } + return 1; +} + +int NetlibSelect(WPARAM wParam,LPARAM lParam) +{ + NETLIBSELECT *nls=(NETLIBSELECT*)lParam; + fd_set readfd,writefd,exceptfd; + TIMEVAL tv; + + if(nls==NULL || nls->cbSize!=sizeof(NETLIBSELECT)) { + SetLastError(ERROR_INVALID_PARAMETER); + return SOCKET_ERROR; + } + tv.tv_sec=nls->dwTimeout/1000; + tv.tv_usec=(nls->dwTimeout%1000)*1000; + WaitForSingleObject(hConnectionHeaderMutex,INFINITE); + if(!ConnectionListToSocketList(nls->hReadConns,&readfd) + || !ConnectionListToSocketList(nls->hWriteConns,&writefd) + || !ConnectionListToSocketList(nls->hExceptConns,&exceptfd)) { + ReleaseMutex(hConnectionHeaderMutex); + return SOCKET_ERROR; + } + ReleaseMutex(hConnectionHeaderMutex); + return select(0,&readfd,&writefd,&exceptfd,nls->dwTimeout==INFINITE?NULL:&tv); +} + +int NetlibSelectEx(WPARAM wParam,LPARAM lParam) +{ + NETLIBSELECTEX *nls=(NETLIBSELECTEX*)lParam; + fd_set readfd,writefd,exceptfd; + TIMEVAL tv; + int rc=SOCKET_ERROR; + int j; + struct NetlibConnection *conn=NULL; + + if(nls==NULL || nls->cbSize!=sizeof(NETLIBSELECTEX)) { + SetLastError(ERROR_INVALID_PARAMETER); + return SOCKET_ERROR; + } + tv.tv_sec=nls->dwTimeout/1000; + tv.tv_usec=(nls->dwTimeout%1000)*1000; + WaitForSingleObject(hConnectionHeaderMutex,INFINITE); + if(!ConnectionListToSocketList(nls->hReadConns,&readfd) + || !ConnectionListToSocketList(nls->hWriteConns,&writefd) + || !ConnectionListToSocketList(nls->hExceptConns,&exceptfd)) { + ReleaseMutex(hConnectionHeaderMutex); + return SOCKET_ERROR; + } + ReleaseMutex(hConnectionHeaderMutex); + rc=select(0,&readfd,&writefd,&exceptfd,nls->dwTimeout==INFINITE?NULL:&tv); + WaitForSingleObject(hConnectionHeaderMutex,INFINITE); + /* go thru each passed HCONN array and grab its socket handle, then give it to FD_ISSET() + to see if an event happened for that socket, if it has it will be returned as TRUE (otherwise not) + This happens for read/write/except */ + for (j=0; jhReadConns[j]; + if (conn==NULL || conn==INVALID_HANDLE_VALUE) break; + + if (conn->usingHttpGateway && conn->nlhpi.szHttpGetUrl == NULL && conn->dataBuffer == NULL) + nls->hReadStatus[j] = (conn->pHttpProxyPacketQueue != NULL); + else + nls->hReadStatus[j] = FD_ISSET(conn->s,&readfd); + } + for (j=0; jhWriteConns[j]; + if (conn==NULL || conn==INVALID_HANDLE_VALUE) break; + nls->hWriteStatus[j] = FD_ISSET(conn->s,&writefd); + } + for (j=0; jhExceptConns[j]; + if (conn==NULL || conn==INVALID_HANDLE_VALUE) break; + nls->hExceptStatus[j] = FD_ISSET(conn->s,&exceptfd); + } + ReleaseMutex(hConnectionHeaderMutex); + return rc; +} diff --git a/miranda-wine/src/modules/netlib/netlibupnp.c b/miranda-wine/src/modules/netlib/netlibupnp.c new file mode 100644 index 0000000..c91de6c --- /dev/null +++ b/miranda-wine/src/modules/netlib/netlibupnp.c @@ -0,0 +1,495 @@ +/* +UPnP plugin for Miranda IM +Copyright (C) 2006 borkra + +This program is free software; you can redistribute it and/or +modify it under the terms of the GNU General Public License +as published by the Free Software Foundation; either version 2 +of the License, or (at your option) any later version. + +This program is distributed in the hope that it will be useful, +but WITHOUT ANY WARRANTY; without even the implied warranty of +MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +GNU General Public License for more details. + +You should have received a copy of the GNU General Public License +along with this program; if not, write to the Free Software +Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. +*/ + +/* Main file for the Weather Protocol, includes loading, unloading, + upgrading, support for plugin uninsaller, and anything that doesn't + belong to any other file. +*/ + +#include "commonheaders.h" +#include "netlib.h" + +static char search_request_msg[] = + "M-SEARCH * HTTP/1.1\r\n" + "MX: 2\r\n" + "HOST: 239.255.255.250:1900\r\n" + "MAN: \"ssdp:discover\"\r\n" + "ST: urn:schemas-upnp-org:service:%s\r\n" + "\r\n"; + +static char xml_get_hdr[] = + "GET %s HTTP/1.1\r\n" + "Connection: close\r\n" + "Host: %s:%s\r\n\r\n"; + +static char soap_post_hdr[] = + "POST %s HTTP/1.1\r\n" + "HOST: %s:%s\r\n" + "CONTENT-LENGTH: %u\r\n" + "CONTENT-TYPE: text/xml; charset=\"utf-8\"\r\n" + "SOAPACTION: \"%s#%s\"\r\n\r\n" + "%s"; + +static char soap_post_hdr_m[] = + "M-POST %s URL HTTP/1.1\r\n" + "HOST: %s:%s\r\n" + "CONTENT-LENGTH: %u\r\n" + "CONTENT-TYPE: text/xml; charset=\"utf-8\"\r\n" + "MAN: \"http://schemas.xmlsoap.org/soap/envelope/\"; ns=01\r\n" + "01-SOAPACTION: \"%s#%s\"\r\n\r\n" + "%s"; + +static char search_device[] = + "%s"; + +static char soap_action[] = + "\r\n" + " \r\n" + " \r\n" + "%s" + " \r\n" + " \r\n" + "\r\n"; + +static char add_port_mapping[] = + " \r\n" + " %i\r\n" + " %s\r\n" + " %i\r\n" + " %s\r\n" + " 1\r\n" + " Miranda\r\n" + " 0\r\n"; + +static char delete_port_mapping[] = + " \r\n" + " %i\r\n" + " %s\r\n"; + +static char default_http_port[] = "80"; + +static BOOL gatewayFound = FALSE; +static SOCKADDR_IN locIP; +static time_t lastDiscTime = 0; +static int expireTime = 120; + +static char szCtlUrl[256], szDev[256]; + + +static BOOL txtParseParam(char* szData, char* presearch, + char* start, char* finish, char* param, int size) +{ + char *cp, *cp1; + int len; + + *param = 0; + + if (presearch != NULL) + { + cp1 = strstr(szData, presearch); + if (cp1 == NULL) return FALSE; + } + else + cp1 = szData; + + cp = strstr(cp1, start); + if (cp == NULL) return FALSE; + cp += strlen(start); + while (*cp == ' ') ++cp; + + cp1 = strstr(cp, finish); + if (cp1 == NULL) return FALSE; + while (*(cp1-1) == ' ' && cp1 > cp) --cp1; + + len = min(cp1 - cp, size); + strncpy(param, cp, len); + param[len] = 0; + + return TRUE; +} + +static LongLog(char* szData) +{ + char* buf = szData; + int sz = strlen(szData); + + while ( sz > 1000) + { + char* nbuf = buf + 1000; + char t = *nbuf; + *nbuf = 0; + Netlib_Logf(NULL, buf); + *nbuf = t; + buf = nbuf; + sz -= 1000; + } + Netlib_Logf(NULL, buf); +} + + +static void discoverUPnP(char* szUrl, int sizeUrl) +{ + char* buf; + int buflen; + unsigned i, j, nip = 0; + char* szData = NULL; + unsigned* ips = NULL; + + static const unsigned any = INADDR_ANY; + fd_set readfd; + TIMEVAL tv = { 1, 0 }; + + char hostname[256]; + PHOSTENT he; + + SOCKET sock = socket(AF_INET, SOCK_DGRAM, IPPROTO_UDP); + + SOCKADDR_IN enetaddr; + enetaddr.sin_family = AF_INET; + enetaddr.sin_port = htons(1900); + enetaddr.sin_addr.s_addr = inet_addr("239.255.255.250"); + + FD_ZERO(&readfd); + FD_SET(sock, &readfd); + + szUrl[0] = 0; + + gethostname( hostname, sizeof( hostname )); + he = gethostbyname( hostname ); + + if (he) + { + while(he->h_addr_list[nip]) ++nip; + + ips = mir_alloc(nip * sizeof(unsigned)); + + for (j=0; jh_addr_list[j]; + } + + buf = mir_alloc(1500); + + for(i = 3; --i && szUrl[0] == 0;) + { + for (j=0; j 1) + { + szPort = _alloca(sz); + strncpy(szPort, pport+1, sz); + szPort[sz-1] = 0; + } + else + szPort = default_http_port; + + for (;;) + { + if (szActionName == NULL) + sz = mir_snprintf (szData, 4096, + xml_get_hdr, ppath, szHost, szPort); + else + { + char szData1[1024]; + + sz = mir_snprintf (szData1, sizeof(szData1), + soap_action, szActionName, szDev, szReq, szActionName); + + sz = mir_snprintf (szData, 4096, + szPostHdr, ppath, szHost, szPort, + sz, szDev, szActionName, szData1); + } + + { + static TIMEVAL tv = { 3, 0 }; + static unsigned ttl = 4; + fd_set readfd; + + SOCKET sock = socket(AF_INET, SOCK_STREAM, IPPROTO_TCP); + + SOCKADDR_IN enetaddr; + enetaddr.sin_family = AF_INET; + enetaddr.sin_port = htons((unsigned short)atol(szPort)); + enetaddr.sin_addr.s_addr = inet_addr(szHost); + + if (enetaddr.sin_addr.s_addr == INADDR_NONE) + { + PHOSTENT he = gethostbyname(szHost); + if (he) + enetaddr.sin_addr.s_addr = *(unsigned*)he->h_addr_list[0]; + } + + Netlib_Logf(NULL, "UPnP HTTP connection Host: %s Port: %s\n", szHost, szPort); + + FD_ZERO(&readfd); + FD_SET(sock, &readfd); + + setsockopt(sock, IPPROTO_IP, IP_TTL, (char *)&ttl, sizeof(unsigned)); + + if (connect(sock, (SOCKADDR*)&enetaddr, sizeof(enetaddr)) == 0) + { + if (send( sock, szData, sz, 0 ) != SOCKET_ERROR) + { + LongLog(szData); + sz = 0; + for(;;) + { + int bytesRecv; + char *hdrend; + + if (select(0, &readfd, NULL, NULL, &tv) != 1) + { + Netlib_Logf(NULL, "UPnP select timeout"); + break; + } + + bytesRecv = recv( sock, &szResult[sz], resSize-sz, 0 ); + if ( bytesRecv == 0 || bytesRecv == SOCKET_ERROR) + break; + else + sz += bytesRecv; + + if (sz >= (resSize-1)) + { + szResult[resSize-1] = 0; + break; + } + else + szResult[sz] = 0; + + hdrend = strstr(szResult, "\r\n\r\n"); + if (hdrend != NULL && + (txtParseParam(szResult, NULL, "Content-Length:", "\r", szRes, sizeof(szRes)) || + txtParseParam(szResult, NULL, "CONTENT-LENGTH:", "\r", szRes, sizeof(szRes)))) + { + int pktsz = atol(szRes) + (hdrend - szResult + 4); + if (sz >= pktsz) + { + szResult[pktsz] = 0; + break; + } + } + + } + LongLog(szResult); + } + else + Netlib_Logf(NULL, "UPnP send failed %d", WSAGetLastError()); + } + else + Netlib_Logf(NULL, "UPnP connect failed %d", WSAGetLastError()); + + if (szActionName == NULL) + { + int len = sizeof(locIP); + getsockname(sock, (SOCKADDR*)&locIP, &len); + } + + shutdown(sock, 2); + closesocket(sock); + } + txtParseParam(szResult, "HTTP", " ", " ", szRes, sizeof(szRes)); + res = atol(szRes); + if (szActionName != NULL && res == 405 && szPostHdr == soap_post_hdr) + szPostHdr = soap_post_hdr_m; + else + break; + } + + mir_free(szData); + mir_free(szReq); + return res; +} + + +static void findUPnPGateway(void) +{ + time_t curTime = time(NULL); + + if ((curTime - lastDiscTime) >= expireTime) + { + char szUrl[256]; + char* szData = mir_alloc(8192); + + lastDiscTime = curTime; + + discoverUPnP(szUrl, sizeof(szUrl)); + gatewayFound = szUrl[0] != 0 && httpTransact(szUrl, szData, 8192, NULL) == 200; + + if (gatewayFound) + { + char szTemp[256]; + size_t ctlLen; + + txtParseParam(szData, NULL, "", "", szTemp, sizeof(szTemp)); + if (szTemp[0] != 0) strcpy(szCtlUrl, szTemp); + ctlLen = strlen(szCtlUrl); + if (ctlLen > 0 && szCtlUrl[ctlLen-1] == '/') + szCtlUrl[--ctlLen] = 0; + + mir_snprintf(szTemp, sizeof(szTemp), search_device, szDev); + txtParseParam(szData, szTemp, "", "", szUrl, sizeof(szUrl)); + switch (szUrl[0]) + { + case 0: + gatewayFound = FALSE; + break; + + case '/': + strncat(szCtlUrl, szUrl, sizeof(szCtlUrl) - ctlLen); + szCtlUrl[sizeof(szCtlUrl)-1] = 0; + break; + + default: + strncpy(szCtlUrl, szUrl, sizeof(szCtlUrl)); + szCtlUrl[sizeof(szCtlUrl)-1] = 0; + break; + } + } + Netlib_Logf(NULL, "UPnP Gateway detected %d, Control URL: %s\n", gatewayFound, szCtlUrl); + mir_free(szData); + } +} + + +BOOL NetlibUPnPAddPortMapping(WORD intport, char *proto, + WORD *extport, DWORD *extip, BOOL search) +{ + int res = 0; + + findUPnPGateway(); + + if (gatewayFound) + { + char* szData = mir_alloc(4096); + char szExtIP[30]; + + *extport = intport - 1; + *extip = ntohl(locIP.sin_addr.S_un.S_addr); + + do { + ++*extport; + mir_snprintf(szData, 4096, add_port_mapping, + *extport, proto, intport, inet_ntoa(locIP.sin_addr)); + res = httpTransact(szCtlUrl, szData, 4096, "AddPortMapping"); + } while (search && res == 718); + + if (res == 200) + { + szData[0] = 0; + res = httpTransact(szCtlUrl, szData, 4096, "GetExternalIPAddress"); + if (res == 200 && txtParseParam(szData, "", "<", szExtIP, sizeof(szExtIP))) + *extip = ntohl(inet_addr(szExtIP)); + } + mir_free(szData); + } + + return res == 200; +} + + +void NetlibUPnPDeletePortMapping(WORD extport, char* proto) +{ + if (extport != 0) + { +// findUPnPGateway(); + + if (gatewayFound) + { + char* szData = mir_alloc(4096); + + mir_snprintf(szData, 4096, delete_port_mapping, + extport, proto); + httpTransact(szCtlUrl, szData, 4096, "DeletePortMapping"); + + mir_free(szData); + } + } +} + -- cgit v1.2.3