/* * 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 #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(); }