riched32: Modified tests to show paragraph break inconsistency.
[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 = sizeof(szCommandFormat) - 2 /* "%s" */ + strlen(pszEmailFrom);
736     HRESULT hr;
737
738     TRACE("(%s)\n", debugstr_a(pszEmailFrom));
739
740     if (!pszEmailFrom)
741         return E_INVALIDARG;
742
743     szCommand = HeapAlloc(GetProcessHeap(), 0, len);
744     if (!szCommand)
745         return E_OUTOFMEMORY;
746
747     sprintf(szCommand, szCommandFormat, pszEmailFrom);
748
749     hr = InternetTransport_DoCommand(&This->InetTransport, szCommand,
750         SMTPTransport_CallbackReadMAILResponse);
751
752     HeapFree(GetProcessHeap(), 0, szCommand);
753     return hr;
754 }
755
756 static HRESULT WINAPI SMTPTransport_CommandRCPT(ISMTPTransport2 *iface, LPSTR pszEmailTo)
757 {
758     SMTPTransport *This = (SMTPTransport *)iface;
759     const char szCommandFormat[] = "RCPT TO: <%s>\n";
760     char *szCommand;
761     int len = sizeof(szCommandFormat) - 2 /* "%s" */ + strlen(pszEmailTo);
762     HRESULT hr;
763
764     TRACE("(%s)\n", debugstr_a(pszEmailTo));
765
766     if (!pszEmailTo)
767         return E_INVALIDARG;
768
769     szCommand = HeapAlloc(GetProcessHeap(), 0, len);
770     if (!szCommand)
771         return E_OUTOFMEMORY;
772
773     sprintf(szCommand, szCommandFormat, pszEmailTo);
774
775     hr = InternetTransport_DoCommand(&This->InetTransport, szCommand,
776         SMTPTransport_CallbackReadRCPTResponse);
777
778     HeapFree(GetProcessHeap(), 0, szCommand);
779     return hr;
780 }
781
782 static HRESULT WINAPI SMTPTransport_CommandEHLO(ISMTPTransport2 *iface)
783 {
784     SMTPTransport *This = (SMTPTransport *)iface;
785     const char szCommandFormat[] = "EHLO %s\n";
786     const char szHostname[] = "localhost"; /* FIXME */
787     char *szCommand;
788     int len = sizeof(szCommandFormat) - 2 /* "%s" */ + sizeof(szHostname);
789     HRESULT hr;
790
791     TRACE("\n");
792
793     szCommand = HeapAlloc(GetProcessHeap(), 0, len);
794     if (!szCommand)
795         return E_OUTOFMEMORY;
796
797     sprintf(szCommand, szCommandFormat, szHostname);
798
799     hr = InternetTransport_DoCommand(&This->InetTransport, szCommand,
800         SMTPTransport_CallbackReadResponseDoNothing);
801
802     HeapFree(GetProcessHeap(), 0, szCommand);
803     return hr;
804 }
805
806 static HRESULT WINAPI SMTPTransport_CommandHELO(ISMTPTransport2 *iface)
807 {
808     SMTPTransport *This = (SMTPTransport *)iface;
809     const char szCommandFormat[] = "HELO %s\n";
810     const char szHostname[] = "localhost"; /* FIXME */
811     char *szCommand;
812     int len = sizeof(szCommandFormat) - 2 /* "%s" */ + sizeof(szHostname);
813     HRESULT hr;
814
815     TRACE("()\n");
816
817     szCommand = HeapAlloc(GetProcessHeap(), 0, len);
818     if (!szCommand)
819         return E_OUTOFMEMORY;
820
821     sprintf(szCommand, szCommandFormat, szHostname);
822
823     hr = InternetTransport_DoCommand(&This->InetTransport, szCommand,
824         SMTPTransport_CallbackReadResponseDoNothing);
825
826     HeapFree(GetProcessHeap(), 0, szCommand);
827     return hr;
828 }
829
830 static HRESULT WINAPI SMTPTransport_CommandAUTH(ISMTPTransport2 *iface,
831     LPSTR pszAuthType)
832 {
833     SMTPTransport *This = (SMTPTransport *)iface;
834     const char szCommandFormat[] = "AUTH %s\n";
835     char *szCommand;
836     int len = sizeof(szCommandFormat) - 2 /* "%s" */ + strlen(pszAuthType);
837     HRESULT hr;
838
839     TRACE("(%s)\n", debugstr_a(pszAuthType));
840
841     if (!pszAuthType)
842         return E_INVALIDARG;
843
844     szCommand = HeapAlloc(GetProcessHeap(), 0, len);
845     if (!szCommand)
846         return E_OUTOFMEMORY;
847
848     sprintf(szCommand, szCommandFormat, pszAuthType);
849
850     hr = InternetTransport_DoCommand(&This->InetTransport, szCommand,
851         SMTPTransport_CallbackReadResponseDoNothing);
852
853     HeapFree(GetProcessHeap(), 0, szCommand);
854     return hr;
855 }
856
857 static HRESULT WINAPI SMTPTransport_CommandQUIT(ISMTPTransport2 *iface)
858 {
859     SMTPTransport *This = (SMTPTransport *)iface;
860
861     TRACE("()\n");
862
863     InternetTransport_ChangeStatus(&This->InetTransport, IXP_DISCONNECTING);
864     return InternetTransport_DoCommand(&This->InetTransport, "QUIT\n",
865         SMTPTransport_CallbackDisconnect);
866 }
867
868 static HRESULT WINAPI SMTPTransport_CommandRSET(ISMTPTransport2 *iface)
869 {
870     SMTPTransport *This = (SMTPTransport *)iface;
871
872     TRACE("()\n");
873
874     return InternetTransport_DoCommand(&This->InetTransport, "RSET\n",
875         SMTPTransport_CallbackReadResponseDoNothing);
876 }
877
878 static HRESULT WINAPI SMTPTransport_CommandDATA(ISMTPTransport2 *iface)
879 {
880     SMTPTransport *This = (SMTPTransport *)iface;
881
882     TRACE("()\n");
883
884     return InternetTransport_DoCommand(&This->InetTransport, "DATA\n",
885         SMTPTransport_CallbackReadDATAResponse);
886 }
887
888 static HRESULT WINAPI SMTPTransport_CommandDOT(ISMTPTransport2 *iface)
889 {
890     FIXME("()\n");
891     return E_NOTIMPL;
892 }
893
894 static HRESULT WINAPI SMTPTransport_SendDataStream(ISMTPTransport2 *iface,
895     IStream *pStream, ULONG cbSize)
896 {
897     FIXME("(%p, %d)\n", pStream, cbSize);
898     return E_NOTIMPL;
899 }
900
901 static HRESULT WINAPI SMTPTransport_SetWindow(ISMTPTransport2 *iface)
902 {
903     FIXME("()\n");
904     return E_NOTIMPL;
905 }
906
907 static HRESULT WINAPI SMTPTransport_ResetWindow(ISMTPTransport2 *iface)
908 {
909     FIXME("()\n");
910     return E_NOTIMPL;
911 }
912
913 static HRESULT WINAPI SMTPTransport_SendMessage2(ISMTPTransport2 *iface, LPSMTPMESSAGE2 pMessage)
914 {
915     FIXME("(%p)\n", pMessage);
916     return E_NOTIMPL;
917 }
918
919 static HRESULT WINAPI SMTPTransport_CommandRCPT2(ISMTPTransport2 *iface, LPSTR pszEmailTo,
920     INETADDRTYPE atDSN)
921 {
922     FIXME("(%s, %u)\n", pszEmailTo, atDSN);
923     return E_NOTIMPL;
924 }
925
926 static const ISMTPTransport2Vtbl SMTPTransport2Vtbl =
927 {
928     SMTPTransport_QueryInterface,
929     SMTPTransport_AddRef,
930     SMTPTransport_Release,
931     SMTPTransport_GetServerInfo,
932     SMTPTransport_GetIXPType,
933     SMTPTransport_IsState,
934     SMTPTransport_InetServerFromAccount,
935     SMTPTransport_Connect,
936     SMTPTransport_HandsOffCallback,
937     SMTPTransport_Disconnect,
938     SMTPTransport_DropConnection,
939     SMTPTransport_GetStatus,
940     SMTPTransport_InitNew,
941     SMTPTransport_SendMessage,
942     SMTPTransport_CommandMAIL,
943     SMTPTransport_CommandRCPT,
944     SMTPTransport_CommandEHLO,
945     SMTPTransport_CommandHELO,
946     SMTPTransport_CommandAUTH,
947     SMTPTransport_CommandQUIT,
948     SMTPTransport_CommandRSET,
949     SMTPTransport_CommandDATA,
950     SMTPTransport_CommandDOT,
951     SMTPTransport_SendDataStream,
952     SMTPTransport_SetWindow,
953     SMTPTransport_ResetWindow,
954     SMTPTransport_SendMessage2,
955     SMTPTransport_CommandRCPT2
956 };
957
958 HRESULT WINAPI CreateSMTPTransport(ISMTPTransport **ppTransport)
959 {
960     HRESULT hr;
961     SMTPTransport *This = HeapAlloc(GetProcessHeap(), 0, sizeof(*This));
962     if (!This)
963         return E_OUTOFMEMORY;
964
965     This->InetTransport.u.vtblSMTP2 = &SMTPTransport2Vtbl;
966     This->refs = 0;
967     This->fESMTP = FALSE;
968     hr = InternetTransport_Init(&This->InetTransport);
969     if (FAILED(hr))
970     {
971         HeapFree(GetProcessHeap(), 0, This);
972         return hr;
973     }
974
975     *ppTransport = (ISMTPTransport *)&This->InetTransport.u.vtblSMTP2;
976     ISMTPTransport_AddRef(*ppTransport);
977
978     return S_OK;
979 }
980
981
982 static HRESULT WINAPI SMTPTransportCF_QueryInterface(LPCLASSFACTORY iface,
983     REFIID riid, LPVOID *ppv)
984 {
985     *ppv = NULL;
986     if (IsEqualIID(riid, &IID_IUnknown) || IsEqualIID(riid, &IID_IClassFactory))
987     {
988         *ppv = iface;
989         IUnknown_AddRef(iface);
990         return S_OK;
991     }
992     return E_NOINTERFACE;
993 }
994
995 static ULONG WINAPI SMTPTransportCF_AddRef(LPCLASSFACTORY iface)
996 {
997     return 2; /* non-heap based object */
998 }
999
1000 static ULONG WINAPI SMTPTransportCF_Release(LPCLASSFACTORY iface)
1001 {
1002     return 1; /* non-heap based object */
1003 }
1004
1005 static HRESULT WINAPI SMTPTransportCF_CreateInstance(LPCLASSFACTORY iface,
1006     LPUNKNOWN pUnk, REFIID riid, LPVOID *ppv)
1007 {
1008     HRESULT hr;
1009     ISMTPTransport *pSmtpTransport;
1010
1011     TRACE("(%p, %s, %p)\n", pUnk, debugstr_guid(riid), ppv);
1012
1013     *ppv = NULL;
1014
1015     if (pUnk)
1016         return CLASS_E_NOAGGREGATION;
1017
1018     hr = CreateSMTPTransport(&pSmtpTransport);
1019     if (FAILED(hr))
1020         return hr;
1021
1022     hr = ISMTPTransport_QueryInterface(pSmtpTransport, riid, ppv);
1023     ISMTPTransport_Release(pSmtpTransport);
1024
1025     return hr;
1026 }
1027
1028 static HRESULT WINAPI SMTPTransportCF_LockServer(LPCLASSFACTORY iface, BOOL fLock)
1029 {
1030     FIXME("(%d)\n",fLock);
1031     return S_OK;
1032 }
1033
1034 static const IClassFactoryVtbl SMTPTransportCFVtbl =
1035 {
1036     SMTPTransportCF_QueryInterface,
1037     SMTPTransportCF_AddRef,
1038     SMTPTransportCF_Release,
1039     SMTPTransportCF_CreateInstance,
1040     SMTPTransportCF_LockServer
1041 };
1042 static const IClassFactoryVtbl *SMTPTransportCF = &SMTPTransportCFVtbl;
1043
1044 HRESULT SMTPTransportCF_Create(REFIID riid, LPVOID *ppv)
1045 {
1046     return IClassFactory_QueryInterface((IClassFactory *)&SMTPTransportCF, riid, ppv);
1047 }