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 int ret;
171
172 if (This->Status == IXP_DISCONNECTED)
173 return IXP_E_NOT_CONNECTED;
174
175 ret = shutdown(This->Socket, SD_BOTH);
176
177 ret = closesocket(This->Socket);
178
179 DestroyWindow(This->hwnd);
180 This->hwnd = NULL;
181
182 InternetTransport_ChangeStatus(This, IXP_DISCONNECTED);
183
184 return S_OK;
185 }
186
187 HRESULT InternetTransport_GetStatus(InternetTransport *This,
188 IXPSTATUS *pCurrentStatus)
189 {
190 *pCurrentStatus = This->Status;
191 return S_OK;
192 }
193
194 HRESULT InternetTransport_ChangeStatus(InternetTransport *This, IXPSTATUS Status)
195 {
196 This->Status = Status;
197 if (This->pCallback)
198 ITransportCallback_OnStatus(This->pCallback, Status,
199 (IInternetTransport *)&This->u.vtbl);
200 return S_OK;
201 }
202
203 HRESULT InternetTransport_ReadLine(InternetTransport *This,
204 INETXPORT_COMPLETION_FUNCTION fnCompletion)
205 {
206 if (This->Status == IXP_DISCONNECTED)
207 return IXP_E_NOT_CONNECTED;
208
209 if (This->fnCompletion)
210 return IXP_E_BUSY;
211
212 This->fnCompletion = fnCompletion;
213
214 This->cbBuffer = 1024;
215 This->pBuffer = HeapAlloc(GetProcessHeap(), 0, This->cbBuffer);
216 This->iCurrentBufferOffset = 0;
217
218 if (WSAAsyncSelect(This->Socket, This->hwnd, IX_READLINE, FD_READ) == SOCKET_ERROR)
219 {
220 ERR("WSAAsyncSelect failed with error %d\n", WSAGetLastError());
221 /* FIXME: handle error */
222 }
223 return S_OK;
224 }
225
226 HRESULT InternetTransport_Write(InternetTransport *This, const char *pvData,
227 int cbSize, INETXPORT_COMPLETION_FUNCTION fnCompletion)
228 {
229 int ret;
230
231 if (This->Status == IXP_DISCONNECTED)
232 return IXP_E_NOT_CONNECTED;
233
234 if (This->fnCompletion)
235 return IXP_E_BUSY;
236
237 /* FIXME: do this asynchronously */
238 ret = send(This->Socket, pvData, cbSize, 0);
239 if (ret == SOCKET_ERROR)
240 {
241 ERR("send failed with error %d\n", WSAGetLastError());
242 /* FIXME: handle error */
243 }
244
245 fnCompletion((IInternetTransport *)&This->u.vtbl, NULL, 0);
246
247 return S_OK;
248 }
249
250 HRESULT InternetTransport_DoCommand(InternetTransport *This,
251 LPCSTR pszCommand, INETXPORT_COMPLETION_FUNCTION fnCompletion)
252 {
253 if (This->Status == IXP_DISCONNECTED)
254 return IXP_E_NOT_CONNECTED;
255
256 if (This->fnCompletion)
257 return IXP_E_BUSY;
258
259 if (This->pCallback && This->fCommandLogging)
260 {
261 ITransportCallback_OnCommand(This->pCallback, CMD_SEND, (LPSTR)pszCommand, 0,
262 (IInternetTransport *)&This->u.vtbl);
263 }
264 return InternetTransport_Write(This, pszCommand, strlen(pszCommand), fnCompletion);
265 }
266
267 static LRESULT CALLBACK InternetTransport_WndProc(HWND hwnd, UINT uMsg, WPARAM wParam, LPARAM lParam)
268 {
269 if (uMsg == IX_READ)
270 {
271 InternetTransport *This = (InternetTransport *)GetWindowLongPtrW(hwnd, GWLP_USERDATA);
272
273 /* no work to do */
274 if (!This->fnCompletion)
275 return 0;
276
277 while (This->iCurrentBufferOffset < This->cbBuffer)
278 {
279 if (recv(This->Socket, &This->pBuffer[This->iCurrentBufferOffset], 1, 0) <= 0)
280 {
281 if (WSAGetLastError() == WSAEWOULDBLOCK)
282 break;
283
284 ERR("recv failed with error %d\n", WSAGetLastError());
285 /* FIXME: handle error */
286 }
287
288 This->iCurrentBufferOffset++;
289 }
290 if (This->iCurrentBufferOffset == This->cbBuffer)
291 {
292 INETXPORT_COMPLETION_FUNCTION fnCompletion = This->fnCompletion;
293 char *pBuffer;
294
295 This->fnCompletion = NULL;
296 pBuffer = This->pBuffer;
297 This->pBuffer = NULL;
298 fnCompletion((IInternetTransport *)&This->u.vtbl, pBuffer,
299 This->iCurrentBufferOffset);
300 HeapFree(GetProcessHeap(), 0, pBuffer);
301 return 0;
302 }
303
304 if (WSAAsyncSelect(This->Socket, hwnd, uMsg, FD_READ) == SOCKET_ERROR)
305 {
306 ERR("WSAAsyncSelect failed with error %d\n", WSAGetLastError());
307 /* FIXME: handle error */
308 }
309 return 0;
310 }
311 else if (uMsg == IX_READLINE)
312 {
313 InternetTransport *This = (InternetTransport *)GetWindowLongPtrW(hwnd, GWLP_USERDATA);
314
315 /* no work to do */
316 if (!This->fnCompletion)
317 return 0;
318
319 while (This->iCurrentBufferOffset < This->cbBuffer - 1)
320 {
321 fd_set infd;
322
323 if (recv(This->Socket, &This->pBuffer[This->iCurrentBufferOffset], 1, 0) <= 0)
324 {
325 if (WSAGetLastError() == WSAEWOULDBLOCK)
326 break;
327
328 ERR("recv failed with error %d\n", WSAGetLastError());
329 /* FIXME: handle error */
330 return 0;
331 }
332
333 if (This->pBuffer[This->iCurrentBufferOffset] == '\n')
334 {
335 INETXPORT_COMPLETION_FUNCTION fnCompletion = This->fnCompletion;
336 char *pBuffer;
337
338 This->fnCompletion = NULL;
339 This->pBuffer[This->iCurrentBufferOffset++] = '\0';
340 pBuffer = This->pBuffer;
341 This->pBuffer = NULL;
342
343 fnCompletion((IInternetTransport *)&This->u.vtbl, pBuffer,
344 This->iCurrentBufferOffset);
345
346 HeapFree(GetProcessHeap(), 0, pBuffer);
347 return 0;
348 }
349 if (This->pBuffer[This->iCurrentBufferOffset] != '\r')
350 This->iCurrentBufferOffset++;
351
352 FD_ZERO(&infd);
353 FD_SET(This->Socket, &infd);
354 }
355 if (This->iCurrentBufferOffset == This->cbBuffer - 1)
356 return 0;
357
358 if (WSAAsyncSelect(This->Socket, hwnd, uMsg, FD_READ) == SOCKET_ERROR)
359 {
360 ERR("WSAAsyncSelect failed with error %d\n", WSAGetLastError());
361 /* FIXME: handle error */
362 }
363 return 0;
364 }
365 else
366 return DefWindowProcW(hwnd, uMsg, wParam, lParam);
367 }
368
369 BOOL InternetTransport_RegisterClass(HINSTANCE hInstance)
370 {
371 WNDCLASSW cls;
372 WSADATA wsadata;
373
374 if (WSAStartup(MAKEWORD(2, 2), &wsadata))
375 return FALSE;
376
377 memset(&cls, 0, sizeof(cls));
378 cls.hInstance = hInstance;
379 cls.lpfnWndProc = InternetTransport_WndProc;
380 cls.lpszClassName = wszClassName;
381
382 return RegisterClassW(&cls);
383 }
384
385 void InternetTransport_UnregisterClass(HINSTANCE hInstance)
386 {
387 UnregisterClassW(wszClassName, hInstance);
388 WSACleanup();
389 }
390
This page was automatically generated by the
LXR engine.
Visit the LXR main site for more
information.