secur32: Eliminate broken clean-up "cheat".
[wine] / dlls / inetcomm / pop3transport.c
1 /*
2  * POP3 Transport
3  *
4  * Copyright 2008 Hans Leidekker for CodeWeavers
5  *
6  * This library is free software; you can redistribute it and/or
7  * modify it under the terms of the GNU Lesser General Public
8  * License as published by the Free Software Foundation; either
9  * version 2.1 of the License, or (at your option) any later version.
10  *
11  * This library is distributed in the hope that it will be useful,
12  * but WITHOUT ANY WARRANTY; without even the implied warranty of
13  * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
14  * Lesser General Public License for more details.
15  *
16  * You should have received a copy of the GNU Lesser General Public
17  * License along with this library; if not, write to the Free Software
18  * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301, USA
19  */
20
21 #define COBJMACROS
22 #define NONAMELESSUNION
23
24 #include <stdarg.h>
25 #include <stdio.h>
26
27 #include "windef.h"
28 #include "winbase.h"
29 #include "winnt.h"
30 #include "winuser.h"
31 #include "objbase.h"
32 #include "mimeole.h"
33 #include "wine/debug.h"
34
35 #include "inetcomm_private.h"
36
37 WINE_DEFAULT_DEBUG_CHANNEL(inetcomm);
38
39 enum parse_state
40 {
41     STATE_NONE,
42     STATE_OK,
43     STATE_MULTILINE,
44     STATE_DONE
45 };
46
47 typedef struct
48 {
49     InternetTransport InetTransport;
50     ULONG refs;
51     POP3COMMAND command;
52     POP3CMDTYPE type;
53     char *response;
54     char *ptr;
55     enum parse_state state;
56     BOOL valid_info;
57     DWORD msgid;
58     DWORD preview_lines;
59 } POP3Transport;
60
61 static HRESULT parse_response(POP3Transport *This)
62 {
63     switch (This->state)
64     {
65     case STATE_NONE:
66         if (strlen(This->response) < 3)
67         {
68             WARN("parse error\n");
69             This->state = STATE_DONE;
70             return S_FALSE;
71         }
72         if (!memcmp(This->response, "+OK", 3))
73         {
74             This->ptr = This->response + 3;
75             This->state = STATE_OK;
76             return S_OK;
77         }
78         This->state = STATE_DONE;
79         return S_FALSE;
80
81     default: return S_OK;
82     }
83 }
84
85 static HRESULT parse_uidl_response(POP3Transport *This, POP3UIDL *uidl)
86 {
87     char *p;
88
89     uidl->dwPopId = 0;
90     uidl->pszUidl = NULL;
91     switch (This->state)
92     {
93     case STATE_OK:
94         if (This->type == POP3CMD_GET_POPID)
95         {
96             if ((p = strchr(This->ptr, ' ')))
97             {
98                 while (*p == ' ') p++;
99                 sscanf(p, "%u", &uidl->dwPopId);
100                 if ((p = strchr(p, ' ')))
101                 {
102                     while (*p == ' ') p++;
103                     uidl->pszUidl = p;
104                     This->valid_info = TRUE;
105                 }
106              }
107              This->state = STATE_DONE;
108              return S_OK;
109         }
110         This->state = STATE_MULTILINE;
111         return S_OK;
112
113     case STATE_MULTILINE:
114         if (This->response[0] == '.' && !This->response[1])
115         {
116             This->valid_info = FALSE;
117             This->state = STATE_DONE;
118             return S_OK;
119         }
120         sscanf(This->response, "%u", &uidl->dwPopId);
121         if ((p = strchr(This->response, ' ')))
122         {
123             while (*p == ' ') p++;
124             uidl->pszUidl = p;
125             This->valid_info = TRUE;
126             return S_OK;
127         }
128
129     default:
130         WARN("parse error\n");
131         This->state = STATE_DONE;
132         return S_FALSE;
133     }
134 }
135
136 static HRESULT parse_stat_response(POP3Transport *This, POP3STAT *stat)
137 {
138     char *p;
139
140     stat->cMessages = 0;
141     stat->cbMessages = 0;
142     switch (This->state)
143     {
144     case STATE_OK:
145         if ((p = strchr(This->ptr, ' ')))
146         {
147             while (*p == ' ') p++;
148             sscanf(p, "%u %u", &stat->cMessages, &stat->cbMessages);
149             This->valid_info = TRUE;
150             This->state = STATE_DONE;
151             return S_OK;
152         }
153
154     default:
155         WARN("parse error\n");
156         This->state = STATE_DONE;
157         return S_FALSE;
158     }
159 }
160
161 static HRESULT parse_list_response(POP3Transport *This, POP3LIST *list)
162 {
163     char *p;
164
165     list->dwPopId = 0;
166     list->cbSize = 0;
167     switch (This->state)
168     {
169     case STATE_OK:
170         if (This->type == POP3CMD_GET_POPID)
171         {
172             if ((p = strchr(This->ptr, ' ')))
173             {
174                 while (*p == ' ') p++;
175                 sscanf(p, "%u %u", &list->dwPopId, &list->cbSize);
176                 This->valid_info = TRUE;
177             }
178             This->state = STATE_DONE;
179             return S_OK;
180         }
181         This->state = STATE_MULTILINE;
182         return S_OK;
183
184     case STATE_MULTILINE:
185         if (This->response[0] == '.' && !This->response[1])
186         {
187             This->valid_info = FALSE;
188             This->state = STATE_DONE;
189             return S_OK;
190         }
191         sscanf(This->response, "%u", &list->dwPopId);
192         if ((p = strchr(This->response, ' ')))
193         {
194             while (*p == ' ') p++;
195             sscanf(p, "%u", &list->cbSize);
196             This->valid_info = TRUE;
197             return S_OK;
198         }
199
200     default:
201         WARN("parse error\n");
202         This->state = STATE_DONE;
203         return S_FALSE;
204     }
205 }
206
207 static HRESULT parse_dele_response(POP3Transport *This, DWORD *dwPopId)
208 {
209     switch (This->state)
210     {
211     case STATE_OK:
212         *dwPopId = 0; /* FIXME */
213         This->state = STATE_DONE;
214         return S_OK;
215
216     default:
217         WARN("parse error\n");
218         This->state = STATE_DONE;
219         return S_FALSE;
220     }
221 }
222
223 static HRESULT parse_retr_response(POP3Transport *This, POP3RETR *retr)
224 {
225     switch (This->state)
226     {
227     case STATE_OK:
228         retr->fHeader = FALSE;
229         retr->fBody = FALSE;
230         retr->dwPopId = This->msgid;
231         retr->cbSoFar = 0;
232         retr->pszLines = This->response;
233         retr->cbLines = 0;
234
235         This->state = STATE_MULTILINE;
236         This->valid_info = FALSE;
237         return S_OK;
238
239     case STATE_MULTILINE:
240     {
241         int len;
242
243         if (This->response[0] == '.' && !This->response[1])
244         {
245             retr->cbLines = retr->cbSoFar;
246             This->state = STATE_DONE;
247             return S_OK;
248         }
249         retr->fHeader = TRUE;
250         if (!This->response[0]) retr->fBody = TRUE;
251
252         len = strlen(This->response);
253         retr->cbSoFar += len;
254         retr->pszLines = This->response;
255         retr->cbLines = len;
256
257         This->valid_info = TRUE;
258         return S_OK;
259     }
260
261     default:
262         WARN("parse error\n");
263         This->state = STATE_DONE;
264         return S_FALSE;
265     }
266 }
267
268 static HRESULT parse_top_response(POP3Transport *This, POP3TOP *top)
269 {
270     switch (This->state)
271     {
272     case STATE_OK:
273         top->fHeader = FALSE;
274         top->fBody = FALSE;
275         top->dwPopId = This->msgid;
276         top->cPreviewLines = This->preview_lines;
277         top->cbSoFar = 0;
278         top->pszLines = This->response;
279         top->cbLines = 0;
280
281         This->state = STATE_MULTILINE;
282         This->valid_info = FALSE;
283         return S_OK;
284
285     case STATE_MULTILINE:
286     {
287         int len;
288
289         if (This->response[0] == '.' && !This->response[1])
290         {
291             top->cbLines = top->cbSoFar;
292             This->state = STATE_DONE;
293             return S_OK;
294         }
295         top->fHeader = TRUE;
296         if (!This->response[0]) top->fBody = TRUE;
297
298         len = strlen(This->response);
299         top->cbSoFar += len;
300         top->pszLines = This->response;
301         top->cbLines = len;
302
303         This->valid_info = TRUE;
304         return S_OK;
305     }
306
307     default:
308         WARN("parse error\n");
309         This->state = STATE_DONE;
310         return S_FALSE;
311     }
312 }
313
314 static void init_parser(POP3Transport *This, POP3COMMAND command, POP3CMDTYPE type)
315 {
316     This->state = STATE_NONE;
317     This->command = command;
318     This->type = type;
319 }
320
321 static HRESULT POP3Transport_ParseResponse(POP3Transport *This, char *pszResponse, POP3RESPONSE *pResponse)
322 {
323     HRESULT hr;
324
325     TRACE("response: %s\n", debugstr_a(pszResponse));
326
327     This->response = pszResponse;
328     This->valid_info = FALSE;
329     TRACE("state %u\n", This->state);
330
331     if (SUCCEEDED((hr = parse_response(This))))
332     {
333         switch (This->command)
334         {
335         case POP3_UIDL: hr = parse_uidl_response(This, &pResponse->u.rUidlInfo); break;
336         case POP3_STAT: hr = parse_stat_response(This, &pResponse->u.rStatInfo); break;
337         case POP3_LIST: hr = parse_list_response(This, &pResponse->u.rListInfo); break;
338         case POP3_DELE: hr = parse_dele_response(This, &pResponse->u.dwPopId); break;
339         case POP3_RETR: hr = parse_retr_response(This, &pResponse->u.rRetrInfo); break;
340         case POP3_TOP: hr = parse_top_response(This, &pResponse->u.rTopInfo); break;
341         default:
342             This->state = STATE_DONE;
343             break;
344         }
345     }
346     pResponse->command = This->command;
347     pResponse->fDone = (This->state == STATE_DONE);
348     pResponse->fValidInfo = This->valid_info;
349     pResponse->rIxpResult.hrResult = hr;
350     pResponse->rIxpResult.pszResponse = pszResponse;
351     pResponse->rIxpResult.uiServerError = 0;
352     pResponse->rIxpResult.hrServerError = pResponse->rIxpResult.hrResult;
353     pResponse->rIxpResult.dwSocketError = WSAGetLastError();
354     pResponse->rIxpResult.pszProblem = NULL;
355     pResponse->pTransport = (IPOP3Transport *)&This->InetTransport.u.vtblPOP3;
356
357     if (This->InetTransport.pCallback && This->InetTransport.fCommandLogging)
358     {
359         ITransportCallback_OnCommand(This->InetTransport.pCallback, CMD_RESP,
360             pResponse->rIxpResult.pszResponse, pResponse->rIxpResult.hrServerError,
361             (IInternetTransport *)&This->InetTransport.u.vtbl);
362     }
363     return S_OK;
364 }
365
366 static void POP3Transport_CallbackProcessDELEResp(IInternetTransport *iface, char *pBuffer, int cbBuffer)
367 {
368     POP3Transport *This = (POP3Transport *)iface;
369     POP3RESPONSE response;
370     HRESULT hr;
371
372     TRACE("\n");
373
374     hr = POP3Transport_ParseResponse(This, pBuffer, &response);
375     if (FAILED(hr))
376     {
377         /* FIXME: handle error */
378         return;
379     }
380
381     IPOP3Callback_OnResponse((IPOP3Callback *)This->InetTransport.pCallback, &response);
382 }
383
384 static void POP3Transport_CallbackRecvDELEResp(IInternetTransport *iface, char *pBuffer, int cbBuffer)
385 {
386     POP3Transport *This = (POP3Transport *)iface;
387
388     TRACE("\n");
389     InternetTransport_ReadLine(&This->InetTransport, POP3Transport_CallbackProcessDELEResp);
390 }
391
392 static void POP3Transport_CallbackProcessNOOPResp(IInternetTransport *iface, char *pBuffer, int cbBuffer)
393 {
394     POP3Transport *This = (POP3Transport *)iface;
395     POP3RESPONSE response;
396     HRESULT hr;
397
398     TRACE("\n");
399
400     hr = POP3Transport_ParseResponse(This, pBuffer, &response);
401     if (FAILED(hr))
402     {
403         /* FIXME: handle error */
404         return;
405     }
406
407     IPOP3Callback_OnResponse((IPOP3Callback *)This->InetTransport.pCallback, &response);
408 }
409
410 static void POP3Transport_CallbackRecvNOOPResp(IInternetTransport *iface, char *pBuffer, int cbBuffer)
411 {
412     POP3Transport *This = (POP3Transport *)iface;
413
414     TRACE("\n");
415     InternetTransport_ReadLine(&This->InetTransport, POP3Transport_CallbackProcessNOOPResp);
416 }
417
418 static void POP3Transport_CallbackProcessRSETResp(IInternetTransport *iface, char *pBuffer, int cbBuffer)
419 {
420     POP3Transport *This = (POP3Transport *)iface;
421     POP3RESPONSE response;
422     HRESULT hr;
423
424     TRACE("\n");
425
426     hr = POP3Transport_ParseResponse(This, pBuffer, &response);
427     if (FAILED(hr))
428     {
429         /* FIXME: handle error */
430         return;
431     }
432
433     IPOP3Callback_OnResponse((IPOP3Callback *)This->InetTransport.pCallback, &response);
434 }
435
436 static void POP3Transport_CallbackRecvRSETResp(IInternetTransport *iface, char *pBuffer, int cbBuffer)
437 {
438     POP3Transport *This = (POP3Transport *)iface;
439
440     TRACE("\n");
441     InternetTransport_ReadLine(&This->InetTransport, POP3Transport_CallbackProcessRSETResp);
442 }
443
444 static void POP3Transport_CallbackProcessRETRResp(IInternetTransport *iface, char *pBuffer, int cbBuffer)
445 {
446     POP3Transport *This = (POP3Transport *)iface;
447     POP3RESPONSE response;
448     HRESULT hr;
449
450     TRACE("\n");
451
452     hr = POP3Transport_ParseResponse(This, pBuffer, &response);
453     if (FAILED(hr))
454     {
455         /* FIXME: handle error */
456         return;
457     }
458
459     IPOP3Callback_OnResponse((IPOP3Callback *)This->InetTransport.pCallback, &response);
460
461     if (!response.fDone)
462     {
463         InternetTransport_ReadLine(&This->InetTransport, POP3Transport_CallbackProcessRETRResp);
464         return;
465     }
466
467     IPOP3Callback_OnResponse((IPOP3Callback *)This->InetTransport.pCallback, &response);
468 }
469
470 static void POP3Transport_CallbackRecvRETRResp(IInternetTransport *iface, char *pBuffer, int cbBuffer)
471 {
472     POP3Transport *This = (POP3Transport *)iface;
473
474     TRACE("\n");
475     InternetTransport_ReadLine(&This->InetTransport, POP3Transport_CallbackProcessRETRResp);
476 }
477
478 static void POP3Transport_CallbackProcessTOPResp(IInternetTransport *iface, char *pBuffer, int cbBuffer)
479 {
480     POP3Transport *This = (POP3Transport *)iface;
481     POP3RESPONSE response;
482     HRESULT hr;
483
484     TRACE("\n");
485
486     hr = POP3Transport_ParseResponse(This, pBuffer, &response);
487     if (FAILED(hr))
488     {
489         /* FIXME: handle error */
490         return;
491     }
492
493     IPOP3Callback_OnResponse((IPOP3Callback *)This->InetTransport.pCallback, &response);
494
495     if (!response.fDone)
496     {
497         InternetTransport_ReadLine(&This->InetTransport, POP3Transport_CallbackProcessTOPResp);
498         return;
499     }
500
501     IPOP3Callback_OnResponse((IPOP3Callback *)This->InetTransport.pCallback, &response);
502 }
503
504 static void POP3Transport_CallbackRecvTOPResp(IInternetTransport *iface, char *pBuffer, int cbBuffer)
505 {
506     POP3Transport *This = (POP3Transport *)iface;
507
508     TRACE("\n");
509     InternetTransport_ReadLine(&This->InetTransport, POP3Transport_CallbackProcessTOPResp);
510 }
511
512 static void POP3Transport_CallbackProcessLISTResp(IInternetTransport *iface, char *pBuffer, int cbBuffer)
513 {
514     POP3Transport *This = (POP3Transport *)iface;
515     POP3RESPONSE response;
516     HRESULT hr;
517
518     TRACE("\n");
519
520     hr = POP3Transport_ParseResponse(This, pBuffer, &response);
521     if (FAILED(hr))
522     {
523         /* FIXME: handle error */
524         return;
525     }
526
527     IPOP3Callback_OnResponse((IPOP3Callback *)This->InetTransport.pCallback, &response);
528
529     if (!response.fDone)
530     {
531         InternetTransport_ReadLine(&This->InetTransport, POP3Transport_CallbackProcessLISTResp);
532         return;
533     }
534
535     IPOP3Callback_OnResponse((IPOP3Callback *)This->InetTransport.pCallback, &response);
536 }
537
538 static void POP3Transport_CallbackRecvLISTResp(IInternetTransport *iface, char *pBuffer, int cbBuffer)
539 {
540     POP3Transport *This = (POP3Transport *)iface;
541
542     TRACE("\n");
543     InternetTransport_ReadLine(&This->InetTransport, POP3Transport_CallbackProcessLISTResp);
544 }
545
546 static void POP3Transport_CallbackProcessUIDLResp(IInternetTransport *iface, char *pBuffer, int cbBuffer)
547 {
548     POP3Transport *This = (POP3Transport *)iface;
549     POP3RESPONSE response;
550     HRESULT hr;
551
552     TRACE("\n");
553
554     hr = POP3Transport_ParseResponse(This, pBuffer, &response);
555     if (FAILED(hr))
556     {
557         /* FIXME: handle error */
558         return;
559     }
560
561     IPOP3Callback_OnResponse((IPOP3Callback *)This->InetTransport.pCallback, &response);
562
563     if (!response.fDone)
564     {
565         InternetTransport_ReadLine(&This->InetTransport, POP3Transport_CallbackProcessUIDLResp);
566         return;
567     }
568
569     IPOP3Callback_OnResponse((IPOP3Callback *)This->InetTransport.pCallback, &response);
570 }
571
572 static void POP3Transport_CallbackRecvUIDLResp(IInternetTransport *iface, char *pBuffer, int cbBuffer)
573 {
574     POP3Transport *This = (POP3Transport *)iface;
575
576     TRACE("\n");
577     InternetTransport_ReadLine(&This->InetTransport, POP3Transport_CallbackProcessUIDLResp);
578 }
579
580 static void POP3Transport_CallbackProcessSTATResp(IInternetTransport *iface, char *pBuffer, int cbBuffer)
581 {
582     POP3Transport *This = (POP3Transport *)iface;
583     POP3RESPONSE response;
584     HRESULT hr;
585
586     TRACE("\n");
587
588     hr = POP3Transport_ParseResponse(This, pBuffer, &response);
589     if (FAILED(hr))
590     {
591         /* FIXME: handle error */
592         return;
593     }
594
595     IPOP3Callback_OnResponse((IPOP3Callback *)This->InetTransport.pCallback, &response);
596 }
597
598 static void POP3Transport_CallbackRecvSTATResp(IInternetTransport *iface, char *pBuffer, int cbBuffer)
599 {
600     POP3Transport *This = (POP3Transport *)iface;
601
602     TRACE("\n");
603     InternetTransport_ReadLine(&This->InetTransport, POP3Transport_CallbackProcessSTATResp);
604 }
605
606 static void POP3Transport_CallbackProcessPASSResp(IInternetTransport *iface, char *pBuffer, int cbBuffer)
607 {
608     POP3Transport *This = (POP3Transport *)iface;
609     POP3RESPONSE response;
610     HRESULT hr;
611
612     TRACE("\n");
613
614     hr = POP3Transport_ParseResponse(This, pBuffer, &response);
615     if (FAILED(hr))
616     {
617         /* FIXME: handle error */
618         return;
619     }
620
621     InternetTransport_ChangeStatus(&This->InetTransport, IXP_AUTHORIZED);
622     InternetTransport_ChangeStatus(&This->InetTransport, IXP_CONNECTED);
623
624     IPOP3Callback_OnResponse((IPOP3Callback *)This->InetTransport.pCallback, &response);
625 }
626
627 static void POP3Transport_CallbackRecvPASSResp(IInternetTransport *iface, char *pBuffer, int cbBuffer)
628 {
629     POP3Transport *This = (POP3Transport *)iface;
630
631     TRACE("\n");
632     InternetTransport_ReadLine(&This->InetTransport, POP3Transport_CallbackProcessPASSResp);
633 }
634
635 static void POP3Transport_CallbackProcessUSERResp(IInternetTransport *iface, char *pBuffer, int cbBuffer)
636 {
637     static char pass[] = "PASS ";
638     POP3Transport *This = (POP3Transport *)iface;
639     POP3RESPONSE response;
640     char *command;
641     int len;
642     HRESULT hr;
643
644     TRACE("\n");
645
646     hr = POP3Transport_ParseResponse(This, pBuffer, &response);
647     if (FAILED(hr))
648     {
649         /* FIXME: handle error */
650         return;
651     }
652
653     IPOP3Callback_OnResponse((IPOP3Callback *)This->InetTransport.pCallback, &response);
654
655     len = sizeof(pass) + strlen(This->InetTransport.ServerInfo.szPassword) + 2; /* "\r\n" */
656     command = HeapAlloc(GetProcessHeap(), 0, len);
657
658     strcpy(command, pass);
659     strcat(command, This->InetTransport.ServerInfo.szPassword);
660     strcat(command, "\r\n");
661
662     init_parser(This, POP3_PASS, POP3_NONE);
663
664     InternetTransport_DoCommand(&This->InetTransport, command, POP3Transport_CallbackRecvPASSResp);
665     HeapFree(GetProcessHeap(), 0, command);
666 }
667
668 static void POP3Transport_CallbackRecvUSERResp(IInternetTransport *iface, char *pBuffer, int cbBuffer)
669 {
670     POP3Transport *This = (POP3Transport *)iface;
671
672     TRACE("\n");
673     InternetTransport_ReadLine(&This->InetTransport, POP3Transport_CallbackProcessUSERResp);
674 }
675
676 static void POP3Transport_CallbackSendUSERCmd(IInternetTransport *iface, char *pBuffer, int cbBuffer)
677 {
678     static char user[] = "USER ";
679     POP3Transport *This = (POP3Transport *)iface;
680     char *command;
681     int len;
682
683     TRACE("\n");
684
685     len = sizeof(user) + strlen(This->InetTransport.ServerInfo.szUserName) + 2; /* "\r\n" */
686     command = HeapAlloc(GetProcessHeap(), 0, len);
687
688     strcpy(command, user);
689     strcat(command, This->InetTransport.ServerInfo.szUserName);
690     strcat(command, "\r\n");
691     InternetTransport_DoCommand(&This->InetTransport, command, POP3Transport_CallbackRecvUSERResp);
692
693     HeapFree(GetProcessHeap(), 0, command);
694 }
695
696 static void POP3Transport_CallbackProcessQUITResponse(IInternetTransport *iface, char *pBuffer, int cbBuffer)
697 {
698     POP3Transport *This = (POP3Transport *)iface;
699     POP3RESPONSE response;
700     HRESULT hr;
701
702     TRACE("%s\n", debugstr_an(pBuffer, cbBuffer));
703
704     hr = POP3Transport_ParseResponse(This, pBuffer, &response);
705     if (FAILED(hr))
706     {
707         /* FIXME: handle error */
708         return;
709     }
710
711     IPOP3Callback_OnResponse((IPOP3Callback *)This->InetTransport.pCallback, &response);
712     InternetTransport_DropConnection(&This->InetTransport);
713 }
714
715 static void POP3Transport_CallbackRecvQUITResp(IInternetTransport *iface, char *pBuffer, int cbBuffer)
716 {
717     POP3Transport *This = (POP3Transport *)iface;
718
719     TRACE("\n");
720     InternetTransport_ReadLine(&This->InetTransport, POP3Transport_CallbackProcessQUITResponse);
721 }
722
723 static HRESULT WINAPI POP3Transport_QueryInterface(IPOP3Transport *iface, REFIID riid, void **ppv)
724 {
725     TRACE("(%s, %p)\n", debugstr_guid(riid), ppv);
726
727     if (IsEqualIID(riid, &IID_IUnknown) ||
728         IsEqualIID(riid, &IID_IInternetTransport) ||
729         IsEqualIID(riid, &IID_IPOP3Transport))
730     {
731         *ppv = iface;
732         IUnknown_AddRef(iface);
733         return S_OK;
734     }
735     *ppv = NULL;
736     FIXME("no interface for %s\n", debugstr_guid(riid));
737     return E_NOINTERFACE;
738 }
739
740 static ULONG WINAPI POP3Transport_AddRef(IPOP3Transport *iface)
741 {
742     POP3Transport *This = (POP3Transport *)iface;
743     return InterlockedIncrement((LONG *)&This->refs);
744 }
745
746 static ULONG WINAPI POP3Transport_Release(IPOP3Transport *iface)
747 {
748     POP3Transport *This = (POP3Transport *)iface;
749     ULONG refs = InterlockedDecrement((LONG *)&This->refs);
750     if (!refs)
751     {
752         TRACE("destroying %p\n", This);
753         if (This->InetTransport.Status != IXP_DISCONNECTED)
754             InternetTransport_DropConnection(&This->InetTransport);
755         if (This->InetTransport.pCallback) ITransportCallback_Release(This->InetTransport.pCallback);
756         HeapFree(GetProcessHeap(), 0, This);
757     }
758     return refs;
759 }
760
761 static HRESULT WINAPI POP3Transport_GetServerInfo(IPOP3Transport *iface,
762     LPINETSERVER pInetServer)
763 {
764     POP3Transport *This = (POP3Transport *)iface;
765
766     TRACE("(%p)\n", pInetServer);
767     return InternetTransport_GetServerInfo(&This->InetTransport, pInetServer);
768 }
769
770 static IXPTYPE WINAPI POP3Transport_GetIXPType(IPOP3Transport *iface)
771 {
772     TRACE("()\n");
773     return IXP_POP3;
774 }
775
776 static HRESULT WINAPI POP3Transport_IsState(IPOP3Transport *iface, IXPISSTATE isstate)
777 {
778     FIXME("(%u)\n", isstate);
779     return E_NOTIMPL;
780 }
781
782 static HRESULT WINAPI POP3Transport_InetServerFromAccount(
783     IPOP3Transport *iface, IImnAccount *pAccount, LPINETSERVER pInetServer)
784 {
785     POP3Transport *This = (POP3Transport *)iface;
786
787     TRACE("(%p, %p)\n", pAccount, pInetServer);
788     return InternetTransport_InetServerFromAccount(&This->InetTransport, pAccount, pInetServer);
789 }
790
791 static HRESULT WINAPI POP3Transport_Connect(IPOP3Transport *iface,
792     LPINETSERVER pInetServer, boolean fAuthenticate, boolean fCommandLogging)
793 {
794     POP3Transport *This = (POP3Transport *)iface;
795     HRESULT hr;
796
797     TRACE("(%p, %s, %s)\n", pInetServer, fAuthenticate ? "TRUE" : "FALSE", fCommandLogging ? "TRUE" : "FALSE");
798
799     hr = InternetTransport_Connect(&This->InetTransport, pInetServer, fAuthenticate, fCommandLogging);
800     if (FAILED(hr))
801         return hr;
802
803     init_parser(This, POP3_USER, POP3_NONE);
804     return InternetTransport_ReadLine(&This->InetTransport, POP3Transport_CallbackSendUSERCmd);
805 }
806
807 static HRESULT WINAPI POP3Transport_HandsOffCallback(IPOP3Transport *iface)
808 {
809     POP3Transport *This = (POP3Transport *)iface;
810
811     TRACE("()\n");
812     return InternetTransport_HandsOffCallback(&This->InetTransport);
813 }
814
815 static HRESULT WINAPI POP3Transport_Disconnect(IPOP3Transport *iface)
816 {
817     TRACE("()\n");
818     return IPOP3Transport_CommandQUIT(iface);
819 }
820
821 static HRESULT WINAPI POP3Transport_DropConnection(IPOP3Transport *iface)
822 {
823     POP3Transport *This = (POP3Transport *)iface;
824
825     TRACE("()\n");
826     return InternetTransport_DropConnection(&This->InetTransport);
827 }
828
829 static HRESULT WINAPI POP3Transport_GetStatus(IPOP3Transport *iface,
830     IXPSTATUS *pCurrentStatus)
831 {
832     POP3Transport *This = (POP3Transport *)iface;
833
834     TRACE("()\n");
835     return InternetTransport_GetStatus(&This->InetTransport, pCurrentStatus);
836 }
837
838 static HRESULT WINAPI POP3Transport_InitNew(IPOP3Transport *iface,
839     LPSTR pszLogFilePath, IPOP3Callback *pCallback)
840 {
841     POP3Transport *This = (POP3Transport *)iface;
842
843     TRACE("(%s, %p)\n", debugstr_a(pszLogFilePath), pCallback);
844
845     if (!pCallback)
846         return E_INVALIDARG;
847
848     if (pszLogFilePath)
849         FIXME("not using log file of %s, use Wine debug logging instead\n",
850             debugstr_a(pszLogFilePath));
851
852     IPOP3Callback_AddRef(pCallback);
853     This->InetTransport.pCallback = (ITransportCallback *)pCallback;
854     This->InetTransport.fInitialised = TRUE;
855
856     return S_OK;
857 }
858
859 static HRESULT WINAPI POP3Transport_MarkItem(IPOP3Transport *iface, POP3MARKTYPE marktype,
860     DWORD dwPopId, boolean fMarked)
861 {
862     FIXME("(%u, %u, %d)\n", marktype, dwPopId, fMarked);
863     return E_NOTIMPL;
864 }
865
866 static HRESULT WINAPI POP3Transport_CommandAUTH(IPOP3Transport *iface, LPSTR pszAuthType)
867 {
868     FIXME("(%s)\n", pszAuthType);
869     return E_NOTIMPL;
870 }
871
872 static HRESULT WINAPI POP3Transport_CommandUSER(IPOP3Transport *iface, LPSTR username)
873 {
874     static char user[] = "USER ";
875     POP3Transport *This = (POP3Transport *)iface;
876     char *command;
877     int len;
878
879     TRACE("(%s)\n", username);
880
881     len = sizeof(user) + strlen(username) + 2; /* "\r\n" */
882     if (!(command = HeapAlloc(GetProcessHeap(), 0, len))) return S_FALSE;
883
884     strcpy(command, user);
885     strcat(command, username);
886     strcat(command, "\r\n");
887
888     init_parser(This, POP3_USER, POP3_NONE);
889     InternetTransport_DoCommand(&This->InetTransport, command, POP3Transport_CallbackRecvUSERResp);
890
891     HeapFree(GetProcessHeap(), 0, command);
892     return S_OK;
893 }
894
895 static HRESULT WINAPI POP3Transport_CommandPASS(IPOP3Transport *iface, LPSTR password)
896 {
897     static char pass[] = "PASS ";
898     POP3Transport *This = (POP3Transport *)iface;
899     char *command;
900     int len;
901
902     TRACE("(%p)\n", password);
903
904     len = sizeof(pass) + strlen(password) + 2; /* "\r\n" */
905     if (!(command = HeapAlloc(GetProcessHeap(), 0, len))) return S_FALSE;
906
907     strcpy(command, pass);
908     strcat(command, password);
909     strcat(command, "\r\n");
910
911     init_parser(This, POP3_PASS, POP3_NONE);
912     InternetTransport_DoCommand(&This->InetTransport, command, POP3Transport_CallbackRecvPASSResp);
913
914     HeapFree(GetProcessHeap(), 0, command);
915     return S_OK;
916 }
917
918 static HRESULT WINAPI POP3Transport_CommandLIST(
919     IPOP3Transport *iface, POP3CMDTYPE cmdtype, DWORD dwPopId)
920 {
921     static char list[] = "LIST %u\r\n";
922     static char list_all[] = "LIST\r\n";
923     POP3Transport *This = (POP3Transport *)iface;
924     char *command;
925     int len;
926
927     TRACE("(%u, %u)\n", cmdtype, dwPopId);
928
929     if (dwPopId)
930     {
931         len = sizeof(list) + 10 + 2; /* "4294967296" + "\r\n" */
932         if (!(command = HeapAlloc(GetProcessHeap(), 0, len))) return S_FALSE;
933         sprintf(command, list, dwPopId);
934     }
935     else command = list_all;
936
937     init_parser(This, POP3_LIST, cmdtype);
938     InternetTransport_DoCommand(&This->InetTransport, command, POP3Transport_CallbackRecvLISTResp);
939
940     if (dwPopId) HeapFree(GetProcessHeap(), 0, command);
941     return S_OK;
942 }
943
944 static HRESULT WINAPI POP3Transport_CommandTOP(
945     IPOP3Transport *iface, POP3CMDTYPE cmdtype, DWORD dwPopId, DWORD cPreviewLines)
946 {
947     static char top[] = "TOP %u %u\r\n";
948     POP3Transport *This = (POP3Transport *)iface;
949     char *command;
950     int len;
951
952     TRACE("(%u, %u, %u)\n", cmdtype, dwPopId, cPreviewLines);
953
954     len = sizeof(top) + 20 + 2; /* 2 * "4294967296" + "\r\n" */
955     if (!(command = HeapAlloc(GetProcessHeap(), 0, len))) return S_FALSE;
956     sprintf(command, top, dwPopId, cPreviewLines);
957
958     This->preview_lines = cPreviewLines;
959     init_parser(This, POP3_TOP, cmdtype);
960     InternetTransport_DoCommand(&This->InetTransport, command, POP3Transport_CallbackRecvTOPResp);
961
962     HeapFree(GetProcessHeap(), 0, command);
963     return S_OK;
964 }
965
966 static HRESULT WINAPI POP3Transport_CommandQUIT(IPOP3Transport *iface)
967 {
968     static char command[] = "QUIT\r\n";
969     POP3Transport *This = (POP3Transport *)iface;
970
971     TRACE("()\n");
972
973     InternetTransport_ChangeStatus(&This->InetTransport, IXP_DISCONNECTING);
974
975     init_parser(This, POP3_QUIT, POP3_NONE);
976     return InternetTransport_DoCommand(&This->InetTransport, command, POP3Transport_CallbackRecvQUITResp);
977 }
978
979 static HRESULT WINAPI POP3Transport_CommandSTAT(IPOP3Transport *iface)
980 {
981     static char stat[] = "STAT\r\n";
982     POP3Transport *This = (POP3Transport *)iface;
983
984     TRACE("\n");
985
986     init_parser(This, POP3_STAT, POP3_NONE);
987     InternetTransport_DoCommand(&This->InetTransport, stat, POP3Transport_CallbackRecvSTATResp);
988     return S_OK;
989 }
990
991 static HRESULT WINAPI POP3Transport_CommandNOOP(IPOP3Transport *iface)
992 {
993     static char noop[] = "NOOP\r\n";
994     POP3Transport *This = (POP3Transport *)iface;
995
996     TRACE("\n");
997
998     init_parser(This, POP3_NOOP, POP3_NONE);
999     InternetTransport_DoCommand(&This->InetTransport, noop, POP3Transport_CallbackRecvNOOPResp);
1000     return S_OK;
1001 }
1002
1003 static HRESULT WINAPI POP3Transport_CommandRSET(IPOP3Transport *iface)
1004 {
1005     static char rset[] = "RSET\r\n";
1006     POP3Transport *This = (POP3Transport *)iface;
1007
1008     TRACE("\n");
1009
1010     init_parser(This, POP3_RSET, POP3_NONE);
1011     InternetTransport_DoCommand(&This->InetTransport, rset, POP3Transport_CallbackRecvRSETResp);
1012     return S_OK;
1013 }
1014
1015 static HRESULT WINAPI POP3Transport_CommandUIDL(
1016     IPOP3Transport *iface, POP3CMDTYPE cmdtype, DWORD dwPopId)
1017 {
1018     static char uidl[] = "UIDL %u\r\n";
1019     static char uidl_all[] = "UIDL\r\n";
1020     POP3Transport *This = (POP3Transport *)iface;
1021     char *command;
1022     int len;
1023
1024     TRACE("(%u, %u)\n", cmdtype, dwPopId);
1025
1026     if (dwPopId)
1027     {
1028         len = sizeof(uidl) + 10 + 2; /* "4294967296" + "\r\n" */
1029         if (!(command = HeapAlloc(GetProcessHeap(), 0, len))) return S_FALSE;
1030         sprintf(command, uidl, dwPopId);
1031     }
1032     else command = uidl_all;
1033
1034     init_parser(This, POP3_UIDL, cmdtype);
1035     InternetTransport_DoCommand(&This->InetTransport, command, POP3Transport_CallbackRecvUIDLResp);
1036
1037     if (dwPopId) HeapFree(GetProcessHeap(), 0, command);
1038     return S_OK;
1039 }
1040
1041 static HRESULT WINAPI POP3Transport_CommandDELE(
1042     IPOP3Transport *iface, POP3CMDTYPE cmdtype, DWORD dwPopId)
1043 {
1044     static char dele[] = "DELE %u\r\n";
1045     POP3Transport *This = (POP3Transport *)iface;
1046     char *command;
1047     int len;
1048
1049     TRACE("(%u, %u)\n", cmdtype, dwPopId);
1050
1051     len = sizeof(dele) + 10 + 2; /* "4294967296" + "\r\n" */
1052     if (!(command = HeapAlloc(GetProcessHeap(), 0, len))) return S_FALSE;
1053     sprintf(command, dele, dwPopId);
1054
1055     init_parser(This, POP3_DELE, cmdtype);
1056     InternetTransport_DoCommand(&This->InetTransport, command, POP3Transport_CallbackRecvDELEResp);
1057
1058     HeapFree(GetProcessHeap(), 0, command);
1059     return S_OK;
1060 }
1061
1062 static HRESULT WINAPI POP3Transport_CommandRETR(
1063     IPOP3Transport *iface, POP3CMDTYPE cmdtype, DWORD dwPopId)
1064 {
1065     static char retr[] = "RETR %u\r\n";
1066     POP3Transport *This = (POP3Transport *)iface;
1067     char *command;
1068     int len;
1069
1070     TRACE("(%u, %u)\n", cmdtype, dwPopId);
1071
1072     len = sizeof(retr) + 10 + 2; /* "4294967296" + "\r\n" */
1073     if (!(command = HeapAlloc(GetProcessHeap(), 0, len))) return S_FALSE;
1074     sprintf(command, retr, dwPopId);
1075
1076     init_parser(This, POP3_RETR, cmdtype);
1077     InternetTransport_DoCommand(&This->InetTransport, command, POP3Transport_CallbackRecvRETRResp);
1078
1079     HeapFree(GetProcessHeap(), 0, command);
1080     return S_OK;
1081 }
1082
1083 static const IPOP3TransportVtbl POP3TransportVtbl =
1084 {
1085     POP3Transport_QueryInterface,
1086     POP3Transport_AddRef,
1087     POP3Transport_Release,
1088     POP3Transport_GetServerInfo,
1089     POP3Transport_GetIXPType,
1090     POP3Transport_IsState,
1091     POP3Transport_InetServerFromAccount,
1092     POP3Transport_Connect,
1093     POP3Transport_HandsOffCallback,
1094     POP3Transport_Disconnect,
1095     POP3Transport_DropConnection,
1096     POP3Transport_GetStatus,
1097     POP3Transport_InitNew,
1098     POP3Transport_MarkItem,
1099     POP3Transport_CommandAUTH,
1100     POP3Transport_CommandUSER,
1101     POP3Transport_CommandPASS,
1102     POP3Transport_CommandLIST,
1103     POP3Transport_CommandTOP,
1104     POP3Transport_CommandQUIT,
1105     POP3Transport_CommandSTAT,
1106     POP3Transport_CommandNOOP,
1107     POP3Transport_CommandRSET,
1108     POP3Transport_CommandUIDL,
1109     POP3Transport_CommandDELE,
1110     POP3Transport_CommandRETR
1111 };
1112
1113 HRESULT WINAPI CreatePOP3Transport(IPOP3Transport **ppTransport)
1114 {
1115     HRESULT hr;
1116     POP3Transport *This = HeapAlloc(GetProcessHeap(), 0, sizeof(*This));
1117     if (!This)
1118         return E_OUTOFMEMORY;
1119
1120     This->InetTransport.u.vtblPOP3 = &POP3TransportVtbl;
1121     This->refs = 0;
1122     hr = InternetTransport_Init(&This->InetTransport);
1123     if (FAILED(hr))
1124     {
1125         HeapFree(GetProcessHeap(), 0, This);
1126         return hr;
1127     }
1128
1129     *ppTransport = (IPOP3Transport *)&This->InetTransport.u.vtblPOP3;
1130     IPOP3Transport_AddRef(*ppTransport);
1131
1132     return S_OK;
1133 }
1134
1135 static HRESULT WINAPI POP3TransportCF_QueryInterface(LPCLASSFACTORY iface,
1136     REFIID riid, LPVOID *ppv)
1137 {
1138     *ppv = NULL;
1139     if (IsEqualIID(riid, &IID_IUnknown) || IsEqualIID(riid, &IID_IClassFactory))
1140     {
1141         *ppv = iface;
1142         IUnknown_AddRef(iface);
1143         return S_OK;
1144     }
1145     return E_NOINTERFACE;
1146 }
1147
1148 static ULONG WINAPI POP3TransportCF_AddRef(LPCLASSFACTORY iface)
1149 {
1150     return 2; /* non-heap based object */
1151 }
1152
1153 static ULONG WINAPI POP3TransportCF_Release(LPCLASSFACTORY iface)
1154 {
1155     return 1; /* non-heap based object */
1156 }
1157
1158 static HRESULT WINAPI POP3TransportCF_CreateInstance(LPCLASSFACTORY iface,
1159     LPUNKNOWN pUnk, REFIID riid, LPVOID *ppv)
1160 {
1161     HRESULT hr;
1162     IPOP3Transport *pPop3Transport;
1163
1164     TRACE("(%p, %s, %p)\n", pUnk, debugstr_guid(riid), ppv);
1165
1166     *ppv = NULL;
1167
1168     if (pUnk)
1169         return CLASS_E_NOAGGREGATION;
1170
1171     hr = CreatePOP3Transport(&pPop3Transport);
1172     if (FAILED(hr))
1173         return hr;
1174
1175     hr = IPOP3Transport_QueryInterface(pPop3Transport, riid, ppv);
1176     IPOP3Transport_Release(pPop3Transport);
1177
1178     return hr;
1179 }
1180
1181 static HRESULT WINAPI POP3TransportCF_LockServer(LPCLASSFACTORY iface, BOOL fLock)
1182 {
1183     FIXME("(%d)\n",fLock);
1184     return S_OK;
1185 }
1186
1187 static const IClassFactoryVtbl POP3TransportCFVtbl =
1188 {
1189     POP3TransportCF_QueryInterface,
1190     POP3TransportCF_AddRef,
1191     POP3TransportCF_Release,
1192     POP3TransportCF_CreateInstance,
1193     POP3TransportCF_LockServer
1194 };
1195 static const IClassFactoryVtbl *POP3TransportCF = &POP3TransportCFVtbl;
1196
1197 HRESULT POP3TransportCF_Create(REFIID riid, LPVOID *ppv)
1198 {
1199     return IClassFactory_QueryInterface((IClassFactory *)&POP3TransportCF, riid, ppv);
1200 }