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