server: Move socket async activation to sock_poll_event.
[wine] / dlls / inetcomm / smtptransport.c
1 /*
2  * SMTP Transport
3  *
4  * Copyright 2006 Robert Shearman for CodeWeavers
5  * Copyright 2008 Hans Leidekker for CodeWeavers
6  *
7  * This library is free software; you can redistribute it and/or
8  * modify it under the terms of the GNU Lesser General Public
9  * License as published by the Free Software Foundation; either
10  * version 2.1 of the License, or (at your option) any later version.
11  *
12  * This library is distributed in the hope that it will be useful,
13  * but WITHOUT ANY WARRANTY; without even the implied warranty of
14  * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
15  * Lesser General Public License for more details.
16  *
17  * You should have received a copy of the GNU Lesser General Public
18  * License along with this library; if not, write to the Free Software
19  * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301, USA
20  *
21  */
22
23 #define COBJMACROS
24
25 #include <stdarg.h>
26 #include <stdio.h>
27
28 #include "windef.h"
29 #include "winbase.h"
30 #include "winnt.h"
31 #include "winuser.h"
32 #include "objbase.h"
33 #include "mimeole.h"
34 #include "wine/debug.h"
35
36 #include "inetcomm_private.h"
37
38 WINE_DEFAULT_DEBUG_CHANNEL(inetcomm);
39
40 typedef struct
41 {
42     InternetTransport InetTransport;
43     ULONG refs;
44     BOOL fESMTP;
45     SMTPMESSAGE pending_message;
46     INETADDR *addrlist;
47     ULONG ulCurrentAddressIndex;
48 } SMTPTransport;
49
50 static HRESULT SMTPTransport_ParseResponse(SMTPTransport *This, char *pszResponse, SMTPRESPONSE *pResponse)
51 {
52     HRESULT hrServerError;
53
54     TRACE("response: %s\n", debugstr_a(pszResponse));
55
56     if (!isdigit(*pszResponse))
57         return IXP_E_SMTP_RESPONSE_ERROR;
58     pResponse->pTransport = (ISMTPTransport *)&This->InetTransport.u.vtblSMTP2;
59     pResponse->rIxpResult.pszResponse = pszResponse;
60     pResponse->rIxpResult.dwSocketError = 0;
61     pResponse->rIxpResult.uiServerError = strtol(pszResponse, &pszResponse, 10);
62     if (*pszResponse == '-')
63     {
64         pResponse->fDone = FALSE;
65         pszResponse++;
66     }
67     else
68         pResponse->fDone = TRUE;
69
70     switch (pResponse->rIxpResult.uiServerError)
71     {
72     case 211: hrServerError = IXP_E_SMTP_211_SYSTEM_STATUS; break;
73     case 214: hrServerError = IXP_E_SMTP_214_HELP_MESSAGE; break;
74     case 220: hrServerError = IXP_E_SMTP_220_READY; break;
75     case 221: hrServerError = IXP_E_SMTP_221_CLOSING; break;
76     case 245: hrServerError = IXP_E_SMTP_245_AUTH_SUCCESS; break;
77     case 250: hrServerError = IXP_E_SMTP_250_MAIL_ACTION_OKAY; break;
78     case 251: hrServerError = IXP_E_SMTP_251_FORWARDING_MAIL; break;
79     case 334: hrServerError = IXP_E_SMTP_334_AUTH_READY_RESPONSE; break;
80     case 354: hrServerError = IXP_E_SMTP_354_START_MAIL_INPUT; break;
81     case 421: hrServerError = IXP_E_SMTP_421_NOT_AVAILABLE; break;
82     case 450: hrServerError = IXP_E_SMTP_450_MAILBOX_BUSY; break;
83     case 451: hrServerError = IXP_E_SMTP_451_ERROR_PROCESSING; break;
84     case 452: hrServerError = IXP_E_SMTP_452_NO_SYSTEM_STORAGE; break;
85     case 454: hrServerError = IXP_E_SMTP_454_STARTTLS_FAILED; break;
86     case 500: hrServerError = IXP_E_SMTP_500_SYNTAX_ERROR; break;
87     case 501: hrServerError = IXP_E_SMTP_501_PARAM_SYNTAX; break;
88     case 502: hrServerError = IXP_E_SMTP_502_COMMAND_NOTIMPL; break;
89     case 503: hrServerError = IXP_E_SMTP_503_COMMAND_SEQ; break;
90     case 504: hrServerError = IXP_E_SMTP_504_COMMAND_PARAM_NOTIMPL; break;
91     case 530: hrServerError = IXP_E_SMTP_530_STARTTLS_REQUIRED; break;
92     case 550: hrServerError = IXP_E_SMTP_550_MAILBOX_NOT_FOUND; break;
93     case 551: hrServerError = IXP_E_SMTP_551_USER_NOT_LOCAL; break;
94     case 552: hrServerError = IXP_E_SMTP_552_STORAGE_OVERFLOW; break;
95     case 553: hrServerError = IXP_E_SMTP_553_MAILBOX_NAME_SYNTAX; break;
96     case 554: hrServerError = IXP_E_SMTP_554_TRANSACT_FAILED; break;
97     default:
98         hrServerError = IXP_E_SMTP_RESPONSE_ERROR;
99         break;
100     }
101     pResponse->rIxpResult.hrResult = hrServerError;
102     pResponse->rIxpResult.hrServerError = hrServerError;
103
104     if (This->InetTransport.pCallback && This->InetTransport.fCommandLogging)
105     {
106         ITransportCallback_OnCommand(This->InetTransport.pCallback, CMD_RESP,
107             pResponse->rIxpResult.pszResponse, hrServerError,
108             (IInternetTransport *)&This->InetTransport.u.vtbl);
109     }
110     return S_OK;
111 }
112
113 static void SMTPTransport_CallbackDoNothing(IInternetTransport *iface, char *pBuffer, int cbBuffer)
114 {
115     TRACE("\n");
116 }
117
118 static void SMTPTransport_CallbackReadResponseDoNothing(IInternetTransport *iface, char *pBuffer, int cbBuffer)
119 {
120     SMTPTransport *This = (SMTPTransport *)iface;
121
122     TRACE("\n");
123     InternetTransport_ReadLine(&This->InetTransport, SMTPTransport_CallbackDoNothing);
124 }
125
126 static void SMTPTransport_CallbackProcessDATAResponse(IInternetTransport *iface, char *pBuffer, int cbBuffer)
127 {
128     SMTPTransport *This = (SMTPTransport *)iface;
129     SMTPRESPONSE response = { 0 };
130     HRESULT hr;
131
132     TRACE("\n");
133
134     hr = SMTPTransport_ParseResponse(This, pBuffer, &response);
135     if (FAILED(hr))
136     {
137         /* FIXME: handle error */
138         return;
139     }
140
141     response.command = SMTP_DATA;
142     ISMTPCallback_OnResponse((ISMTPCallback *)This->InetTransport.pCallback, &response);
143
144     if (FAILED(response.rIxpResult.hrServerError))
145     {
146         ERR("server error: %s\n", debugstr_a(pBuffer));
147         /* FIXME: handle error */
148         return;
149     }
150 }
151
152 static void SMTPTransport_CallbackReadDATAResponse(IInternetTransport *iface, char *pBuffer, int cbBuffer)
153 {
154     SMTPTransport *This = (SMTPTransport *)iface;
155
156     TRACE("\n");
157     InternetTransport_ReadLine(&This->InetTransport, SMTPTransport_CallbackProcessDATAResponse);
158 }
159
160 static void SMTPTransport_CallbackProcessMAILResponse(IInternetTransport *iface, char *pBuffer, int cbBuffer)
161 {
162     SMTPTransport *This = (SMTPTransport *)iface;
163     SMTPRESPONSE response = { 0 };
164     HRESULT hr;
165
166     TRACE("\n");
167
168     hr = SMTPTransport_ParseResponse(This, pBuffer, &response);
169     if (FAILED(hr))
170     {
171         /* FIXME: handle error */
172         return;
173     }
174
175     response.command = SMTP_MAIL;
176     ISMTPCallback_OnResponse((ISMTPCallback *)This->InetTransport.pCallback, &response);
177
178     if (FAILED(response.rIxpResult.hrServerError))
179     {
180         ERR("server error: %s\n", debugstr_a(pBuffer));
181         /* FIXME: handle error */
182         return;
183     }
184 }
185
186 static void SMTPTransport_CallbackReadMAILResponse(IInternetTransport *iface, char *pBuffer, int cbBuffer)
187 {
188     SMTPTransport *This = (SMTPTransport *)iface;
189
190     TRACE("\n");
191     InternetTransport_ReadLine(&This->InetTransport, SMTPTransport_CallbackProcessMAILResponse);
192 }
193
194 static void SMTPTransport_CallbackProcessRCPTResponse(IInternetTransport *iface, char *pBuffer, int cbBuffer)
195 {
196     SMTPTransport *This = (SMTPTransport *)iface;
197     SMTPRESPONSE response = { 0 };
198     HRESULT hr;
199
200     TRACE("\n");
201
202     HeapFree(GetProcessHeap(), 0, This->addrlist);
203     This->addrlist = NULL;
204
205     hr = SMTPTransport_ParseResponse(This, pBuffer, &response);
206     if (FAILED(hr))
207     {
208         /* FIXME: handle error */
209         return;
210     }
211
212     response.command = SMTP_RCPT;
213     ISMTPCallback_OnResponse((ISMTPCallback *)This->InetTransport.pCallback, &response);
214
215     if (FAILED(response.rIxpResult.hrServerError))
216     {
217         ERR("server error: %s\n", debugstr_a(pBuffer));
218         /* FIXME: handle error */
219         return;
220     }
221 }
222
223 static void SMTPTransport_CallbackReadRCPTResponse(IInternetTransport *iface, char *pBuffer, int cbBuffer)
224 {
225     SMTPTransport *This = (SMTPTransport *)iface;
226
227     TRACE("\n");
228     InternetTransport_ReadLine(&This->InetTransport, SMTPTransport_CallbackProcessRCPTResponse);
229 }
230
231 static void SMTPTransport_CallbackProcessHelloResp(IInternetTransport *iface, char *pBuffer, int cbBuffer)
232 {
233     SMTPTransport *This = (SMTPTransport *)iface;
234     SMTPRESPONSE response = { 0 };
235     HRESULT hr;
236
237     TRACE("\n");
238
239     hr = SMTPTransport_ParseResponse(This, pBuffer, &response);
240     if (FAILED(hr))
241     {
242         /* FIXME: handle error */
243         return;
244     }
245
246     response.command = This->fESMTP ? SMTP_EHLO : SMTP_HELO;
247     ISMTPCallback_OnResponse((ISMTPCallback *)This->InetTransport.pCallback, &response);
248
249     if (FAILED(response.rIxpResult.hrServerError))
250     {
251         ERR("server error: %s\n", debugstr_a(pBuffer));
252         /* FIXME: handle error */
253         return;
254     }
255
256     if (!response.fDone)
257     {
258         InternetTransport_ReadLine(&This->InetTransport,
259             SMTPTransport_CallbackProcessHelloResp);
260         return;
261     }
262
263     /* FIXME: try to authorize */
264
265     /* always changed to this status, even if authorization not support on server */
266     InternetTransport_ChangeStatus(&This->InetTransport, IXP_AUTHORIZED);
267     InternetTransport_ChangeStatus(&This->InetTransport, IXP_CONNECTED);
268
269     memset(&response, 0, sizeof(response));
270     response.command = SMTP_CONNECTED;
271     response.fDone = TRUE;
272     ISMTPCallback_OnResponse((ISMTPCallback *)This->InetTransport.pCallback, &response);
273 }
274
275 static void SMTPTransport_CallbackRecvHelloResp(IInternetTransport *iface, char *pBuffer, int cbBuffer)
276 {
277     SMTPTransport *This = (SMTPTransport *)iface;
278
279     TRACE("\n");
280     InternetTransport_ReadLine(&This->InetTransport, SMTPTransport_CallbackProcessHelloResp);
281 }
282
283 static void SMTPTransport_CallbackSendHello(IInternetTransport *iface, char *pBuffer, int cbBuffer)
284 {
285     SMTPTransport *This = (SMTPTransport *)iface;
286     SMTPRESPONSE response = { 0 };
287     HRESULT hr;
288     const char *pszHello;
289     char *pszCommand;
290     const char szHostName[] = "localhost"; /* FIXME */
291
292     TRACE("\n");
293
294     hr = SMTPTransport_ParseResponse(This, pBuffer, &response);
295     if (FAILED(hr))
296     {
297         /* FIXME: handle error */
298         return;
299     }
300
301     response.command = SMTP_BANNER;
302     ISMTPCallback_OnResponse((ISMTPCallback *)This->InetTransport.pCallback, &response);
303
304     if (FAILED(response.rIxpResult.hrServerError))
305     {
306         ERR("server error: %s\n", debugstr_a(pBuffer));
307         /* FIXME: handle error */
308         return;
309     }
310
311     TRACE("(%s)\n", pBuffer);
312
313     This->fESMTP = strstr(response.rIxpResult.pszResponse, "ESMTP") &&
314         This->InetTransport.ServerInfo.dwFlags & (ISF_SSLONSAMEPORT|ISF_QUERYDSNSUPPORT|ISF_QUERYAUTHSUPPORT);
315
316     if (This->fESMTP)
317         pszHello = "EHLO ";
318     else
319         pszHello = "HELO ";
320
321     pszCommand = HeapAlloc(GetProcessHeap(), 0, strlen(pszHello) + strlen(szHostName) + 2);
322     strcpy(pszCommand, pszHello);
323     strcat(pszCommand, szHostName);
324     pszCommand[strlen(pszCommand)+1] = '\0';
325     pszCommand[strlen(pszCommand)] = '\n';
326
327     InternetTransport_DoCommand(&This->InetTransport, pszCommand,
328         SMTPTransport_CallbackRecvHelloResp);
329
330     HeapFree(GetProcessHeap(), 0, pszCommand);
331 }
332
333 static void SMTPTransport_CallbackDisconnect(IInternetTransport *iface, char *pBuffer, int cbBuffer)
334 {
335     SMTPTransport *This = (SMTPTransport *)iface;
336     SMTPRESPONSE response;
337     HRESULT hr;
338
339     TRACE("\n");
340
341     if (pBuffer)
342     {
343         hr = SMTPTransport_ParseResponse(This, pBuffer, &response);
344         if (FAILED(hr))
345         {
346             /* FIXME: handle error */
347             return;
348         }
349
350         if (FAILED(response.rIxpResult.hrServerError))
351         {
352             ERR("server error: %s\n", debugstr_a(pBuffer));
353             /* FIXME: handle error */
354             return;
355         }
356     }
357     InternetTransport_DropConnection(&This->InetTransport);
358 }
359
360 static void SMTPTransport_CallbackMessageProcessResponse(IInternetTransport *iface, char *pBuffer, int cbBuffer)
361 {
362     SMTPTransport *This = (SMTPTransport *)iface;
363     SMTPRESPONSE response = { 0 };
364     HRESULT hr;
365
366     TRACE("\n");
367
368     hr = SMTPTransport_ParseResponse(This, pBuffer, &response);
369     if (FAILED(hr))
370     {
371         /* FIXME: handle error */
372         return;
373     }
374
375     if (FAILED(response.rIxpResult.hrServerError))
376     {
377         ERR("server error: %s\n", debugstr_a(pBuffer));
378         /* FIXME: handle error */
379         return;
380     }
381
382     response.command = SMTP_SEND_MESSAGE;
383     response.rIxpResult.hrResult = S_OK;
384     ISMTPCallback_OnResponse((ISMTPCallback *)This->InetTransport.pCallback, &response);
385 }
386
387 static void SMTPTransport_CallbackMessageReadResponse(IInternetTransport *iface, char *pBuffer, int cbBuffer)
388 {
389     SMTPTransport *This = (SMTPTransport *)iface;
390     InternetTransport_ReadLine(&This->InetTransport, SMTPTransport_CallbackMessageProcessResponse);
391 }
392
393 static void SMTPTransport_CallbackMessageSendDOT(IInternetTransport *iface, char *pBuffer, int cbBuffer)
394 {
395     SMTPTransport *This = (SMTPTransport *)iface;
396
397     IStream_Release(This->pending_message.pstmMsg);
398     InternetTransport_DoCommand(&This->InetTransport, "\n.\n",
399         SMTPTransport_CallbackMessageReadResponse);
400 }
401
402 static void SMTPTransport_CallbackMessageSendDataStream(IInternetTransport *iface, char *pBuffer, int cbBuffer)
403 {
404     SMTPTransport *This = (SMTPTransport *)iface;
405     SMTPRESPONSE response;
406     HRESULT hr;
407     char *pszBuffer;
408     ULONG cbSize;
409
410     TRACE("\n");
411
412     hr = SMTPTransport_ParseResponse(This, pBuffer, &response);
413     if (FAILED(hr))
414     {
415         /* FIXME: handle error */
416         return;
417     }
418
419     if (FAILED(response.rIxpResult.hrServerError))
420     {
421         ERR("server error: %s\n", debugstr_a(pBuffer));
422         /* FIXME: handle error */
423         return;
424     }
425
426     pszBuffer = HeapAlloc(GetProcessHeap(), 0, This->pending_message.cbSize);
427     hr = IStream_Read(This->pending_message.pstmMsg, pszBuffer, This->pending_message.cbSize, NULL);
428     if (FAILED(hr))
429     {
430         /* FIXME: handle error */
431         return;
432     }
433     cbSize = This->pending_message.cbSize;
434
435     /* FIXME: map "\n.\n" to "\n..\n", reallocate memory, update cbSize */
436
437     /* FIXME: properly stream the message rather than writing it all at once */
438
439     hr = InternetTransport_Write(&This->InetTransport, pszBuffer, cbSize,
440         SMTPTransport_CallbackMessageSendDOT);
441
442     HeapFree(GetProcessHeap(), 0, pszBuffer);
443 }
444
445 static void SMTPTransport_CallbackMessageReadDataResponse(IInternetTransport *iface, char *pBuffer, int cbBuffer)
446 {
447     SMTPTransport *This = (SMTPTransport *)iface;
448
449     TRACE("\n");
450     InternetTransport_ReadLine(&This->InetTransport, SMTPTransport_CallbackMessageSendDataStream);
451 }
452
453 static void SMTPTransport_CallbackMessageSendTo(IInternetTransport *iface, char *pBuffer, int cbBuffer);
454
455 static void SMTPTransport_CallbackMessageReadToResponse(IInternetTransport *iface, char *pBuffer, int cbBuffer)
456 {
457     SMTPTransport *This = (SMTPTransport *)iface;
458
459     TRACE("\n");
460     InternetTransport_ReadLine(&This->InetTransport, SMTPTransport_CallbackMessageSendTo);
461 }
462
463 static void SMTPTransport_CallbackMessageSendTo(IInternetTransport *iface, char *pBuffer, int cbBuffer)
464 {
465     SMTPTransport *This = (SMTPTransport *)iface;
466     SMTPRESPONSE response;
467     HRESULT hr;
468
469     TRACE("\n");
470
471     hr = SMTPTransport_ParseResponse(This, pBuffer, &response);
472     if (FAILED(hr))
473     {
474         /* FIXME: handle error */
475         return;
476     }
477
478     if (FAILED(response.rIxpResult.hrServerError))
479     {
480         ERR("server error: %s\n", debugstr_a(pBuffer));
481         /* FIXME: handle error */
482         return;
483     }
484
485     for (; This->ulCurrentAddressIndex < This->pending_message.rAddressList.cAddress; This->ulCurrentAddressIndex++)
486     {
487         TRACE("address[%d]: %s\n", This->ulCurrentAddressIndex,
488             This->pending_message.rAddressList.prgAddress[This->ulCurrentAddressIndex].szEmail);
489
490         if ((This->pending_message.rAddressList.prgAddress[This->ulCurrentAddressIndex].addrtype & ADDR_TOFROM_MASK) == ADDR_TO)
491         {
492             const char szCommandFormat[] = "RCPT TO: <%s>\n";
493             char *szCommand;
494             int len = sizeof(szCommandFormat) - 2 /* "%s" */ +
495                 strlen(This->pending_message.rAddressList.prgAddress[This->ulCurrentAddressIndex].szEmail);
496
497             szCommand = HeapAlloc(GetProcessHeap(), 0, len);
498             if (!szCommand)
499                 return;
500
501             sprintf(szCommand, szCommandFormat,
502                 This->pending_message.rAddressList.prgAddress[This->ulCurrentAddressIndex].szEmail);
503
504             This->ulCurrentAddressIndex++;
505             hr = InternetTransport_DoCommand(&This->InetTransport, szCommand,
506                 SMTPTransport_CallbackMessageReadToResponse);
507
508             HeapFree(GetProcessHeap(), 0, szCommand);
509             return;
510         }
511     }
512
513     hr = InternetTransport_DoCommand(&This->InetTransport, "DATA\n",
514         SMTPTransport_CallbackMessageReadDataResponse);
515 }
516
517 static void SMTPTransport_CallbackMessageReadFromResponse(IInternetTransport *iface, char *pBuffer, int cbBuffer)
518 {
519     SMTPTransport *This = (SMTPTransport *)iface;
520
521     TRACE("\n");
522     InternetTransport_ReadLine(&This->InetTransport, SMTPTransport_CallbackMessageSendTo);
523 }
524
525 static HRESULT WINAPI SMTPTransport_QueryInterface(ISMTPTransport2 *iface, REFIID riid, void **ppv)
526 {
527     TRACE("(%s, %p)\n", debugstr_guid(riid), ppv);
528
529     if (IsEqualIID(riid, &IID_IUnknown) ||
530         IsEqualIID(riid, &IID_IInternetTransport) ||
531         IsEqualIID(riid, &IID_ISMTPTransport) ||
532         IsEqualIID(riid, &IID_ISMTPTransport2))
533     {
534         *ppv = iface;
535         IUnknown_AddRef(iface);
536         return S_OK;
537     }
538     *ppv = NULL;
539     FIXME("no interface for %s\n", debugstr_guid(riid));
540     return E_NOINTERFACE;
541 }
542
543 static ULONG WINAPI SMTPTransport_AddRef(ISMTPTransport2 *iface)
544 {
545     SMTPTransport *This = (SMTPTransport *)iface;
546     return InterlockedIncrement((LONG *)&This->refs);
547 }
548
549 static ULONG WINAPI SMTPTransport_Release(ISMTPTransport2 *iface)
550 {
551     SMTPTransport *This = (SMTPTransport *)iface;
552     ULONG refs = InterlockedDecrement((LONG *)&This->refs);
553     if (!refs)
554     {
555         TRACE("destroying %p\n", This);
556         if (This->InetTransport.Status != IXP_DISCONNECTED)
557             InternetTransport_DropConnection(&This->InetTransport);
558
559         if (This->InetTransport.pCallback) ITransportCallback_Release(This->InetTransport.pCallback);
560         HeapFree(GetProcessHeap(), 0, This->addrlist);
561         HeapFree(GetProcessHeap(), 0, This);
562     }
563     return refs;
564 }
565
566 static HRESULT WINAPI SMTPTransport_GetServerInfo(ISMTPTransport2 *iface,
567     LPINETSERVER pInetServer)
568 {
569     SMTPTransport *This = (SMTPTransport *)iface;
570
571     TRACE("(%p)\n", pInetServer);
572     return InternetTransport_GetServerInfo(&This->InetTransport, pInetServer);
573 }
574
575 static IXPTYPE WINAPI SMTPTransport_GetIXPType(ISMTPTransport2 *iface)
576 {
577     TRACE("()\n");
578     return IXP_SMTP;
579 }
580
581 static HRESULT WINAPI SMTPTransport_IsState(ISMTPTransport2 *iface,
582     IXPISSTATE isstate)
583 {
584     FIXME("(%d): stub\n", isstate);
585     return E_NOTIMPL;
586 }
587
588 static HRESULT WINAPI SMTPTransport_InetServerFromAccount(
589     ISMTPTransport2 *iface, IImnAccount *pAccount, LPINETSERVER pInetServer)
590 {
591     SMTPTransport *This = (SMTPTransport *)iface;
592
593     TRACE("(%p, %p)\n", pAccount, pInetServer);
594     return InternetTransport_InetServerFromAccount(&This->InetTransport, pAccount, pInetServer);
595 }
596
597 static HRESULT WINAPI SMTPTransport_Connect(ISMTPTransport2 *iface,
598     LPINETSERVER pInetServer, boolean fAuthenticate, boolean fCommandLogging)
599 {
600     SMTPTransport *This = (SMTPTransport *)iface;
601     HRESULT hr;
602
603     TRACE("(%p, %s, %s)\n", pInetServer, fAuthenticate ? "TRUE" : "FALSE", fCommandLogging ? "TRUE" : "FALSE");
604
605     hr = InternetTransport_Connect(&This->InetTransport, pInetServer, fAuthenticate, fCommandLogging);
606     if (FAILED(hr))
607         return hr;
608
609     /* this starts the state machine, which continues in SMTPTransport_CallbackSendHELO */
610     return InternetTransport_ReadLine(&This->InetTransport, SMTPTransport_CallbackSendHello);
611 }
612
613 static HRESULT WINAPI SMTPTransport_HandsOffCallback(ISMTPTransport2 *iface)
614 {
615     SMTPTransport *This = (SMTPTransport *)iface;
616
617     TRACE("()\n");
618     return InternetTransport_HandsOffCallback(&This->InetTransport);
619 }
620
621 static HRESULT WINAPI SMTPTransport_Disconnect(ISMTPTransport2 *iface)
622 {
623     TRACE("()\n");
624     return ISMTPTransport2_CommandQUIT(iface);
625 }
626
627 static HRESULT WINAPI SMTPTransport_DropConnection(ISMTPTransport2 *iface)
628 {
629     SMTPTransport *This = (SMTPTransport *)iface;
630
631     TRACE("()\n");
632     return InternetTransport_DropConnection(&This->InetTransport);
633 }
634
635 static HRESULT WINAPI SMTPTransport_GetStatus(ISMTPTransport2 *iface,
636     IXPSTATUS *pCurrentStatus)
637 {
638     SMTPTransport *This = (SMTPTransport *)iface;
639
640     TRACE("()\n");
641     return InternetTransport_GetStatus(&This->InetTransport, pCurrentStatus);
642 }
643
644 static HRESULT WINAPI SMTPTransport_InitNew(ISMTPTransport2 *iface,
645     LPSTR pszLogFilePath, ISMTPCallback *pCallback)
646 {
647     SMTPTransport *This = (SMTPTransport *)iface;
648
649     TRACE("(%s, %p)\n", debugstr_a(pszLogFilePath), pCallback);
650
651     if (!pCallback)
652         return E_INVALIDARG;
653
654     if (pszLogFilePath)
655         FIXME("not using log file of %s, use Wine debug logging instead\n",
656             debugstr_a(pszLogFilePath));
657
658     ISMTPCallback_AddRef(pCallback);
659     This->InetTransport.pCallback = (ITransportCallback *)pCallback;
660     This->InetTransport.fInitialised = TRUE;
661
662     return S_OK;
663 }
664
665 static HRESULT WINAPI SMTPTransport_SendMessage(ISMTPTransport2 *iface,
666     LPSMTPMESSAGE pMessage)
667 {
668     SMTPTransport *This = (SMTPTransport *)iface;
669     ULONG i, size;
670     LPSTR pszFromAddress = NULL;
671     const char szCommandFormat[] = "MAIL FROM: <%s>\n";
672     char *szCommand;
673     int len;
674     HRESULT hr;
675
676     TRACE("(%p)\n", pMessage);
677
678     This->pending_message = *pMessage;
679     IStream_AddRef(pMessage->pstmMsg);
680
681     size = pMessage->rAddressList.cAddress * sizeof(INETADDR);
682     This->addrlist = HeapAlloc(GetProcessHeap(), 0, size);
683     if (!This->addrlist)
684         return E_OUTOFMEMORY;
685
686     memcpy(This->addrlist, pMessage->rAddressList.prgAddress, size);
687     This->pending_message.rAddressList.prgAddress = This->addrlist;
688     This->ulCurrentAddressIndex = 0;
689
690     for (i = 0; i < pMessage->rAddressList.cAddress; i++)
691     {
692         if ((pMessage->rAddressList.prgAddress[i].addrtype & ADDR_TOFROM_MASK) == ADDR_FROM)
693         {
694             TRACE("address[%d]: ADDR_FROM, %s\n", i,
695                 pMessage->rAddressList.prgAddress[i].szEmail);
696             pszFromAddress = pMessage->rAddressList.prgAddress[i].szEmail;
697         }
698         else if ((pMessage->rAddressList.prgAddress[i].addrtype & ADDR_TOFROM_MASK) == ADDR_TO)
699         {
700             TRACE("address[%d]: ADDR_TO, %s\n", i,
701                 pMessage->rAddressList.prgAddress[i].szEmail);
702         }
703     }
704
705     if (!pszFromAddress)
706     {
707         SMTPRESPONSE response;
708         memset(&response, 0, sizeof(response));
709         response.command = SMTP_SEND_MESSAGE;
710         response.fDone = TRUE;
711         response.pTransport = (ISMTPTransport *)&This->InetTransport.u.vtblSMTP2;
712         response.rIxpResult.hrResult = IXP_E_SMTP_NO_SENDER;
713         ISMTPCallback_OnResponse((ISMTPCallback *)This->InetTransport.pCallback, &response);
714         return S_OK;
715     }
716     len = sizeof(szCommandFormat) - 2 /* "%s" */ + strlen(pszFromAddress);
717
718     szCommand = HeapAlloc(GetProcessHeap(), 0, len);
719     if (!szCommand)
720         return E_OUTOFMEMORY;
721
722     sprintf(szCommand, szCommandFormat, pszFromAddress);
723
724     hr = InternetTransport_DoCommand(&This->InetTransport, szCommand,
725         SMTPTransport_CallbackMessageReadFromResponse);
726
727     return hr;
728 }
729
730 static HRESULT WINAPI SMTPTransport_CommandMAIL(ISMTPTransport2 *iface, LPSTR pszEmailFrom)
731 {
732     SMTPTransport *This = (SMTPTransport *)iface;
733     const char szCommandFormat[] = "MAIL FROM: <%s>\n";
734     char *szCommand;
735     int len;
736     HRESULT hr;
737
738     TRACE("(%s)\n", debugstr_a(pszEmailFrom));
739
740     if (!pszEmailFrom)
741         return E_INVALIDARG;
742
743     len = sizeof(szCommandFormat) - 2 /* "%s" */ + strlen(pszEmailFrom);
744     szCommand = HeapAlloc(GetProcessHeap(), 0, len);
745     if (!szCommand)
746         return E_OUTOFMEMORY;
747
748     sprintf(szCommand, szCommandFormat, pszEmailFrom);
749
750     hr = InternetTransport_DoCommand(&This->InetTransport, szCommand,
751         SMTPTransport_CallbackReadMAILResponse);
752
753     HeapFree(GetProcessHeap(), 0, szCommand);
754     return hr;
755 }
756
757 static HRESULT WINAPI SMTPTransport_CommandRCPT(ISMTPTransport2 *iface, LPSTR pszEmailTo)
758 {
759     SMTPTransport *This = (SMTPTransport *)iface;
760     const char szCommandFormat[] = "RCPT TO: <%s>\n";
761     char *szCommand;
762     int len;
763     HRESULT hr;
764
765     TRACE("(%s)\n", debugstr_a(pszEmailTo));
766
767     if (!pszEmailTo)
768         return E_INVALIDARG;
769
770     len = sizeof(szCommandFormat) - 2 /* "%s" */ + strlen(pszEmailTo);
771     szCommand = HeapAlloc(GetProcessHeap(), 0, len);
772     if (!szCommand)
773         return E_OUTOFMEMORY;
774
775     sprintf(szCommand, szCommandFormat, pszEmailTo);
776
777     hr = InternetTransport_DoCommand(&This->InetTransport, szCommand,
778         SMTPTransport_CallbackReadRCPTResponse);
779
780     HeapFree(GetProcessHeap(), 0, szCommand);
781     return hr;
782 }
783
784 static HRESULT WINAPI SMTPTransport_CommandEHLO(ISMTPTransport2 *iface)
785 {
786     SMTPTransport *This = (SMTPTransport *)iface;
787     const char szCommandFormat[] = "EHLO %s\n";
788     const char szHostname[] = "localhost"; /* FIXME */
789     char *szCommand;
790     int len = sizeof(szCommandFormat) - 2 /* "%s" */ + sizeof(szHostname);
791     HRESULT hr;
792
793     TRACE("\n");
794
795     szCommand = HeapAlloc(GetProcessHeap(), 0, len);
796     if (!szCommand)
797         return E_OUTOFMEMORY;
798
799     sprintf(szCommand, szCommandFormat, szHostname);
800
801     hr = InternetTransport_DoCommand(&This->InetTransport, szCommand,
802         SMTPTransport_CallbackReadResponseDoNothing);
803
804     HeapFree(GetProcessHeap(), 0, szCommand);
805     return hr;
806 }
807
808 static HRESULT WINAPI SMTPTransport_CommandHELO(ISMTPTransport2 *iface)
809 {
810     SMTPTransport *This = (SMTPTransport *)iface;
811     const char szCommandFormat[] = "HELO %s\n";
812     const char szHostname[] = "localhost"; /* FIXME */
813     char *szCommand;
814     int len = sizeof(szCommandFormat) - 2 /* "%s" */ + sizeof(szHostname);
815     HRESULT hr;
816
817     TRACE("()\n");
818
819     szCommand = HeapAlloc(GetProcessHeap(), 0, len);
820     if (!szCommand)
821         return E_OUTOFMEMORY;
822
823     sprintf(szCommand, szCommandFormat, szHostname);
824
825     hr = InternetTransport_DoCommand(&This->InetTransport, szCommand,
826         SMTPTransport_CallbackReadResponseDoNothing);
827
828     HeapFree(GetProcessHeap(), 0, szCommand);
829     return hr;
830 }
831
832 static HRESULT WINAPI SMTPTransport_CommandAUTH(ISMTPTransport2 *iface,
833     LPSTR pszAuthType)
834 {
835     SMTPTransport *This = (SMTPTransport *)iface;
836     const char szCommandFormat[] = "AUTH %s\n";
837     char *szCommand;
838     int len;
839     HRESULT hr;
840
841     TRACE("(%s)\n", debugstr_a(pszAuthType));
842
843     if (!pszAuthType)
844         return E_INVALIDARG;
845
846     len = sizeof(szCommandFormat) - 2 /* "%s" */ + strlen(pszAuthType);
847     szCommand = HeapAlloc(GetProcessHeap(), 0, len);
848     if (!szCommand)
849         return E_OUTOFMEMORY;
850
851     sprintf(szCommand, szCommandFormat, pszAuthType);
852
853     hr = InternetTransport_DoCommand(&This->InetTransport, szCommand,
854         SMTPTransport_CallbackReadResponseDoNothing);
855
856     HeapFree(GetProcessHeap(), 0, szCommand);
857     return hr;
858 }
859
860 static HRESULT WINAPI SMTPTransport_CommandQUIT(ISMTPTransport2 *iface)
861 {
862     SMTPTransport *This = (SMTPTransport *)iface;
863
864     TRACE("()\n");
865
866     InternetTransport_ChangeStatus(&This->InetTransport, IXP_DISCONNECTING);
867     return InternetTransport_DoCommand(&This->InetTransport, "QUIT\n",
868         SMTPTransport_CallbackDisconnect);
869 }
870
871 static HRESULT WINAPI SMTPTransport_CommandRSET(ISMTPTransport2 *iface)
872 {
873     SMTPTransport *This = (SMTPTransport *)iface;
874
875     TRACE("()\n");
876
877     return InternetTransport_DoCommand(&This->InetTransport, "RSET\n",
878         SMTPTransport_CallbackReadResponseDoNothing);
879 }
880
881 static HRESULT WINAPI SMTPTransport_CommandDATA(ISMTPTransport2 *iface)
882 {
883     SMTPTransport *This = (SMTPTransport *)iface;
884
885     TRACE("()\n");
886
887     return InternetTransport_DoCommand(&This->InetTransport, "DATA\n",
888         SMTPTransport_CallbackReadDATAResponse);
889 }
890
891 static HRESULT WINAPI SMTPTransport_CommandDOT(ISMTPTransport2 *iface)
892 {
893     FIXME("()\n");
894     return E_NOTIMPL;
895 }
896
897 static HRESULT WINAPI SMTPTransport_SendDataStream(ISMTPTransport2 *iface,
898     IStream *pStream, ULONG cbSize)
899 {
900     FIXME("(%p, %d)\n", pStream, cbSize);
901     return E_NOTIMPL;
902 }
903
904 static HRESULT WINAPI SMTPTransport_SetWindow(ISMTPTransport2 *iface)
905 {
906     FIXME("()\n");
907     return E_NOTIMPL;
908 }
909
910 static HRESULT WINAPI SMTPTransport_ResetWindow(ISMTPTransport2 *iface)
911 {
912     FIXME("()\n");
913     return E_NOTIMPL;
914 }
915
916 static HRESULT WINAPI SMTPTransport_SendMessage2(ISMTPTransport2 *iface, LPSMTPMESSAGE2 pMessage)
917 {
918     FIXME("(%p)\n", pMessage);
919     return E_NOTIMPL;
920 }
921
922 static HRESULT WINAPI SMTPTransport_CommandRCPT2(ISMTPTransport2 *iface, LPSTR pszEmailTo,
923     INETADDRTYPE atDSN)
924 {
925     FIXME("(%s, %u)\n", pszEmailTo, atDSN);
926     return E_NOTIMPL;
927 }
928
929 static const ISMTPTransport2Vtbl SMTPTransport2Vtbl =
930 {
931     SMTPTransport_QueryInterface,
932     SMTPTransport_AddRef,
933     SMTPTransport_Release,
934     SMTPTransport_GetServerInfo,
935     SMTPTransport_GetIXPType,
936     SMTPTransport_IsState,
937     SMTPTransport_InetServerFromAccount,
938     SMTPTransport_Connect,
939     SMTPTransport_HandsOffCallback,
940     SMTPTransport_Disconnect,
941     SMTPTransport_DropConnection,
942     SMTPTransport_GetStatus,
943     SMTPTransport_InitNew,
944     SMTPTransport_SendMessage,
945     SMTPTransport_CommandMAIL,
946     SMTPTransport_CommandRCPT,
947     SMTPTransport_CommandEHLO,
948     SMTPTransport_CommandHELO,
949     SMTPTransport_CommandAUTH,
950     SMTPTransport_CommandQUIT,
951     SMTPTransport_CommandRSET,
952     SMTPTransport_CommandDATA,
953     SMTPTransport_CommandDOT,
954     SMTPTransport_SendDataStream,
955     SMTPTransport_SetWindow,
956     SMTPTransport_ResetWindow,
957     SMTPTransport_SendMessage2,
958     SMTPTransport_CommandRCPT2
959 };
960
961 HRESULT WINAPI CreateSMTPTransport(ISMTPTransport **ppTransport)
962 {
963     HRESULT hr;
964     SMTPTransport *This = HeapAlloc(GetProcessHeap(), 0, sizeof(*This));
965     if (!This)
966         return E_OUTOFMEMORY;
967
968     This->InetTransport.u.vtblSMTP2 = &SMTPTransport2Vtbl;
969     This->refs = 0;
970     This->fESMTP = FALSE;
971     hr = InternetTransport_Init(&This->InetTransport);
972     if (FAILED(hr))
973     {
974         HeapFree(GetProcessHeap(), 0, This);
975         return hr;
976     }
977
978     *ppTransport = (ISMTPTransport *)&This->InetTransport.u.vtblSMTP2;
979     ISMTPTransport_AddRef(*ppTransport);
980
981     return S_OK;
982 }
983
984
985 static HRESULT WINAPI SMTPTransportCF_QueryInterface(LPCLASSFACTORY iface,
986     REFIID riid, LPVOID *ppv)
987 {
988     *ppv = NULL;
989     if (IsEqualIID(riid, &IID_IUnknown) || IsEqualIID(riid, &IID_IClassFactory))
990     {
991         *ppv = iface;
992         IUnknown_AddRef(iface);
993         return S_OK;
994     }
995     return E_NOINTERFACE;
996 }
997
998 static ULONG WINAPI SMTPTransportCF_AddRef(LPCLASSFACTORY iface)
999 {
1000     return 2; /* non-heap based object */
1001 }
1002
1003 static ULONG WINAPI SMTPTransportCF_Release(LPCLASSFACTORY iface)
1004 {
1005     return 1; /* non-heap based object */
1006 }
1007
1008 static HRESULT WINAPI SMTPTransportCF_CreateInstance(LPCLASSFACTORY iface,
1009     LPUNKNOWN pUnk, REFIID riid, LPVOID *ppv)
1010 {
1011     HRESULT hr;
1012     ISMTPTransport *pSmtpTransport;
1013
1014     TRACE("(%p, %s, %p)\n", pUnk, debugstr_guid(riid), ppv);
1015
1016     *ppv = NULL;
1017
1018     if (pUnk)
1019         return CLASS_E_NOAGGREGATION;
1020
1021     hr = CreateSMTPTransport(&pSmtpTransport);
1022     if (FAILED(hr))
1023         return hr;
1024
1025     hr = ISMTPTransport_QueryInterface(pSmtpTransport, riid, ppv);
1026     ISMTPTransport_Release(pSmtpTransport);
1027
1028     return hr;
1029 }
1030
1031 static HRESULT WINAPI SMTPTransportCF_LockServer(LPCLASSFACTORY iface, BOOL fLock)
1032 {
1033     FIXME("(%d)\n",fLock);
1034     return S_OK;
1035 }
1036
1037 static const IClassFactoryVtbl SMTPTransportCFVtbl =
1038 {
1039     SMTPTransportCF_QueryInterface,
1040     SMTPTransportCF_AddRef,
1041     SMTPTransportCF_Release,
1042     SMTPTransportCF_CreateInstance,
1043     SMTPTransportCF_LockServer
1044 };
1045 static const IClassFactoryVtbl *SMTPTransportCF = &SMTPTransportCFVtbl;
1046
1047 HRESULT SMTPTransportCF_Create(REFIID riid, LPVOID *ppv)
1048 {
1049     return IClassFactory_QueryInterface((IClassFactory *)&SMTPTransportCF, riid, ppv);
1050 }