inetcomm: Add an implementation of the HELO/EHLO command.
[wine/multimedia.git] / dlls / inetcomm / smtptransport.c
blobb3bc781f8a41f88deea73d1e0e86ef6327366b5d
1 /*
2 * SMTP Transport
4 * Copyright 2006 Robert Shearman for CodeWeavers
5 * Copyright 2008 Hans Leidekker for CodeWeavers
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.
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.
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
23 #define COBJMACROS
25 #include <stdarg.h>
26 #include <stdio.h>
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"
36 #include "inetcomm_private.h"
38 WINE_DEFAULT_DEBUG_CHANNEL(inetcomm);
40 typedef struct
42 InternetTransport InetTransport;
43 ULONG refs;
44 BOOL fESMTP;
45 } SMTPTransport;
47 static HRESULT SMTPTransport_ParseResponse(SMTPTransport *This, char *pszResponse, SMTPRESPONSE *pResponse)
49 HRESULT hrServerError;
51 TRACE("response: %s\n", debugstr_a(pszResponse));
53 if (!isdigit(*pszResponse))
54 return IXP_E_SMTP_RESPONSE_ERROR;
55 pResponse->pTransport = (ISMTPTransport *)&This->InetTransport.u.vtblSMTP2;
56 pResponse->rIxpResult.pszResponse = pszResponse;
57 pResponse->rIxpResult.dwSocketError = 0;
58 pResponse->rIxpResult.uiServerError = strtol(pszResponse, &pszResponse, 10);
59 if (*pszResponse == '-')
61 pResponse->fDone = FALSE;
62 pszResponse++;
64 else
65 pResponse->fDone = TRUE;
67 switch (pResponse->rIxpResult.uiServerError)
69 case 211: hrServerError = IXP_E_SMTP_211_SYSTEM_STATUS; break;
70 case 214: hrServerError = IXP_E_SMTP_214_HELP_MESSAGE; break;
71 case 220: hrServerError = IXP_E_SMTP_220_READY; break;
72 case 221: hrServerError = IXP_E_SMTP_221_CLOSING; break;
73 case 245: hrServerError = IXP_E_SMTP_245_AUTH_SUCCESS; break;
74 case 250: hrServerError = IXP_E_SMTP_250_MAIL_ACTION_OKAY; break;
75 case 251: hrServerError = IXP_E_SMTP_251_FORWARDING_MAIL; break;
76 case 334: hrServerError = IXP_E_SMTP_334_AUTH_READY_RESPONSE; break;
77 case 354: hrServerError = IXP_E_SMTP_354_START_MAIL_INPUT; break;
78 case 421: hrServerError = IXP_E_SMTP_421_NOT_AVAILABLE; break;
79 case 450: hrServerError = IXP_E_SMTP_450_MAILBOX_BUSY; break;
80 case 451: hrServerError = IXP_E_SMTP_451_ERROR_PROCESSING; break;
81 case 452: hrServerError = IXP_E_SMTP_452_NO_SYSTEM_STORAGE; break;
82 case 454: hrServerError = IXP_E_SMTP_454_STARTTLS_FAILED; break;
83 case 500: hrServerError = IXP_E_SMTP_500_SYNTAX_ERROR; break;
84 case 501: hrServerError = IXP_E_SMTP_501_PARAM_SYNTAX; break;
85 case 502: hrServerError = IXP_E_SMTP_502_COMMAND_NOTIMPL; break;
86 case 503: hrServerError = IXP_E_SMTP_503_COMMAND_SEQ; break;
87 case 504: hrServerError = IXP_E_SMTP_504_COMMAND_PARAM_NOTIMPL; break;
88 case 530: hrServerError = IXP_E_SMTP_530_STARTTLS_REQUIRED; break;
89 case 550: hrServerError = IXP_E_SMTP_550_MAILBOX_NOT_FOUND; break;
90 case 551: hrServerError = IXP_E_SMTP_551_USER_NOT_LOCAL; break;
91 case 552: hrServerError = IXP_E_SMTP_552_STORAGE_OVERFLOW; break;
92 case 553: hrServerError = IXP_E_SMTP_553_MAILBOX_NAME_SYNTAX; break;
93 case 554: hrServerError = IXP_E_SMTP_554_TRANSACT_FAILED; break;
94 default:
95 hrServerError = IXP_E_SMTP_RESPONSE_ERROR;
96 break;
98 pResponse->rIxpResult.hrResult = hrServerError;
99 pResponse->rIxpResult.hrServerError = hrServerError;
101 if (This->InetTransport.pCallback && This->InetTransport.fCommandLogging)
103 ITransportCallback_OnCommand(This->InetTransport.pCallback, CMD_RESP,
104 pResponse->rIxpResult.pszResponse, hrServerError,
105 (IInternetTransport *)&This->InetTransport.u.vtbl);
107 return S_OK;
110 static void SMTPTransport_CallbackProcessHelloResp(IInternetTransport *iface, char *pBuffer, int cbBuffer)
112 SMTPTransport *This = (SMTPTransport *)iface;
113 SMTPRESPONSE response = { 0 };
114 HRESULT hr;
116 TRACE("\n");
118 hr = SMTPTransport_ParseResponse(This, pBuffer, &response);
119 if (FAILED(hr))
121 /* FIXME: handle error */
122 return;
125 response.command = This->fESMTP ? SMTP_EHLO : SMTP_HELO;
126 ISMTPCallback_OnResponse((ISMTPCallback *)This->InetTransport.pCallback, &response);
128 if (FAILED(response.rIxpResult.hrServerError))
130 ERR("server error: %s\n", debugstr_a(pBuffer));
131 /* FIXME: handle error */
132 return;
135 if (!response.fDone)
137 InternetTransport_ReadLine(&This->InetTransport,
138 SMTPTransport_CallbackProcessHelloResp);
139 return;
142 /* FIXME: try to authorize */
144 /* always changed to this status, even if authorization not support on server */
145 InternetTransport_ChangeStatus(&This->InetTransport, IXP_AUTHORIZED);
146 InternetTransport_ChangeStatus(&This->InetTransport, IXP_CONNECTED);
148 memset(&response, 0, sizeof(response));
149 response.command = SMTP_CONNECTED;
150 response.fDone = TRUE;
151 ISMTPCallback_OnResponse((ISMTPCallback *)This->InetTransport.pCallback, &response);
154 static void SMTPTransport_CallbackRecvHelloResp(IInternetTransport *iface, char *pBuffer, int cbBuffer)
156 SMTPTransport *This = (SMTPTransport *)iface;
158 TRACE("\n");
159 InternetTransport_ReadLine(&This->InetTransport, SMTPTransport_CallbackProcessHelloResp);
162 static void SMTPTransport_CallbackSendHello(IInternetTransport *iface, char *pBuffer, int cbBuffer)
164 SMTPTransport *This = (SMTPTransport *)iface;
165 SMTPRESPONSE response = { 0 };
166 HRESULT hr;
167 const char *pszHello;
168 char *pszCommand;
169 const char szHostName[] = "localhost"; /* FIXME */
171 TRACE("\n");
173 hr = SMTPTransport_ParseResponse(This, pBuffer, &response);
174 if (FAILED(hr))
176 /* FIXME: handle error */
177 return;
180 response.command = SMTP_BANNER;
181 ISMTPCallback_OnResponse((ISMTPCallback *)This->InetTransport.pCallback, &response);
183 if (FAILED(response.rIxpResult.hrServerError))
185 ERR("server error: %s\n", debugstr_a(pBuffer));
186 /* FIXME: handle error */
187 return;
190 TRACE("(%s)\n", pBuffer);
192 This->fESMTP = strstr(response.rIxpResult.pszResponse, "ESMTP") &&
193 This->InetTransport.ServerInfo.dwFlags & (ISF_SSLONSAMEPORT|ISF_QUERYDSNSUPPORT|ISF_QUERYAUTHSUPPORT);
195 if (This->fESMTP)
196 pszHello = "EHLO ";
197 else
198 pszHello = "HELO ";
200 pszCommand = HeapAlloc(GetProcessHeap(), 0, strlen(pszHello) + strlen(szHostName) + 2);
201 strcpy(pszCommand, pszHello);
202 strcat(pszCommand, szHostName);
203 pszCommand[strlen(pszCommand)+1] = '\0';
204 pszCommand[strlen(pszCommand)] = '\n';
206 InternetTransport_DoCommand(&This->InetTransport, pszCommand,
207 SMTPTransport_CallbackRecvHelloResp);
209 HeapFree(GetProcessHeap(), 0, pszCommand);
212 static HRESULT WINAPI SMTPTransport_QueryInterface(ISMTPTransport2 *iface, REFIID riid, void **ppv)
214 TRACE("(%s, %p)\n", debugstr_guid(riid), ppv);
216 if (IsEqualIID(riid, &IID_IUnknown) ||
217 IsEqualIID(riid, &IID_IInternetTransport) ||
218 IsEqualIID(riid, &IID_ISMTPTransport) ||
219 IsEqualIID(riid, &IID_ISMTPTransport2))
221 *ppv = iface;
222 IUnknown_AddRef(iface);
223 return S_OK;
225 *ppv = NULL;
226 FIXME("no interface for %s\n", debugstr_guid(riid));
227 return E_NOINTERFACE;
230 static ULONG WINAPI SMTPTransport_AddRef(ISMTPTransport2 *iface)
232 SMTPTransport *This = (SMTPTransport *)iface;
233 return InterlockedIncrement((LONG *)&This->refs);
236 static ULONG WINAPI SMTPTransport_Release(ISMTPTransport2 *iface)
238 SMTPTransport *This = (SMTPTransport *)iface;
239 ULONG refs = InterlockedDecrement((LONG *)&This->refs);
240 if (!refs)
242 TRACE("destroying %p\n", This);
243 if (This->InetTransport.Status != IXP_DISCONNECTED)
244 InternetTransport_DropConnection(&This->InetTransport);
246 if (This->InetTransport.pCallback) ITransportCallback_Release(This->InetTransport.pCallback);
247 HeapFree(GetProcessHeap(), 0, This);
249 return refs;
252 static HRESULT WINAPI SMTPTransport_GetServerInfo(ISMTPTransport2 *iface,
253 LPINETSERVER pInetServer)
255 SMTPTransport *This = (SMTPTransport *)iface;
257 TRACE("(%p)\n", pInetServer);
258 return InternetTransport_GetServerInfo(&This->InetTransport, pInetServer);
261 static IXPTYPE WINAPI SMTPTransport_GetIXPType(ISMTPTransport2 *iface)
263 TRACE("()\n");
264 return IXP_SMTP;
267 static HRESULT WINAPI SMTPTransport_IsState(ISMTPTransport2 *iface,
268 IXPISSTATE isstate)
270 FIXME("(%d): stub\n", isstate);
271 return E_NOTIMPL;
274 static HRESULT WINAPI SMTPTransport_InetServerFromAccount(
275 ISMTPTransport2 *iface, IImnAccount *pAccount, LPINETSERVER pInetServer)
277 SMTPTransport *This = (SMTPTransport *)iface;
279 TRACE("(%p, %p)\n", pAccount, pInetServer);
280 return InternetTransport_InetServerFromAccount(&This->InetTransport, pAccount, pInetServer);
283 static HRESULT WINAPI SMTPTransport_Connect(ISMTPTransport2 *iface,
284 LPINETSERVER pInetServer, boolean fAuthenticate, boolean fCommandLogging)
286 SMTPTransport *This = (SMTPTransport *)iface;
287 HRESULT hr;
289 TRACE("(%p, %s, %s)\n", pInetServer, fAuthenticate ? "TRUE" : "FALSE", fCommandLogging ? "TRUE" : "FALSE");
291 hr = InternetTransport_Connect(&This->InetTransport, pInetServer, fAuthenticate, fCommandLogging);
293 /* this starts the state machine, which continues in SMTPTransport_CallbackSendHELO */
294 return InternetTransport_ReadLine(&This->InetTransport, SMTPTransport_CallbackSendHello);
297 static HRESULT WINAPI SMTPTransport_HandsOffCallback(ISMTPTransport2 *iface)
299 SMTPTransport *This = (SMTPTransport *)iface;
301 TRACE("()\n");
302 return InternetTransport_HandsOffCallback(&This->InetTransport);
305 static HRESULT WINAPI SMTPTransport_Disconnect(ISMTPTransport2 *iface)
307 FIXME("()\n");
308 return E_NOTIMPL;
311 static HRESULT WINAPI SMTPTransport_DropConnection(ISMTPTransport2 *iface)
313 SMTPTransport *This = (SMTPTransport *)iface;
315 TRACE("()\n");
316 return InternetTransport_DropConnection(&This->InetTransport);
319 static HRESULT WINAPI SMTPTransport_GetStatus(ISMTPTransport2 *iface,
320 IXPSTATUS *pCurrentStatus)
322 SMTPTransport *This = (SMTPTransport *)iface;
324 TRACE("()\n");
325 return InternetTransport_GetStatus(&This->InetTransport, pCurrentStatus);
328 static HRESULT WINAPI SMTPTransport_InitNew(ISMTPTransport2 *iface,
329 LPSTR pszLogFilePath, ISMTPCallback *pCallback)
331 SMTPTransport *This = (SMTPTransport *)iface;
333 TRACE("(%s, %p)\n", debugstr_a(pszLogFilePath), pCallback);
335 if (!pCallback)
336 return E_INVALIDARG;
338 if (pszLogFilePath)
339 FIXME("not using log file of %s, use Wine debug logging instead\n",
340 debugstr_a(pszLogFilePath));
342 ISMTPCallback_AddRef(pCallback);
343 This->InetTransport.pCallback = (ITransportCallback *)pCallback;
344 This->InetTransport.fInitialised = TRUE;
346 return S_OK;
349 static HRESULT WINAPI SMTPTransport_SendMessage(ISMTPTransport2 *iface,
350 LPSMTPMESSAGE pMessage)
352 FIXME("(%p)\n", pMessage);
353 return E_NOTIMPL;
356 static HRESULT WINAPI SMTPTransport_CommandMAIL(ISMTPTransport2 *iface, LPSTR pszEmailFrom)
358 FIXME("(%s)\n", pszEmailFrom);
359 return E_NOTIMPL;
362 static HRESULT WINAPI SMTPTransport_CommandRCPT(ISMTPTransport2 *iface, LPSTR pszEmailTo)
364 FIXME("(%s)\n", pszEmailTo);
365 return E_NOTIMPL;
368 static HRESULT WINAPI SMTPTransport_CommandEHLO(ISMTPTransport2 *iface)
370 FIXME("\n");
371 return E_NOTIMPL;
374 static HRESULT WINAPI SMTPTransport_CommandHELO(ISMTPTransport2 *iface)
376 FIXME("()\n");
377 return E_NOTIMPL;
380 static HRESULT WINAPI SMTPTransport_CommandAUTH(ISMTPTransport2 *iface,
381 LPSTR pszAuthType)
383 FIXME("(%s)\n", pszAuthType);
384 return E_NOTIMPL;
387 static HRESULT WINAPI SMTPTransport_CommandQUIT(ISMTPTransport2 *iface)
389 FIXME("()\n");
390 return E_NOTIMPL;
393 static HRESULT WINAPI SMTPTransport_CommandRSET(ISMTPTransport2 *iface)
395 FIXME("()\n");
396 return E_NOTIMPL;
399 static HRESULT WINAPI SMTPTransport_CommandDATA(ISMTPTransport2 *iface)
401 FIXME("()\n");
402 return E_NOTIMPL;
405 static HRESULT WINAPI SMTPTransport_CommandDOT(ISMTPTransport2 *iface)
407 FIXME("()\n");
408 return E_NOTIMPL;
411 static HRESULT WINAPI SMTPTransport_SendDataStream(ISMTPTransport2 *iface,
412 IStream *pStream, ULONG cbSize)
414 FIXME("(%p, %d)\n", pStream, cbSize);
415 return E_NOTIMPL;
418 static HRESULT WINAPI SMTPTransport_SetWindow(ISMTPTransport2 *iface)
420 FIXME("()\n");
421 return E_NOTIMPL;
424 static HRESULT WINAPI SMTPTransport_ResetWindow(ISMTPTransport2 *iface)
426 FIXME("()\n");
427 return E_NOTIMPL;
430 static HRESULT WINAPI SMTPTransport_SendMessage2(ISMTPTransport2 *iface, LPSMTPMESSAGE2 pMessage)
432 FIXME("(%p)\n", pMessage);
433 return E_NOTIMPL;
436 static HRESULT WINAPI SMTPTransport_CommandRCPT2(ISMTPTransport2 *iface, LPSTR pszEmailTo,
437 INETADDRTYPE atDSN)
439 FIXME("(%s, %u)\n", pszEmailTo, atDSN);
440 return E_NOTIMPL;
443 static const ISMTPTransport2Vtbl SMTPTransport2Vtbl =
445 SMTPTransport_QueryInterface,
446 SMTPTransport_AddRef,
447 SMTPTransport_Release,
448 SMTPTransport_GetServerInfo,
449 SMTPTransport_GetIXPType,
450 SMTPTransport_IsState,
451 SMTPTransport_InetServerFromAccount,
452 SMTPTransport_Connect,
453 SMTPTransport_HandsOffCallback,
454 SMTPTransport_Disconnect,
455 SMTPTransport_DropConnection,
456 SMTPTransport_GetStatus,
457 SMTPTransport_InitNew,
458 SMTPTransport_SendMessage,
459 SMTPTransport_CommandMAIL,
460 SMTPTransport_CommandRCPT,
461 SMTPTransport_CommandEHLO,
462 SMTPTransport_CommandHELO,
463 SMTPTransport_CommandAUTH,
464 SMTPTransport_CommandQUIT,
465 SMTPTransport_CommandRSET,
466 SMTPTransport_CommandDATA,
467 SMTPTransport_CommandDOT,
468 SMTPTransport_SendDataStream,
469 SMTPTransport_SetWindow,
470 SMTPTransport_ResetWindow,
471 SMTPTransport_SendMessage2,
472 SMTPTransport_CommandRCPT2
475 HRESULT WINAPI CreateSMTPTransport(ISMTPTransport **ppTransport)
477 HRESULT hr;
478 SMTPTransport *This = HeapAlloc(GetProcessHeap(), 0, sizeof(*This));
479 if (!This)
480 return E_OUTOFMEMORY;
482 This->InetTransport.u.vtblSMTP2 = &SMTPTransport2Vtbl;
483 This->refs = 0;
484 This->fESMTP = FALSE;
485 hr = InternetTransport_Init(&This->InetTransport);
486 if (FAILED(hr))
488 HeapFree(GetProcessHeap(), 0, This);
489 return hr;
492 *ppTransport = (ISMTPTransport *)&This->InetTransport.u.vtblSMTP2;
493 ISMTPTransport_AddRef(*ppTransport);
495 return S_OK;
499 static HRESULT WINAPI SMTPTransportCF_QueryInterface(LPCLASSFACTORY iface,
500 REFIID riid, LPVOID *ppv)
502 *ppv = NULL;
503 if (IsEqualIID(riid, &IID_IUnknown) || IsEqualIID(riid, &IID_IClassFactory))
505 *ppv = iface;
506 IUnknown_AddRef(iface);
507 return S_OK;
509 return E_NOINTERFACE;
512 static ULONG WINAPI SMTPTransportCF_AddRef(LPCLASSFACTORY iface)
514 return 2; /* non-heap based object */
517 static ULONG WINAPI SMTPTransportCF_Release(LPCLASSFACTORY iface)
519 return 1; /* non-heap based object */
522 static HRESULT WINAPI SMTPTransportCF_CreateInstance(LPCLASSFACTORY iface,
523 LPUNKNOWN pUnk, REFIID riid, LPVOID *ppv)
525 HRESULT hr;
526 ISMTPTransport *pSmtpTransport;
528 TRACE("(%p, %s, %p)\n", pUnk, debugstr_guid(riid), ppv);
530 *ppv = NULL;
532 if (pUnk)
533 return CLASS_E_NOAGGREGATION;
535 hr = CreateSMTPTransport(&pSmtpTransport);
536 if (FAILED(hr))
537 return hr;
539 hr = ISMTPTransport_QueryInterface(pSmtpTransport, riid, ppv);
540 ISMTPTransport_Release(pSmtpTransport);
542 return hr;
545 static HRESULT WINAPI SMTPTransportCF_LockServer(LPCLASSFACTORY iface, BOOL fLock)
547 FIXME("(%d)\n",fLock);
548 return S_OK;
551 static const IClassFactoryVtbl SMTPTransportCFVtbl =
553 SMTPTransportCF_QueryInterface,
554 SMTPTransportCF_AddRef,
555 SMTPTransportCF_Release,
556 SMTPTransportCF_CreateInstance,
557 SMTPTransportCF_LockServer
559 static const IClassFactoryVtbl *SMTPTransportCF = &SMTPTransportCFVtbl;
561 HRESULT SMTPTransportCF_Create(REFIID riid, LPVOID *ppv)
563 return IClassFactory_QueryInterface((IClassFactory *)&SMTPTransportCF, riid, ppv);