af11308258d35557a2798dc586762c26e397eb31
[reactos.git] / dll / win32 / 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 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
107 static void SMTPTransport_CallbackDoNothing(IInternetTransport *iface, char *pBuffer, int cbBuffer)
108 {
109 TRACE("\n");
110 }
111
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
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
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
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
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
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
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
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
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
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 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
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
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
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
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
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
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
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
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 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
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
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
537 static ULONG WINAPI SMTPTransport_AddRef(ISMTPTransport2 *iface)
538 {
539 SMTPTransport *This = (SMTPTransport *)iface;
540 return InterlockedIncrement((LONG *)&This->refs);
541 }
542
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
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
569 static IXPTYPE WINAPI SMTPTransport_GetIXPType(ISMTPTransport2 *iface)
570 {
571 TRACE("()\n");
572 return IXP_SMTP;
573 }
574
575 static HRESULT WINAPI SMTPTransport_IsState(ISMTPTransport2 *iface,
576 IXPISSTATE isstate)
577 {
578 FIXME("(%d): stub\n", isstate);
579 return E_NOTIMPL;
580 }
581
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
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
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
615 static HRESULT WINAPI SMTPTransport_Disconnect(ISMTPTransport2 *iface)
616 {
617 TRACE("()\n");
618 return ISMTPTransport2_CommandQUIT(iface);
619 }
620
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
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
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
659 static HRESULT WINAPI SMTPTransport_SendMessage(ISMTPTransport2 *iface,
660 LPSMTPMESSAGE pMessage)
661 {
662 SMTPTransport *This = (SMTPTransport *)iface;
663 ULONG i, size;
664 LPSTR pszFromAddress = NULL;
665 const char szCommandFormat[] = "MAIL FROM: <%s>\n";
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
724 static HRESULT WINAPI SMTPTransport_CommandMAIL(ISMTPTransport2 *iface, LPSTR pszEmailFrom)
725 {
726 SMTPTransport *This = (SMTPTransport *)iface;
727 const char szCommandFormat[] = "MAIL FROM: <%s>\n";
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
751 static HRESULT WINAPI SMTPTransport_CommandRCPT(ISMTPTransport2 *iface, LPSTR pszEmailTo)
752 {
753 SMTPTransport *This = (SMTPTransport *)iface;
754 const char szCommandFormat[] = "RCPT TO: <%s>\n";
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
778 static HRESULT WINAPI SMTPTransport_CommandEHLO(ISMTPTransport2 *iface)
779 {
780 SMTPTransport *This = (SMTPTransport *)iface;
781 const char szCommandFormat[] = "EHLO %s\n";
782 const char szHostname[] = "localhost"; /* FIXME */
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
802 static HRESULT WINAPI SMTPTransport_CommandHELO(ISMTPTransport2 *iface)
803 {
804 SMTPTransport *This = (SMTPTransport *)iface;
805 const char szCommandFormat[] = "HELO %s\n";
806 const char szHostname[] = "localhost"; /* FIXME */
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
826 static HRESULT WINAPI SMTPTransport_CommandAUTH(ISMTPTransport2 *iface,
827 LPSTR pszAuthType)
828 {
829 SMTPTransport *This = (SMTPTransport *)iface;
830 const char szCommandFormat[] = "AUTH %s\n";
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
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
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
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
885 static HRESULT WINAPI SMTPTransport_CommandDOT(ISMTPTransport2 *iface)
886 {
887 FIXME("()\n");
888 return E_NOTIMPL;
889 }
890
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
898 static HRESULT WINAPI SMTPTransport_SetWindow(ISMTPTransport2 *iface)
899 {
900 FIXME("()\n");
901 return E_NOTIMPL;
902 }
903
904 static HRESULT WINAPI SMTPTransport_ResetWindow(ISMTPTransport2 *iface)
905 {
906 FIXME("()\n");
907 return E_NOTIMPL;
908 }
909
910 static HRESULT WINAPI SMTPTransport_SendMessage2(ISMTPTransport2 *iface, LPSMTPMESSAGE2 pMessage)
911 {
912 FIXME("(%p)\n", pMessage);
913 return E_NOTIMPL;
914 }
915
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
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
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
992 static ULONG WINAPI SMTPTransportCF_AddRef(LPCLASSFACTORY iface)
993 {
994 return 2; /* non-heap based object */
995 }
996
997 static ULONG WINAPI SMTPTransportCF_Release(LPCLASSFACTORY iface)
998 {
999 return 1; /* non-heap based object */
1000 }
1001
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
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
1041 HRESULT SMTPTransportCF_Create(REFIID riid, LPVOID *ppv)
1042 {
1043 return IClassFactory_QueryInterface((IClassFactory *)&SMTPTransportCF, riid, ppv);
1044 }