1 /* 2 * Internet Messaging Transport Base Class 3 * 4 * Copyright 2006 Robert Shearman for CodeWeavers 5 * 6 * This library is free software; you can redistribute it and/or 7 * modify it under the terms of the GNU Lesser General Public 8 * License as published by the Free Software Foundation; either 9 * version 2.1 of the License, or (at your option) any later version. 10 * 11 * This library is distributed in the hope that it will be useful, 12 * but WITHOUT ANY WARRANTY; without even the implied warranty of 13 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU 14 * Lesser General Public License for more details. 15 * 16 * You should have received a copy of the GNU Lesser General Public 17 * License along with this library; if not, write to the Free Software 18 * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301, USA 19 */ 20 21 #define COBJMACROS 22 23 #include "ws2tcpip.h" 24 #include "windef.h" 25 #include "winnt.h" 26 #include "objbase.h" 27 #include "ole2.h" 28 #include "mimeole.h" 29 30 #include <stdio.h> 31 32 #include "wine/debug.h" 33 34 #include "inetcomm_private.h" 35 36 WINE_DEFAULT_DEBUG_CHANNEL(inetcomm); 37 38 static const WCHAR wszClassName[] = {'T','h','o','r','C','o','n','n','W','n','d','C','l','a','s','s',0}; 39 40 #define IX_READ (WM_USER + 0) 41 #define IX_READLINE (WM_USER + 1) 42 #define IX_WRITE (WM_USER + 2) 43 44 HRESULT InternetTransport_Init(InternetTransport *This) 45 { 46 This->pCallback = NULL; 47 This->Status = IXP_DISCONNECTED; 48 This->Socket = -1; 49 This->fCommandLogging = FALSE; 50 This->fnCompletion = NULL; 51 52 return S_OK; 53 } 54 55 HRESULT InternetTransport_GetServerInfo(InternetTransport *This, LPINETSERVER pInetServer) 56 { 57 if (This->Status == IXP_DISCONNECTED) 58 return IXP_E_NOT_CONNECTED; 59 60 *pInetServer = This->ServerInfo; 61 return S_OK; 62 } 63 64 HRESULT InternetTransport_InetServerFromAccount(InternetTransport *This, 65 IImnAccount *pAccount, LPINETSERVER pInetServer) 66 { 67 FIXME("(%p, %p): stub\n", pAccount, pInetServer); 68 return E_NOTIMPL; 69 } 70 71 HRESULT InternetTransport_Connect(InternetTransport *This, 72 LPINETSERVER pInetServer, boolean fAuthenticate, boolean fCommandLogging) 73 { 74 struct addrinfo *ai; 75 struct addrinfo *ai_cur; 76 struct addrinfo hints; 77 int ret; 78 char szPort[10]; 79 80 if (This->Status != IXP_DISCONNECTED) 81 return IXP_E_ALREADY_CONNECTED; 82 83 This->ServerInfo = *pInetServer; 84 This->fCommandLogging = fCommandLogging; 85 86 This->hwnd = CreateWindowW(wszClassName, wszClassName, 0, 0, 0, 0, 0, NULL, NULL, NULL, 0); 87 if (!This->hwnd) 88 return HRESULT_FROM_WIN32(GetLastError()); 89 SetWindowLongPtrW(This->hwnd, GWLP_USERDATA, (LONG_PTR)This); 90 91 hints.ai_flags = 0; 92 hints.ai_family = PF_UNSPEC; 93 hints.ai_socktype = SOCK_STREAM; 94 hints.ai_protocol = IPPROTO_TCP; 95 hints.ai_addrlen = 0; 96 hints.ai_addr = NULL; 97 hints.ai_canonname = NULL; 98 hints.ai_next = NULL; 99 100 snprintf(szPort, sizeof(szPort), "%d", (unsigned short)pInetServer->dwPort); 101 102 InternetTransport_ChangeStatus(This, IXP_FINDINGHOST); 103 104 ret = getaddrinfo(pInetServer->szServerName, szPort, &hints, &ai); 105 if (ret) 106 { 107 ERR("getaddrinfo failed: %d\n", ret); 108 return IXP_E_CANT_FIND_HOST; 109 } 110 111 for (ai_cur = ai; ai_cur; ai_cur = ai->ai_next) 112 { 113 int so; 114 115 if (TRACE_ON(inetcomm)) 116 { 117 char host[256]; 118 char service[256]; 119 getnameinfo(ai_cur->ai_addr, ai_cur->ai_addrlen, 120 host, sizeof(host), service, sizeof(service), 121 NI_NUMERICHOST | NI_NUMERICSERV); 122 TRACE("trying %s:%s\n", host, service); 123 } 124 125 InternetTransport_ChangeStatus(This, IXP_CONNECTING); 126 127 so = socket(ai_cur->ai_family, ai_cur->ai_socktype, ai_cur->ai_protocol); 128 if (so == -1) 129 { 130 WARN("socket() failed\n"); 131 continue; 132 } 133 This->Socket = so; 134 135 /* FIXME: set to async */ 136 137 if (0 > connect(This->Socket, ai_cur->ai_addr, ai_cur->ai_addrlen)) 138 { 139 WARN("connect() failed\n"); 140 closesocket(This->Socket); 141 continue; 142 } 143 InternetTransport_ChangeStatus(This, IXP_CONNECTED); 144 145 /* FIXME: call WSAAsyncSelect */ 146 147 freeaddrinfo(ai); 148 TRACE("connected\n"); 149 return S_OK; 150 } 151 152 freeaddrinfo(ai); 153 154 return IXP_E_CANT_FIND_HOST; 155 } 156 157 HRESULT InternetTransport_HandsOffCallback(InternetTransport *This) 158 { 159 if (!This->pCallback) 160 return S_FALSE; 161 162 ITransportCallback_Release(This->pCallback); 163 This->pCallback = NULL; 164 165 return S_OK; 166 } 167 168 HRESULT InternetTransport_DropConnection(InternetTransport *This) 169 { 170 if (This->Status == IXP_DISCONNECTED) 171 return IXP_E_NOT_CONNECTED; 172 173 shutdown(This->Socket, SD_BOTH); 174 175 closesocket(This->Socket); 176 177 DestroyWindow(This->hwnd); 178 This->hwnd = NULL; 179 180 InternetTransport_ChangeStatus(This, IXP_DISCONNECTED); 181 182 return S_OK; 183 } 184 185 HRESULT InternetTransport_GetStatus(InternetTransport *This, 186 IXPSTATUS *pCurrentStatus) 187 { 188 *pCurrentStatus = This->Status; 189 return S_OK; 190 } 191 192 HRESULT InternetTransport_ChangeStatus(InternetTransport *This, IXPSTATUS Status) 193 { 194 This->Status = Status; 195 if (This->pCallback) 196 ITransportCallback_OnStatus(This->pCallback, Status, 197 (IInternetTransport *)&This->u.vtbl); 198 return S_OK; 199 } 200 201 HRESULT InternetTransport_ReadLine(InternetTransport *This, 202 INETXPORT_COMPLETION_FUNCTION fnCompletion) 203 { 204 if (This->Status == IXP_DISCONNECTED) 205 return IXP_E_NOT_CONNECTED; 206 207 if (This->fnCompletion) 208 return IXP_E_BUSY; 209 210 This->fnCompletion = fnCompletion; 211 212 This->cbBuffer = 1024; 213 This->pBuffer = HeapAlloc(GetProcessHeap(), 0, This->cbBuffer); 214 This->iCurrentBufferOffset = 0; 215 216 if (WSAAsyncSelect(This->Socket, This->hwnd, IX_READLINE, FD_READ) == SOCKET_ERROR) 217 { 218 ERR("WSAAsyncSelect failed with error %d\n", WSAGetLastError()); 219 /* FIXME: handle error */ 220 } 221 return S_OK; 222 } 223 224 HRESULT InternetTransport_Write(InternetTransport *This, const char *pvData, 225 int cbSize, INETXPORT_COMPLETION_FUNCTION fnCompletion) 226 { 227 int ret; 228 229 if (This->Status == IXP_DISCONNECTED) 230 return IXP_E_NOT_CONNECTED; 231 232 if (This->fnCompletion) 233 return IXP_E_BUSY; 234 235 /* FIXME: do this asynchronously */ 236 ret = send(This->Socket, pvData, cbSize, 0); 237 if (ret == SOCKET_ERROR) 238 { 239 ERR("send failed with error %d\n", WSAGetLastError()); 240 /* FIXME: handle error */ 241 } 242 243 fnCompletion((IInternetTransport *)&This->u.vtbl, NULL, 0); 244 245 return S_OK; 246 } 247 248 HRESULT InternetTransport_DoCommand(InternetTransport *This, 249 LPCSTR pszCommand, INETXPORT_COMPLETION_FUNCTION fnCompletion) 250 { 251 if (This->Status == IXP_DISCONNECTED) 252 return IXP_E_NOT_CONNECTED; 253 254 if (This->fnCompletion) 255 return IXP_E_BUSY; 256 257 if (This->pCallback && This->fCommandLogging) 258 { 259 ITransportCallback_OnCommand(This->pCallback, CMD_SEND, (LPSTR)pszCommand, 0, 260 (IInternetTransport *)&This->u.vtbl); 261 } 262 return InternetTransport_Write(This, pszCommand, strlen(pszCommand), fnCompletion); 263 } 264 265 static LRESULT CALLBACK InternetTransport_WndProc(HWND hwnd, UINT uMsg, WPARAM wParam, LPARAM lParam) 266 { 267 if (uMsg == IX_READ) 268 { 269 InternetTransport *This = (InternetTransport *)GetWindowLongPtrW(hwnd, GWLP_USERDATA); 270 271 /* no work to do */ 272 if (!This->fnCompletion) 273 return 0; 274 275 while (This->iCurrentBufferOffset < This->cbBuffer) 276 { 277 if (recv(This->Socket, &This->pBuffer[This->iCurrentBufferOffset], 1, 0) <= 0) 278 { 279 if (WSAGetLastError() == WSAEWOULDBLOCK) 280 break; 281 282 ERR("recv failed with error %d\n", WSAGetLastError()); 283 /* FIXME: handle error */ 284 } 285 286 This->iCurrentBufferOffset++; 287 } 288 if (This->iCurrentBufferOffset == This->cbBuffer) 289 { 290 INETXPORT_COMPLETION_FUNCTION fnCompletion = This->fnCompletion; 291 char *pBuffer; 292 293 This->fnCompletion = NULL; 294 pBuffer = This->pBuffer; 295 This->pBuffer = NULL; 296 fnCompletion((IInternetTransport *)&This->u.vtbl, pBuffer, 297 This->iCurrentBufferOffset); 298 HeapFree(GetProcessHeap(), 0, pBuffer); 299 return 0; 300 } 301 302 if (WSAAsyncSelect(This->Socket, hwnd, uMsg, FD_READ) == SOCKET_ERROR) 303 { 304 ERR("WSAAsyncSelect failed with error %d\n", WSAGetLastError()); 305 /* FIXME: handle error */ 306 } 307 return 0; 308 } 309 else if (uMsg == IX_READLINE) 310 { 311 InternetTransport *This = (InternetTransport *)GetWindowLongPtrW(hwnd, GWLP_USERDATA); 312 313 /* no work to do */ 314 if (!This->fnCompletion) 315 return 0; 316 317 while (This->iCurrentBufferOffset < This->cbBuffer - 1) 318 { 319 fd_set infd; 320 321 if (recv(This->Socket, &This->pBuffer[This->iCurrentBufferOffset], 1, 0) <= 0) 322 { 323 if (WSAGetLastError() == WSAEWOULDBLOCK) 324 break; 325 326 ERR("recv failed with error %d\n", WSAGetLastError()); 327 /* FIXME: handle error */ 328 return 0; 329 } 330 331 if (This->pBuffer[This->iCurrentBufferOffset] == '\n') 332 { 333 INETXPORT_COMPLETION_FUNCTION fnCompletion = This->fnCompletion; 334 char *pBuffer; 335 336 This->fnCompletion = NULL; 337 This->pBuffer[This->iCurrentBufferOffset++] = '\0'; 338 pBuffer = This->pBuffer; 339 This->pBuffer = NULL; 340 341 fnCompletion((IInternetTransport *)&This->u.vtbl, pBuffer, 342 This->iCurrentBufferOffset); 343 344 HeapFree(GetProcessHeap(), 0, pBuffer); 345 return 0; 346 } 347 if (This->pBuffer[This->iCurrentBufferOffset] != '\r') 348 This->iCurrentBufferOffset++; 349 350 FD_ZERO(&infd); 351 FD_SET(This->Socket, &infd); 352 } 353 if (This->iCurrentBufferOffset == This->cbBuffer - 1) 354 return 0; 355 356 if (WSAAsyncSelect(This->Socket, hwnd, uMsg, FD_READ) == SOCKET_ERROR) 357 { 358 ERR("WSAAsyncSelect failed with error %d\n", WSAGetLastError()); 359 /* FIXME: handle error */ 360 } 361 return 0; 362 } 363 else 364 return DefWindowProcW(hwnd, uMsg, wParam, lParam); 365 } 366 367 BOOL InternetTransport_RegisterClass(HINSTANCE hInstance) 368 { 369 WNDCLASSW cls; 370 WSADATA wsadata; 371 372 if (WSAStartup(MAKEWORD(2, 2), &wsadata)) 373 return FALSE; 374 375 memset(&cls, 0, sizeof(cls)); 376 cls.hInstance = hInstance; 377 cls.lpfnWndProc = InternetTransport_WndProc; 378 cls.lpszClassName = wszClassName; 379 380 return RegisterClassW(&cls); 381 } 382 383 void InternetTransport_UnregisterClass(HINSTANCE hInstance) 384 { 385 UnregisterClassW(wszClassName, hInstance); 386 WSACleanup(); 387 } 388