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