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