mshtml: Fixed handling channels without container and necko channel.
[wine] / dlls / 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 #define COBJMACROS
22
23 #include <stdarg.h>
24 #include <stdio.h>
25
26 #include "windef.h"
27 #include "winbase.h"
28 #include "winnt.h"
29 #include "winsock2.h"
30 #include "ws2tcpip.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     memcpy(pInetServer, &This->ServerInfo, sizeof(*pInetServer));
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     memcpy(&This->ServerInfo, pInetServer, sizeof(This->ServerInfo));
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         if (TRACE_ON(inetcomm))
114         {
115             char host[256];
116             char service[256];
117             getnameinfo(ai_cur->ai_addr, ai_cur->ai_addrlen,
118                 host, sizeof(host), service, sizeof(service),
119                 NI_NUMERICHOST | NI_NUMERICSERV);
120             TRACE("trying %s:%s\n", host, service);
121         }
122
123         InternetTransport_ChangeStatus(This, IXP_CONNECTING);
124
125         This->Socket = socket(ai_cur->ai_family, ai_cur->ai_socktype, ai_cur->ai_protocol);
126         if (This->Socket < 0)
127         {
128             WARN("socket() failed\n");
129             continue;
130         }
131
132         /* FIXME: set to async */
133
134         if (0 > connect(This->Socket, ai_cur->ai_addr, ai_cur->ai_addrlen))
135         {
136             WARN("connect() failed\n");
137             closesocket(This->Socket);
138             continue;
139         }
140         InternetTransport_ChangeStatus(This, IXP_CONNECTED);
141
142         /* FIXME: call WSAAsyncSelect */
143
144         freeaddrinfo(ai);
145         TRACE("connected\n");
146         return S_OK;
147     }
148
149     freeaddrinfo(ai);
150
151     return IXP_E_CANT_FIND_HOST;
152 }
153
154 HRESULT InternetTransport_HandsOffCallback(InternetTransport *This)
155 {
156     if (!This->pCallback)
157         return S_FALSE;
158
159     ITransportCallback_Release(This->pCallback);
160     This->pCallback = NULL;
161
162     return S_OK;
163 }
164
165 HRESULT InternetTransport_DropConnection(InternetTransport *This)
166 {
167     int ret;
168
169     if (This->Status == IXP_DISCONNECTED)
170         return IXP_E_NOT_CONNECTED;
171
172     ret = shutdown(This->Socket, SD_BOTH);
173
174     ret = closesocket(This->Socket);
175
176     DestroyWindow(This->hwnd);
177     This->hwnd = NULL;
178
179     InternetTransport_ChangeStatus(This, IXP_DISCONNECTED);
180
181     return S_OK;
182 }
183
184 HRESULT InternetTransport_GetStatus(InternetTransport *This,
185     IXPSTATUS *pCurrentStatus)
186 {
187     *pCurrentStatus = This->Status;
188     return S_OK;
189 }
190
191 HRESULT InternetTransport_ChangeStatus(InternetTransport *This, IXPSTATUS Status)
192 {
193     This->Status = Status;
194     if (This->pCallback)
195         ITransportCallback_OnStatus(This->pCallback, Status,
196             (IInternetTransport *)&This->u.vtbl);
197     return S_OK;
198 }
199
200 HRESULT InternetTransport_Read(InternetTransport *This, int cbBuffer,
201     INETXPORT_COMPLETION_FUNCTION fnCompletion)
202 {
203     if (This->Status == IXP_DISCONNECTED)
204         return IXP_E_NOT_CONNECTED;
205
206     if (This->fnCompletion)
207         return IXP_E_BUSY;
208
209     This->fnCompletion = fnCompletion;
210
211     This->cbBuffer = cbBuffer;
212     This->pBuffer = HeapAlloc(GetProcessHeap(), 0, This->cbBuffer);
213     This->iCurrentBufferOffset = 0;
214
215     if (WSAAsyncSelect(This->Socket, This->hwnd, IX_READ, FD_READ) == SOCKET_ERROR)
216     {
217         ERR("WSAAsyncSelect failed with error %d\n", WSAGetLastError());
218         /* FIXME: handle error */
219     }
220     return S_OK;
221 }
222
223 HRESULT InternetTransport_ReadLine(InternetTransport *This,
224     INETXPORT_COMPLETION_FUNCTION fnCompletion)
225 {
226     if (This->Status == IXP_DISCONNECTED)
227         return IXP_E_NOT_CONNECTED;
228
229     if (This->fnCompletion)
230         return IXP_E_BUSY;
231
232     This->fnCompletion = fnCompletion;
233
234     This->cbBuffer = 1024;
235     This->pBuffer = HeapAlloc(GetProcessHeap(), 0, This->cbBuffer);
236     This->iCurrentBufferOffset = 0;
237
238     if (WSAAsyncSelect(This->Socket, This->hwnd, IX_READLINE, FD_READ) == SOCKET_ERROR)
239     {
240         ERR("WSAAsyncSelect failed with error %d\n", WSAGetLastError());
241         /* FIXME: handle error */
242     }
243     return S_OK;
244 }
245
246 HRESULT InternetTransport_Write(InternetTransport *This, const char *pvData,
247     int cbSize, INETXPORT_COMPLETION_FUNCTION fnCompletion)
248 {
249     int ret;
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     /* FIXME: do this asynchronously */
258     ret = send(This->Socket, pvData, cbSize, 0);
259     if (ret == SOCKET_ERROR)
260     {
261         ERR("send failed with error %d\n", WSAGetLastError());
262         /* FIXME: handle error */
263     }
264
265     fnCompletion((IInternetTransport *)&This->u.vtbl, NULL, 0);
266
267     return S_OK;
268 }
269
270 static LRESULT CALLBACK InternetTransport_WndProc(HWND hwnd, UINT uMsg, WPARAM wParam, LPARAM lParam)
271 {
272     if (uMsg == IX_READ)
273     {
274         InternetTransport *This = (InternetTransport *)GetWindowLongPtrW(hwnd, GWLP_USERDATA);
275
276         /* no work to do */
277         if (!This->fnCompletion)
278             return 0;
279
280         while (This->iCurrentBufferOffset < This->cbBuffer)
281         {
282             if (recv(This->Socket, &This->pBuffer[This->iCurrentBufferOffset], 1, 0) <= 0)
283             {
284                 if (WSAGetLastError() == WSAEWOULDBLOCK)
285                     break;
286
287                 ERR("recv failed with error %d\n", WSAGetLastError());
288                 /* FIXME: handle error */
289             }
290
291             This->iCurrentBufferOffset++;
292         }
293         if (This->iCurrentBufferOffset == This->cbBuffer)
294         {
295             INETXPORT_COMPLETION_FUNCTION fnCompletion = This->fnCompletion;
296             char *pBuffer;
297
298             This->fnCompletion = NULL;
299             pBuffer = This->pBuffer;
300             This->pBuffer = NULL;
301             fnCompletion((IInternetTransport *)&This->u.vtbl, pBuffer,
302                 This->iCurrentBufferOffset);
303             HeapFree(GetProcessHeap(), 0, pBuffer);
304             return 0;
305         }
306
307         if (WSAAsyncSelect(This->Socket, hwnd, uMsg, FD_READ) == SOCKET_ERROR)
308         {
309             ERR("WSAAsyncSelect failed with error %d\n", WSAGetLastError());
310             /* FIXME: handle error */
311         }
312         return 0;
313     }
314     else if (uMsg == IX_READLINE)
315     {
316         InternetTransport *This = (InternetTransport *)GetWindowLongPtrW(hwnd, GWLP_USERDATA);
317
318         /* no work to do */
319         if (!This->fnCompletion)
320             return 0;
321
322         while (This->iCurrentBufferOffset < This->cbBuffer - 1)
323         {
324             struct timeval tv;
325             fd_set infd;
326
327             if (recv(This->Socket, &This->pBuffer[This->iCurrentBufferOffset], 1, 0) <= 0)
328             {
329                 if (WSAGetLastError() == WSAEWOULDBLOCK)
330                     break;
331
332                 ERR("recv failed with error %d\n", WSAGetLastError());
333                 /* FIXME: handle error */
334                 return 0;
335             }
336
337             if (This->pBuffer[This->iCurrentBufferOffset] == '\n')
338             {
339                 INETXPORT_COMPLETION_FUNCTION fnCompletion = This->fnCompletion;
340                 char *pBuffer;
341
342                 This->fnCompletion = NULL;
343                 This->pBuffer[This->iCurrentBufferOffset++] = '\0';
344                 pBuffer = This->pBuffer;
345                 This->pBuffer = NULL;
346
347                 fnCompletion((IInternetTransport *)&This->u.vtbl, pBuffer,
348                     This->iCurrentBufferOffset);
349
350                 HeapFree(GetProcessHeap(), 0, pBuffer);
351                 return 0;
352             }
353             if (This->pBuffer[This->iCurrentBufferOffset] != '\r')
354                 This->iCurrentBufferOffset++;
355
356             FD_ZERO(&infd);
357             FD_SET(This->Socket, &infd);
358             tv.tv_sec = 0;
359             tv.tv_usec = 0;
360         }
361         if (This->iCurrentBufferOffset == This->cbBuffer - 1)
362             return 0;
363
364         if (WSAAsyncSelect(This->Socket, hwnd, uMsg, FD_READ) == SOCKET_ERROR)
365         {
366             ERR("WSAAsyncSelect failed with error %d\n", WSAGetLastError());
367             /* FIXME: handle error */
368         }
369         return 0;
370     }
371     else
372         return DefWindowProcW(hwnd, uMsg, wParam, lParam);
373 }
374
375 BOOL InternetTransport_RegisterClass(HINSTANCE hInstance)
376 {
377     WNDCLASSW cls;
378     WSADATA wsadata;
379
380     if (WSAStartup(MAKEWORD(2, 2), &wsadata))
381         return FALSE;
382
383     memset(&cls, 0, sizeof(cls));
384     cls.hInstance     = hInstance;
385     cls.lpfnWndProc   = InternetTransport_WndProc;
386     cls.lpszClassName = wszClassName;
387
388     return RegisterClassW(&cls);
389 }
390
391 void InternetTransport_UnregisterClass(HINSTANCE hInstance)
392 {
393     UnregisterClassW(wszClassName, hInstance);
394     WSACleanup();
395 }