|  | /* | 
|  | * 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 "ws2tcpip.h" | 
|  | #include "windef.h" | 
|  | #include "winnt.h" | 
|  | #include "objbase.h" | 
|  | #include "ole2.h" | 
|  | #include "mimeole.h" | 
|  |  | 
|  | #include <stdio.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; | 
|  |  | 
|  | *pInetServer = This->ServerInfo; | 
|  | 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; | 
|  |  | 
|  | This->ServerInfo = *pInetServer; | 
|  | 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) | 
|  | { | 
|  | int so; | 
|  |  | 
|  | 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); | 
|  |  | 
|  | so = socket(ai_cur->ai_family, ai_cur->ai_socktype, ai_cur->ai_protocol); | 
|  | if (so == -1) | 
|  | { | 
|  | WARN("socket() failed\n"); | 
|  | continue; | 
|  | } | 
|  | This->Socket = so; | 
|  |  | 
|  | /* 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) | 
|  | { | 
|  | if (This->Status == IXP_DISCONNECTED) | 
|  | return IXP_E_NOT_CONNECTED; | 
|  |  | 
|  | shutdown(This->Socket, SD_BOTH); | 
|  |  | 
|  | closesocket(This->Socket); | 
|  |  | 
|  | DestroyWindow(This->hwnd); | 
|  | This->hwnd = NULL; | 
|  |  | 
|  | InternetTransport_ChangeStatus(This, IXP_DISCONNECTED); | 
|  |  | 
|  | 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_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; | 
|  | } | 
|  |  | 
|  | HRESULT InternetTransport_DoCommand(InternetTransport *This, | 
|  | LPCSTR pszCommand, INETXPORT_COMPLETION_FUNCTION fnCompletion) | 
|  | { | 
|  | if (This->Status == IXP_DISCONNECTED) | 
|  | return IXP_E_NOT_CONNECTED; | 
|  |  | 
|  | if (This->fnCompletion) | 
|  | return IXP_E_BUSY; | 
|  |  | 
|  | if (This->pCallback && This->fCommandLogging) | 
|  | { | 
|  | ITransportCallback_OnCommand(This->pCallback, CMD_SEND, (LPSTR)pszCommand, 0, | 
|  | (IInternetTransport *)&This->u.vtbl); | 
|  | } | 
|  | return InternetTransport_Write(This, pszCommand, strlen(pszCommand), fnCompletion); | 
|  | } | 
|  |  | 
|  | 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) | 
|  | { | 
|  | 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); | 
|  | } | 
|  | 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(); | 
|  | } |