inetcomm: Add a framework for connecting to a server and sending/receiving data asynchronously with callbacks being called in the context of the original thread, using window messages.
This commit is contained in:
parent
08050758de
commit
994014b15a
|
@ -4,10 +4,12 @@ SRCDIR = @srcdir@
|
|||
VPATH = @srcdir@
|
||||
MODULE = inetcomm.dll
|
||||
IMPORTLIB = libinetcomm.$(IMPLIBEXT)
|
||||
IMPORTS = kernel32
|
||||
IMPORTS = ole32 ws2_32 user32 advapi32 kernel32
|
||||
EXTRALIBS = -luuid
|
||||
|
||||
C_SRCS = \
|
||||
inetcomm_main.c
|
||||
inetcomm_main.c \
|
||||
internettransport.c
|
||||
|
||||
@MAKE_DLL_RULES@
|
||||
|
||||
|
|
|
@ -0,0 +1,70 @@
|
|||
/*
|
||||
* Internet Messaging APIs
|
||||
*
|
||||
* Copyright 2006 Robert Shearman for CodeWeavers
|
||||
*
|
||||
* This library is free software; you can redistribute it and/or
|
||||
* modify it under the terms of the GNU Lesser General Public
|
||||
* License as published by the Free Software Foundation; either
|
||||
* version 2.1 of the License, or (at your option) any later version.
|
||||
*
|
||||
* This library 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
|
||||
* Lesser General Public License for more details.
|
||||
*
|
||||
* You should have received a copy of the GNU Lesser General Public
|
||||
* License along with this library; if not, write to the Free Software
|
||||
* Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301, USA
|
||||
*/
|
||||
|
||||
#include "winsock2.h"
|
||||
#include "winuser.h"
|
||||
#include "objbase.h"
|
||||
#include "imnxport.h"
|
||||
|
||||
typedef struct InternetTransport InternetTransport;
|
||||
|
||||
typedef void (*INETXPORT_COMPLETION_FUNCTION)(IInternetTransport *, char *, int);
|
||||
|
||||
struct InternetTransport
|
||||
{
|
||||
union
|
||||
{
|
||||
const IInternetTransportVtbl *vtbl;
|
||||
const ISMTPTransportVtbl *vtblSMTP;
|
||||
} u;
|
||||
|
||||
ITransportCallback *pCallback;
|
||||
IXPSTATUS Status;
|
||||
INETSERVER ServerInfo;
|
||||
SOCKET Socket;
|
||||
boolean fCommandLogging;
|
||||
boolean fInitialised;
|
||||
INETXPORT_COMPLETION_FUNCTION fnCompletion;
|
||||
char *pBuffer;
|
||||
int cbBuffer;
|
||||
int iCurrentBufferOffset;
|
||||
HWND hwnd;
|
||||
};
|
||||
|
||||
HRESULT InternetTransport_Init(InternetTransport *This);
|
||||
HRESULT InternetTransport_GetServerInfo(InternetTransport *This, LPINETSERVER pInetServer);
|
||||
HRESULT InternetTransport_InetServerFromAccount(InternetTransport *This,
|
||||
IImnAccount *pAccount, LPINETSERVER pInetServer);
|
||||
HRESULT InternetTransport_Connect(InternetTransport *This,
|
||||
LPINETSERVER pInetServer, boolean fAuthenticate, boolean fCommandLogging);
|
||||
HRESULT InternetTransport_HandsOffCallback(InternetTransport *This);
|
||||
HRESULT InternetTransport_DropConnection(InternetTransport *This);
|
||||
HRESULT InternetTransport_GetStatus(InternetTransport *This,
|
||||
IXPSTATUS *pCurrentStatus);
|
||||
HRESULT InternetTransport_ChangeStatus(InternetTransport *This, IXPSTATUS Status);
|
||||
HRESULT InternetTransport_Read(InternetTransport *This, int cbBuffer,
|
||||
INETXPORT_COMPLETION_FUNCTION fnCompletion);
|
||||
HRESULT InternetTransport_ReadLine(InternetTransport *This,
|
||||
INETXPORT_COMPLETION_FUNCTION fnCompletion);
|
||||
HRESULT InternetTransport_Write(InternetTransport *This, const char *pvData,
|
||||
int cbSize, INETXPORT_COMPLETION_FUNCTION fnCompletion);
|
||||
|
||||
BOOL InternetTransport_RegisterClass(HINSTANCE hInstance);
|
||||
void InternetTransport_UnregisterClass(HINSTANCE hInstance);
|
|
@ -0,0 +1,395 @@
|
|||
/*
|
||||
* Internet Messaging Transport Base Class
|
||||
*
|
||||
* Copyright 2006 Robert Shearman for CodeWeavers
|
||||
*
|
||||
* This library is free software; you can redistribute it and/or
|
||||
* modify it under the terms of the GNU Lesser General Public
|
||||
* License as published by the Free Software Foundation; either
|
||||
* version 2.1 of the License, or (at your option) any later version.
|
||||
*
|
||||
* This library 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
|
||||
* Lesser General Public License for more details.
|
||||
*
|
||||
* You should have received a copy of the GNU Lesser General Public
|
||||
* License along with this library; if not, write to the Free Software
|
||||
* Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301, USA
|
||||
*/
|
||||
|
||||
#define COBJMACROS
|
||||
|
||||
#include <stdarg.h>
|
||||
#include <stdio.h>
|
||||
|
||||
#include "windef.h"
|
||||
#include "winbase.h"
|
||||
#include "winnt.h"
|
||||
#include "winsock2.h"
|
||||
#include "ws2tcpip.h"
|
||||
|
||||
#include "wine/debug.h"
|
||||
|
||||
#include "inetcomm_private.h"
|
||||
|
||||
WINE_DEFAULT_DEBUG_CHANNEL(inetcomm);
|
||||
|
||||
static const WCHAR wszClassName[] = {'T','h','o','r','C','o','n','n','W','n','d','C','l','a','s','s',0};
|
||||
|
||||
#define IX_READ (WM_USER + 0)
|
||||
#define IX_READLINE (WM_USER + 1)
|
||||
#define IX_WRITE (WM_USER + 2)
|
||||
|
||||
HRESULT InternetTransport_Init(InternetTransport *This)
|
||||
{
|
||||
This->pCallback = NULL;
|
||||
This->Status = IXP_DISCONNECTED;
|
||||
This->Socket = -1;
|
||||
This->fCommandLogging = FALSE;
|
||||
This->fnCompletion = NULL;
|
||||
|
||||
return S_OK;
|
||||
}
|
||||
|
||||
HRESULT InternetTransport_GetServerInfo(InternetTransport *This, LPINETSERVER pInetServer)
|
||||
{
|
||||
if (This->Status == IXP_DISCONNECTED)
|
||||
return IXP_E_NOT_CONNECTED;
|
||||
|
||||
memcpy(pInetServer, &This->ServerInfo, sizeof(*pInetServer));
|
||||
return S_OK;
|
||||
}
|
||||
|
||||
HRESULT InternetTransport_InetServerFromAccount(InternetTransport *This,
|
||||
IImnAccount *pAccount, LPINETSERVER pInetServer)
|
||||
{
|
||||
FIXME("(%p, %p): stub\n", pAccount, pInetServer);
|
||||
return E_NOTIMPL;
|
||||
}
|
||||
|
||||
HRESULT InternetTransport_Connect(InternetTransport *This,
|
||||
LPINETSERVER pInetServer, boolean fAuthenticate, boolean fCommandLogging)
|
||||
{
|
||||
struct addrinfo *ai;
|
||||
struct addrinfo *ai_cur;
|
||||
struct addrinfo hints;
|
||||
int ret;
|
||||
char szPort[10];
|
||||
|
||||
if (This->Status != IXP_DISCONNECTED)
|
||||
return IXP_E_ALREADY_CONNECTED;
|
||||
|
||||
memcpy(&This->ServerInfo, pInetServer, sizeof(This->ServerInfo));
|
||||
This->fCommandLogging = fCommandLogging;
|
||||
|
||||
This->hwnd = CreateWindowW(wszClassName, wszClassName, 0, 0, 0, 0, 0, NULL, NULL, NULL, 0);
|
||||
if (!This->hwnd)
|
||||
return HRESULT_FROM_WIN32(GetLastError());
|
||||
SetWindowLongPtrW(This->hwnd, GWLP_USERDATA, (LONG_PTR)This);
|
||||
|
||||
hints.ai_flags = 0;
|
||||
hints.ai_family = PF_UNSPEC;
|
||||
hints.ai_socktype = SOCK_STREAM;
|
||||
hints.ai_protocol = IPPROTO_TCP;
|
||||
hints.ai_addrlen = 0;
|
||||
hints.ai_addr = NULL;
|
||||
hints.ai_canonname = NULL;
|
||||
hints.ai_next = NULL;
|
||||
|
||||
snprintf(szPort, sizeof(szPort), "%d", (unsigned short)pInetServer->dwPort);
|
||||
|
||||
InternetTransport_ChangeStatus(This, IXP_FINDINGHOST);
|
||||
|
||||
ret = getaddrinfo(pInetServer->szServerName, szPort, &hints, &ai);
|
||||
if (ret)
|
||||
{
|
||||
ERR("getaddrinfo failed: %d\n", ret);
|
||||
return IXP_E_CANT_FIND_HOST;
|
||||
}
|
||||
|
||||
for (ai_cur = ai; ai_cur; ai_cur = ai->ai_next)
|
||||
{
|
||||
if (TRACE_ON(inetcomm))
|
||||
{
|
||||
char host[256];
|
||||
char service[256];
|
||||
getnameinfo(ai_cur->ai_addr, ai_cur->ai_addrlen,
|
||||
host, sizeof(host), service, sizeof(service),
|
||||
NI_NUMERICHOST | NI_NUMERICSERV);
|
||||
TRACE("trying %s:%s\n", host, service);
|
||||
}
|
||||
|
||||
InternetTransport_ChangeStatus(This, IXP_CONNECTING);
|
||||
|
||||
This->Socket = socket(ai_cur->ai_family, ai_cur->ai_socktype, ai_cur->ai_protocol);
|
||||
if (This->Socket < 0)
|
||||
{
|
||||
WARN("socket() failed\n");
|
||||
continue;
|
||||
}
|
||||
|
||||
/* FIXME: set to async */
|
||||
|
||||
if (0 > connect(This->Socket, ai_cur->ai_addr, ai_cur->ai_addrlen))
|
||||
{
|
||||
WARN("connect() failed\n");
|
||||
closesocket(This->Socket);
|
||||
continue;
|
||||
}
|
||||
InternetTransport_ChangeStatus(This, IXP_CONNECTED);
|
||||
|
||||
/* FIXME: call WSAAsyncSelect */
|
||||
|
||||
freeaddrinfo(ai);
|
||||
TRACE("connected\n");
|
||||
return S_OK;
|
||||
}
|
||||
|
||||
freeaddrinfo(ai);
|
||||
|
||||
return IXP_E_CANT_FIND_HOST;
|
||||
}
|
||||
|
||||
HRESULT InternetTransport_HandsOffCallback(InternetTransport *This)
|
||||
{
|
||||
if (!This->pCallback)
|
||||
return S_FALSE;
|
||||
|
||||
ITransportCallback_Release(This->pCallback);
|
||||
This->pCallback = NULL;
|
||||
|
||||
return S_OK;
|
||||
}
|
||||
|
||||
HRESULT InternetTransport_DropConnection(InternetTransport *This)
|
||||
{
|
||||
int ret;
|
||||
|
||||
if (This->Status == IXP_DISCONNECTED)
|
||||
return IXP_E_NOT_CONNECTED;
|
||||
|
||||
ret = shutdown(This->Socket, SD_BOTH);
|
||||
|
||||
This->Status = IXP_DISCONNECTED;
|
||||
|
||||
ret = closesocket(This->Socket);
|
||||
|
||||
DestroyWindow(This->hwnd);
|
||||
This->hwnd = NULL;
|
||||
|
||||
return S_OK;
|
||||
}
|
||||
|
||||
HRESULT InternetTransport_GetStatus(InternetTransport *This,
|
||||
IXPSTATUS *pCurrentStatus)
|
||||
{
|
||||
*pCurrentStatus = This->Status;
|
||||
return S_OK;
|
||||
}
|
||||
|
||||
HRESULT InternetTransport_ChangeStatus(InternetTransport *This, IXPSTATUS Status)
|
||||
{
|
||||
This->Status = Status;
|
||||
if (This->pCallback)
|
||||
ITransportCallback_OnStatus(This->pCallback, Status,
|
||||
(IInternetTransport *)&This->u.vtbl);
|
||||
return S_OK;
|
||||
}
|
||||
|
||||
HRESULT InternetTransport_Read(InternetTransport *This, int cbBuffer,
|
||||
INETXPORT_COMPLETION_FUNCTION fnCompletion)
|
||||
{
|
||||
if (This->Status == IXP_DISCONNECTED)
|
||||
return IXP_E_NOT_CONNECTED;
|
||||
|
||||
if (This->fnCompletion)
|
||||
return IXP_E_BUSY;
|
||||
|
||||
This->fnCompletion = fnCompletion;
|
||||
|
||||
This->cbBuffer = cbBuffer;
|
||||
This->pBuffer = HeapAlloc(GetProcessHeap(), 0, This->cbBuffer);
|
||||
This->iCurrentBufferOffset = 0;
|
||||
|
||||
if (WSAAsyncSelect(This->Socket, This->hwnd, IX_READ, FD_READ) == SOCKET_ERROR)
|
||||
{
|
||||
ERR("WSAAsyncSelect failed with error %d\n", WSAGetLastError());
|
||||
/* FIXME: handle error */
|
||||
}
|
||||
return S_OK;
|
||||
}
|
||||
|
||||
HRESULT InternetTransport_ReadLine(InternetTransport *This,
|
||||
INETXPORT_COMPLETION_FUNCTION fnCompletion)
|
||||
{
|
||||
if (This->Status == IXP_DISCONNECTED)
|
||||
return IXP_E_NOT_CONNECTED;
|
||||
|
||||
if (This->fnCompletion)
|
||||
return IXP_E_BUSY;
|
||||
|
||||
This->fnCompletion = fnCompletion;
|
||||
|
||||
This->cbBuffer = 1024;
|
||||
This->pBuffer = HeapAlloc(GetProcessHeap(), 0, This->cbBuffer);
|
||||
This->iCurrentBufferOffset = 0;
|
||||
|
||||
if (WSAAsyncSelect(This->Socket, This->hwnd, IX_READLINE, FD_READ) == SOCKET_ERROR)
|
||||
{
|
||||
ERR("WSAAsyncSelect failed with error %d\n", WSAGetLastError());
|
||||
/* FIXME: handle error */
|
||||
}
|
||||
return S_OK;
|
||||
}
|
||||
|
||||
HRESULT InternetTransport_Write(InternetTransport *This, const char *pvData,
|
||||
int cbSize, INETXPORT_COMPLETION_FUNCTION fnCompletion)
|
||||
{
|
||||
int ret;
|
||||
|
||||
if (This->Status == IXP_DISCONNECTED)
|
||||
return IXP_E_NOT_CONNECTED;
|
||||
|
||||
if (This->fnCompletion)
|
||||
return IXP_E_BUSY;
|
||||
|
||||
/* FIXME: do this asynchronously */
|
||||
ret = send(This->Socket, pvData, cbSize, 0);
|
||||
if (ret == SOCKET_ERROR)
|
||||
{
|
||||
ERR("send failed with error %d\n", WSAGetLastError());
|
||||
/* FIXME: handle error */
|
||||
}
|
||||
|
||||
fnCompletion((IInternetTransport *)&This->u.vtbl, NULL, 0);
|
||||
|
||||
return S_OK;
|
||||
}
|
||||
|
||||
static LRESULT CALLBACK InternetTransport_WndProc(HWND hwnd, UINT uMsg, WPARAM wParam, LPARAM lParam)
|
||||
{
|
||||
if (uMsg == IX_READ)
|
||||
{
|
||||
InternetTransport *This = (InternetTransport *)GetWindowLongPtrW(hwnd, GWLP_USERDATA);
|
||||
|
||||
/* no work to do */
|
||||
if (!This->fnCompletion)
|
||||
return 0;
|
||||
|
||||
while (This->iCurrentBufferOffset < This->cbBuffer)
|
||||
{
|
||||
if (recv(This->Socket, &This->pBuffer[This->iCurrentBufferOffset], 1, 0) <= 0)
|
||||
{
|
||||
if (WSAGetLastError() == WSAEWOULDBLOCK)
|
||||
break;
|
||||
|
||||
ERR("recv failed with error %d\n", WSAGetLastError());
|
||||
/* FIXME: handle error */
|
||||
}
|
||||
|
||||
This->iCurrentBufferOffset++;
|
||||
}
|
||||
if (This->iCurrentBufferOffset == This->cbBuffer)
|
||||
{
|
||||
INETXPORT_COMPLETION_FUNCTION fnCompletion = This->fnCompletion;
|
||||
char *pBuffer;
|
||||
|
||||
This->fnCompletion = NULL;
|
||||
pBuffer = This->pBuffer;
|
||||
This->pBuffer = NULL;
|
||||
fnCompletion((IInternetTransport *)&This->u.vtbl, pBuffer,
|
||||
This->iCurrentBufferOffset);
|
||||
HeapFree(GetProcessHeap(), 0, pBuffer);
|
||||
return 0;
|
||||
}
|
||||
|
||||
if (WSAAsyncSelect(This->Socket, hwnd, uMsg, FD_READ) == SOCKET_ERROR)
|
||||
{
|
||||
ERR("WSAAsyncSelect failed with error %d\n", WSAGetLastError());
|
||||
/* FIXME: handle error */
|
||||
}
|
||||
return 0;
|
||||
}
|
||||
else if (uMsg == IX_READLINE)
|
||||
{
|
||||
InternetTransport *This = (InternetTransport *)GetWindowLongPtrW(hwnd, GWLP_USERDATA);
|
||||
|
||||
/* no work to do */
|
||||
if (!This->fnCompletion)
|
||||
return 0;
|
||||
|
||||
while (This->iCurrentBufferOffset < This->cbBuffer - 1)
|
||||
{
|
||||
struct timeval tv;
|
||||
fd_set infd;
|
||||
|
||||
if (recv(This->Socket, &This->pBuffer[This->iCurrentBufferOffset], 1, 0) <= 0)
|
||||
{
|
||||
if (WSAGetLastError() == WSAEWOULDBLOCK)
|
||||
break;
|
||||
|
||||
ERR("recv failed with error %d\n", WSAGetLastError());
|
||||
/* FIXME: handle error */
|
||||
return 0;
|
||||
}
|
||||
|
||||
if (This->pBuffer[This->iCurrentBufferOffset] == '\n')
|
||||
{
|
||||
INETXPORT_COMPLETION_FUNCTION fnCompletion = This->fnCompletion;
|
||||
char *pBuffer;
|
||||
|
||||
This->fnCompletion = NULL;
|
||||
This->pBuffer[This->iCurrentBufferOffset++] = '\0';
|
||||
pBuffer = This->pBuffer;
|
||||
This->pBuffer = NULL;
|
||||
|
||||
fnCompletion((IInternetTransport *)&This->u.vtbl, pBuffer,
|
||||
This->iCurrentBufferOffset);
|
||||
|
||||
HeapFree(GetProcessHeap(), 0, pBuffer);
|
||||
return 0;
|
||||
}
|
||||
if (This->pBuffer[This->iCurrentBufferOffset] != '\r')
|
||||
This->iCurrentBufferOffset++;
|
||||
|
||||
FD_ZERO(&infd);
|
||||
FD_SET(This->Socket, &infd);
|
||||
tv.tv_sec = 0;
|
||||
tv.tv_usec = 0;
|
||||
}
|
||||
if (This->iCurrentBufferOffset == This->cbBuffer - 1)
|
||||
return 0;
|
||||
|
||||
if (WSAAsyncSelect(This->Socket, hwnd, uMsg, FD_READ) == SOCKET_ERROR)
|
||||
{
|
||||
ERR("WSAAsyncSelect failed with error %d\n", WSAGetLastError());
|
||||
/* FIXME: handle error */
|
||||
}
|
||||
return 0;
|
||||
}
|
||||
else
|
||||
return DefWindowProcW(hwnd, uMsg, wParam, lParam);
|
||||
}
|
||||
|
||||
BOOL InternetTransport_RegisterClass(HINSTANCE hInstance)
|
||||
{
|
||||
WNDCLASSW cls;
|
||||
WSADATA wsadata;
|
||||
|
||||
if (WSAStartup(MAKEWORD(2, 2), &wsadata))
|
||||
return FALSE;
|
||||
|
||||
memset(&cls, 0, sizeof(cls));
|
||||
cls.hInstance = hInstance;
|
||||
cls.lpfnWndProc = InternetTransport_WndProc;
|
||||
cls.lpszClassName = wszClassName;
|
||||
|
||||
return RegisterClassW(&cls);
|
||||
}
|
||||
|
||||
void InternetTransport_UnregisterClass(HINSTANCE hInstance)
|
||||
{
|
||||
UnregisterClassW(wszClassName, hInstance);
|
||||
WSACleanup();
|
||||
}
|
Loading…
Reference in New Issue