xref: /reactos/dll/win32/inetcomm/smtptransport.c (revision 25720d75)
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 }
1051