xref: /reactos/dll/win32/inetcomm/pop3transport.c (revision 0623a6f8)
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)
315 {
316     This->state = STATE_NONE;
317     This->command = command;
318 }
319 
320 static HRESULT POP3Transport_ParseResponse(POP3Transport *This, char *pszResponse, POP3RESPONSE *pResponse)
321 {
322     HRESULT hr;
323 
324     TRACE("response: %s\n", debugstr_a(pszResponse));
325 
326     This->response = pszResponse;
327     This->valid_info = FALSE;
328     TRACE("state %u\n", This->state);
329 
330     if (SUCCEEDED((hr = parse_response(This))))
331     {
332         switch (This->command)
333         {
334         case POP3_UIDL: hr = parse_uidl_response(This, &pResponse->u.rUidlInfo); break;
335         case POP3_STAT: hr = parse_stat_response(This, &pResponse->u.rStatInfo); break;
336         case POP3_LIST: hr = parse_list_response(This, &pResponse->u.rListInfo); break;
337         case POP3_DELE: hr = parse_dele_response(This, &pResponse->u.dwPopId); break;
338         case POP3_RETR: hr = parse_retr_response(This, &pResponse->u.rRetrInfo); break;
339         case POP3_TOP: hr = parse_top_response(This, &pResponse->u.rTopInfo); break;
340         default:
341             This->state = STATE_DONE;
342             break;
343         }
344     }
345     pResponse->command = This->command;
346     pResponse->fDone = (This->state == STATE_DONE);
347     pResponse->fValidInfo = This->valid_info;
348     pResponse->rIxpResult.hrResult = hr;
349     pResponse->rIxpResult.pszResponse = pszResponse;
350     pResponse->rIxpResult.uiServerError = 0;
351     pResponse->rIxpResult.hrServerError = pResponse->rIxpResult.hrResult;
352     pResponse->rIxpResult.dwSocketError = WSAGetLastError();
353     pResponse->rIxpResult.pszProblem = NULL;
354     pResponse->pTransport = (IPOP3Transport *)&This->InetTransport.u.vtblPOP3;
355 
356     if (This->InetTransport.pCallback && This->InetTransport.fCommandLogging)
357     {
358         ITransportCallback_OnCommand(This->InetTransport.pCallback, CMD_RESP,
359             pResponse->rIxpResult.pszResponse, pResponse->rIxpResult.hrServerError,
360             (IInternetTransport *)&This->InetTransport.u.vtbl);
361     }
362     return S_OK;
363 }
364 
365 static void POP3Transport_CallbackProcessDELEResp(IInternetTransport *iface, char *pBuffer, int cbBuffer)
366 {
367     POP3Transport *This = (POP3Transport *)iface;
368     POP3RESPONSE response;
369     HRESULT hr;
370 
371     TRACE("\n");
372 
373     hr = POP3Transport_ParseResponse(This, pBuffer, &response);
374     if (FAILED(hr))
375     {
376         /* FIXME: handle error */
377         return;
378     }
379 
380     IPOP3Callback_OnResponse((IPOP3Callback *)This->InetTransport.pCallback, &response);
381 }
382 
383 static void POP3Transport_CallbackRecvDELEResp(IInternetTransport *iface, char *pBuffer, int cbBuffer)
384 {
385     POP3Transport *This = (POP3Transport *)iface;
386 
387     TRACE("\n");
388     InternetTransport_ReadLine(&This->InetTransport, POP3Transport_CallbackProcessDELEResp);
389 }
390 
391 static void POP3Transport_CallbackProcessNOOPResp(IInternetTransport *iface, char *pBuffer, int cbBuffer)
392 {
393     POP3Transport *This = (POP3Transport *)iface;
394     POP3RESPONSE response;
395     HRESULT hr;
396 
397     TRACE("\n");
398 
399     hr = POP3Transport_ParseResponse(This, pBuffer, &response);
400     if (FAILED(hr))
401     {
402         /* FIXME: handle error */
403         return;
404     }
405 
406     IPOP3Callback_OnResponse((IPOP3Callback *)This->InetTransport.pCallback, &response);
407 }
408 
409 static void POP3Transport_CallbackRecvNOOPResp(IInternetTransport *iface, char *pBuffer, int cbBuffer)
410 {
411     POP3Transport *This = (POP3Transport *)iface;
412 
413     TRACE("\n");
414     InternetTransport_ReadLine(&This->InetTransport, POP3Transport_CallbackProcessNOOPResp);
415 }
416 
417 static void POP3Transport_CallbackProcessRSETResp(IInternetTransport *iface, char *pBuffer, int cbBuffer)
418 {
419     POP3Transport *This = (POP3Transport *)iface;
420     POP3RESPONSE response;
421     HRESULT hr;
422 
423     TRACE("\n");
424 
425     hr = POP3Transport_ParseResponse(This, pBuffer, &response);
426     if (FAILED(hr))
427     {
428         /* FIXME: handle error */
429         return;
430     }
431 
432     IPOP3Callback_OnResponse((IPOP3Callback *)This->InetTransport.pCallback, &response);
433 }
434 
435 static void POP3Transport_CallbackRecvRSETResp(IInternetTransport *iface, char *pBuffer, int cbBuffer)
436 {
437     POP3Transport *This = (POP3Transport *)iface;
438 
439     TRACE("\n");
440     InternetTransport_ReadLine(&This->InetTransport, POP3Transport_CallbackProcessRSETResp);
441 }
442 
443 static void POP3Transport_CallbackProcessRETRResp(IInternetTransport *iface, char *pBuffer, int cbBuffer)
444 {
445     POP3Transport *This = (POP3Transport *)iface;
446     POP3RESPONSE response;
447     HRESULT hr;
448 
449     TRACE("\n");
450 
451     hr = POP3Transport_ParseResponse(This, pBuffer, &response);
452     if (FAILED(hr))
453     {
454         /* FIXME: handle error */
455         return;
456     }
457 
458     IPOP3Callback_OnResponse((IPOP3Callback *)This->InetTransport.pCallback, &response);
459 
460     if (!response.fDone)
461     {
462         InternetTransport_ReadLine(&This->InetTransport, POP3Transport_CallbackProcessRETRResp);
463         return;
464     }
465 
466     IPOP3Callback_OnResponse((IPOP3Callback *)This->InetTransport.pCallback, &response);
467 }
468 
469 static void POP3Transport_CallbackRecvRETRResp(IInternetTransport *iface, char *pBuffer, int cbBuffer)
470 {
471     POP3Transport *This = (POP3Transport *)iface;
472 
473     TRACE("\n");
474     InternetTransport_ReadLine(&This->InetTransport, POP3Transport_CallbackProcessRETRResp);
475 }
476 
477 static void POP3Transport_CallbackProcessTOPResp(IInternetTransport *iface, char *pBuffer, int cbBuffer)
478 {
479     POP3Transport *This = (POP3Transport *)iface;
480     POP3RESPONSE response;
481     HRESULT hr;
482 
483     TRACE("\n");
484 
485     hr = POP3Transport_ParseResponse(This, pBuffer, &response);
486     if (FAILED(hr))
487     {
488         /* FIXME: handle error */
489         return;
490     }
491 
492     IPOP3Callback_OnResponse((IPOP3Callback *)This->InetTransport.pCallback, &response);
493 
494     if (!response.fDone)
495     {
496         InternetTransport_ReadLine(&This->InetTransport, POP3Transport_CallbackProcessTOPResp);
497         return;
498     }
499 
500     IPOP3Callback_OnResponse((IPOP3Callback *)This->InetTransport.pCallback, &response);
501 }
502 
503 static void POP3Transport_CallbackRecvTOPResp(IInternetTransport *iface, char *pBuffer, int cbBuffer)
504 {
505     POP3Transport *This = (POP3Transport *)iface;
506 
507     TRACE("\n");
508     InternetTransport_ReadLine(&This->InetTransport, POP3Transport_CallbackProcessTOPResp);
509 }
510 
511 static void POP3Transport_CallbackProcessLISTResp(IInternetTransport *iface, char *pBuffer, int cbBuffer)
512 {
513     POP3Transport *This = (POP3Transport *)iface;
514     POP3RESPONSE response;
515     HRESULT hr;
516 
517     TRACE("\n");
518 
519     hr = POP3Transport_ParseResponse(This, pBuffer, &response);
520     if (FAILED(hr))
521     {
522         /* FIXME: handle error */
523         return;
524     }
525 
526     IPOP3Callback_OnResponse((IPOP3Callback *)This->InetTransport.pCallback, &response);
527 
528     if (!response.fDone)
529     {
530         InternetTransport_ReadLine(&This->InetTransport, POP3Transport_CallbackProcessLISTResp);
531         return;
532     }
533 
534     IPOP3Callback_OnResponse((IPOP3Callback *)This->InetTransport.pCallback, &response);
535 }
536 
537 static void POP3Transport_CallbackRecvLISTResp(IInternetTransport *iface, char *pBuffer, int cbBuffer)
538 {
539     POP3Transport *This = (POP3Transport *)iface;
540 
541     TRACE("\n");
542     InternetTransport_ReadLine(&This->InetTransport, POP3Transport_CallbackProcessLISTResp);
543 }
544 
545 static void POP3Transport_CallbackProcessUIDLResp(IInternetTransport *iface, char *pBuffer, int cbBuffer)
546 {
547     POP3Transport *This = (POP3Transport *)iface;
548     POP3RESPONSE response;
549     HRESULT hr;
550 
551     TRACE("\n");
552 
553     hr = POP3Transport_ParseResponse(This, pBuffer, &response);
554     if (FAILED(hr))
555     {
556         /* FIXME: handle error */
557         return;
558     }
559 
560     IPOP3Callback_OnResponse((IPOP3Callback *)This->InetTransport.pCallback, &response);
561 
562     if (!response.fDone)
563     {
564         InternetTransport_ReadLine(&This->InetTransport, POP3Transport_CallbackProcessUIDLResp);
565         return;
566     }
567 
568     IPOP3Callback_OnResponse((IPOP3Callback *)This->InetTransport.pCallback, &response);
569 }
570 
571 static void POP3Transport_CallbackRecvUIDLResp(IInternetTransport *iface, char *pBuffer, int cbBuffer)
572 {
573     POP3Transport *This = (POP3Transport *)iface;
574 
575     TRACE("\n");
576     InternetTransport_ReadLine(&This->InetTransport, POP3Transport_CallbackProcessUIDLResp);
577 }
578 
579 static void POP3Transport_CallbackProcessSTATResp(IInternetTransport *iface, char *pBuffer, int cbBuffer)
580 {
581     POP3Transport *This = (POP3Transport *)iface;
582     POP3RESPONSE response;
583     HRESULT hr;
584 
585     TRACE("\n");
586 
587     hr = POP3Transport_ParseResponse(This, pBuffer, &response);
588     if (FAILED(hr))
589     {
590         /* FIXME: handle error */
591         return;
592     }
593 
594     IPOP3Callback_OnResponse((IPOP3Callback *)This->InetTransport.pCallback, &response);
595 }
596 
597 static void POP3Transport_CallbackRecvSTATResp(IInternetTransport *iface, char *pBuffer, int cbBuffer)
598 {
599     POP3Transport *This = (POP3Transport *)iface;
600 
601     TRACE("\n");
602     InternetTransport_ReadLine(&This->InetTransport, POP3Transport_CallbackProcessSTATResp);
603 }
604 
605 static void POP3Transport_CallbackProcessPASSResp(IInternetTransport *iface, char *pBuffer, int cbBuffer)
606 {
607     POP3Transport *This = (POP3Transport *)iface;
608     POP3RESPONSE response;
609     HRESULT hr;
610 
611     TRACE("\n");
612 
613     hr = POP3Transport_ParseResponse(This, pBuffer, &response);
614     if (FAILED(hr))
615     {
616         /* FIXME: handle error */
617         return;
618     }
619 
620     InternetTransport_ChangeStatus(&This->InetTransport, IXP_AUTHORIZED);
621     InternetTransport_ChangeStatus(&This->InetTransport, IXP_CONNECTED);
622 
623     IPOP3Callback_OnResponse((IPOP3Callback *)This->InetTransport.pCallback, &response);
624 }
625 
626 static void POP3Transport_CallbackRecvPASSResp(IInternetTransport *iface, char *pBuffer, int cbBuffer)
627 {
628     POP3Transport *This = (POP3Transport *)iface;
629 
630     TRACE("\n");
631     InternetTransport_ReadLine(&This->InetTransport, POP3Transport_CallbackProcessPASSResp);
632 }
633 
634 static void POP3Transport_CallbackProcessUSERResp(IInternetTransport *iface, char *pBuffer, int cbBuffer)
635 {
636     static const char pass[] = "PASS ";
637     POP3Transport *This = (POP3Transport *)iface;
638     POP3RESPONSE response;
639     char *command;
640     int len;
641     HRESULT hr;
642 
643     TRACE("\n");
644 
645     hr = POP3Transport_ParseResponse(This, pBuffer, &response);
646     if (FAILED(hr))
647     {
648         /* FIXME: handle error */
649         return;
650     }
651 
652     IPOP3Callback_OnResponse((IPOP3Callback *)This->InetTransport.pCallback, &response);
653 
654     len = sizeof(pass) + strlen(This->InetTransport.ServerInfo.szPassword) + 2; /* "\r\n" */
655     command = HeapAlloc(GetProcessHeap(), 0, len);
656 
657     strcpy(command, pass);
658     strcat(command, This->InetTransport.ServerInfo.szPassword);
659     strcat(command, "\r\n");
660 
661     init_parser(This, POP3_PASS);
662 
663     InternetTransport_DoCommand(&This->InetTransport, command, POP3Transport_CallbackRecvPASSResp);
664     HeapFree(GetProcessHeap(), 0, command);
665 }
666 
667 static void POP3Transport_CallbackRecvUSERResp(IInternetTransport *iface, char *pBuffer, int cbBuffer)
668 {
669     POP3Transport *This = (POP3Transport *)iface;
670 
671     TRACE("\n");
672     InternetTransport_ReadLine(&This->InetTransport, POP3Transport_CallbackProcessUSERResp);
673 }
674 
675 static void POP3Transport_CallbackSendUSERCmd(IInternetTransport *iface, char *pBuffer, int cbBuffer)
676 {
677     static const char user[] = "USER ";
678     POP3Transport *This = (POP3Transport *)iface;
679     char *command;
680     int len;
681 
682     TRACE("\n");
683 
684     len = sizeof(user) + strlen(This->InetTransport.ServerInfo.szUserName) + 2; /* "\r\n" */
685     command = HeapAlloc(GetProcessHeap(), 0, len);
686 
687     strcpy(command, user);
688     strcat(command, This->InetTransport.ServerInfo.szUserName);
689     strcat(command, "\r\n");
690     InternetTransport_DoCommand(&This->InetTransport, command, POP3Transport_CallbackRecvUSERResp);
691 
692     HeapFree(GetProcessHeap(), 0, command);
693 }
694 
695 static void POP3Transport_CallbackProcessQUITResponse(IInternetTransport *iface, char *pBuffer, int cbBuffer)
696 {
697     POP3Transport *This = (POP3Transport *)iface;
698     POP3RESPONSE response;
699     HRESULT hr;
700 
701     TRACE("%s\n", debugstr_an(pBuffer, cbBuffer));
702 
703     hr = POP3Transport_ParseResponse(This, pBuffer, &response);
704     if (FAILED(hr))
705     {
706         /* FIXME: handle error */
707         return;
708     }
709 
710     IPOP3Callback_OnResponse((IPOP3Callback *)This->InetTransport.pCallback, &response);
711     InternetTransport_DropConnection(&This->InetTransport);
712 }
713 
714 static void POP3Transport_CallbackRecvQUITResp(IInternetTransport *iface, char *pBuffer, int cbBuffer)
715 {
716     POP3Transport *This = (POP3Transport *)iface;
717 
718     TRACE("\n");
719     InternetTransport_ReadLine(&This->InetTransport, POP3Transport_CallbackProcessQUITResponse);
720 }
721 
722 static HRESULT WINAPI POP3Transport_QueryInterface(IPOP3Transport *iface, REFIID riid, void **ppv)
723 {
724     TRACE("(%s, %p)\n", debugstr_guid(riid), ppv);
725 
726     if (IsEqualIID(riid, &IID_IUnknown) ||
727         IsEqualIID(riid, &IID_IInternetTransport) ||
728         IsEqualIID(riid, &IID_IPOP3Transport))
729     {
730         *ppv = iface;
731         IPOP3Transport_AddRef(iface);
732         return S_OK;
733     }
734     *ppv = NULL;
735     FIXME("no interface for %s\n", debugstr_guid(riid));
736     return E_NOINTERFACE;
737 }
738 
739 static ULONG WINAPI POP3Transport_AddRef(IPOP3Transport *iface)
740 {
741     POP3Transport *This = (POP3Transport *)iface;
742     return InterlockedIncrement((LONG *)&This->refs);
743 }
744 
745 static ULONG WINAPI POP3Transport_Release(IPOP3Transport *iface)
746 {
747     POP3Transport *This = (POP3Transport *)iface;
748     ULONG refs = InterlockedDecrement((LONG *)&This->refs);
749     if (!refs)
750     {
751         TRACE("destroying %p\n", This);
752         if (This->InetTransport.Status != IXP_DISCONNECTED)
753             InternetTransport_DropConnection(&This->InetTransport);
754         if (This->InetTransport.pCallback) ITransportCallback_Release(This->InetTransport.pCallback);
755         HeapFree(GetProcessHeap(), 0, This);
756     }
757     return refs;
758 }
759 
760 static HRESULT WINAPI POP3Transport_GetServerInfo(IPOP3Transport *iface,
761     LPINETSERVER pInetServer)
762 {
763     POP3Transport *This = (POP3Transport *)iface;
764 
765     TRACE("(%p)\n", pInetServer);
766     return InternetTransport_GetServerInfo(&This->InetTransport, pInetServer);
767 }
768 
769 static IXPTYPE WINAPI POP3Transport_GetIXPType(IPOP3Transport *iface)
770 {
771     TRACE("()\n");
772     return IXP_POP3;
773 }
774 
775 static HRESULT WINAPI POP3Transport_IsState(IPOP3Transport *iface, IXPISSTATE isstate)
776 {
777     FIXME("(%u)\n", isstate);
778     return E_NOTIMPL;
779 }
780 
781 static HRESULT WINAPI POP3Transport_InetServerFromAccount(
782     IPOP3Transport *iface, IImnAccount *pAccount, LPINETSERVER pInetServer)
783 {
784     POP3Transport *This = (POP3Transport *)iface;
785 
786     TRACE("(%p, %p)\n", pAccount, pInetServer);
787     return InternetTransport_InetServerFromAccount(&This->InetTransport, pAccount, pInetServer);
788 }
789 
790 static HRESULT WINAPI POP3Transport_Connect(IPOP3Transport *iface,
791     LPINETSERVER pInetServer, boolean fAuthenticate, boolean fCommandLogging)
792 {
793     POP3Transport *This = (POP3Transport *)iface;
794     HRESULT hr;
795 
796     TRACE("(%p, %s, %s)\n", pInetServer, fAuthenticate ? "TRUE" : "FALSE", fCommandLogging ? "TRUE" : "FALSE");
797 
798     hr = InternetTransport_Connect(&This->InetTransport, pInetServer, fAuthenticate, fCommandLogging);
799     if (FAILED(hr))
800         return hr;
801 
802     init_parser(This, POP3_USER);
803     return InternetTransport_ReadLine(&This->InetTransport, POP3Transport_CallbackSendUSERCmd);
804 }
805 
806 static HRESULT WINAPI POP3Transport_HandsOffCallback(IPOP3Transport *iface)
807 {
808     POP3Transport *This = (POP3Transport *)iface;
809 
810     TRACE("()\n");
811     return InternetTransport_HandsOffCallback(&This->InetTransport);
812 }
813 
814 static HRESULT WINAPI POP3Transport_Disconnect(IPOP3Transport *iface)
815 {
816     TRACE("()\n");
817     return IPOP3Transport_CommandQUIT(iface);
818 }
819 
820 static HRESULT WINAPI POP3Transport_DropConnection(IPOP3Transport *iface)
821 {
822     POP3Transport *This = (POP3Transport *)iface;
823 
824     TRACE("()\n");
825     return InternetTransport_DropConnection(&This->InetTransport);
826 }
827 
828 static HRESULT WINAPI POP3Transport_GetStatus(IPOP3Transport *iface,
829     IXPSTATUS *pCurrentStatus)
830 {
831     POP3Transport *This = (POP3Transport *)iface;
832 
833     TRACE("()\n");
834     return InternetTransport_GetStatus(&This->InetTransport, pCurrentStatus);
835 }
836 
837 static HRESULT WINAPI POP3Transport_InitNew(IPOP3Transport *iface,
838     LPSTR pszLogFilePath, IPOP3Callback *pCallback)
839 {
840     POP3Transport *This = (POP3Transport *)iface;
841 
842     TRACE("(%s, %p)\n", debugstr_a(pszLogFilePath), pCallback);
843 
844     if (!pCallback)
845         return E_INVALIDARG;
846 
847     if (pszLogFilePath)
848         FIXME("not using log file of %s, use Wine debug logging instead\n",
849             debugstr_a(pszLogFilePath));
850 
851     IPOP3Callback_AddRef(pCallback);
852     This->InetTransport.pCallback = (ITransportCallback *)pCallback;
853     This->InetTransport.fInitialised = TRUE;
854 
855     return S_OK;
856 }
857 
858 static HRESULT WINAPI POP3Transport_MarkItem(IPOP3Transport *iface, POP3MARKTYPE marktype,
859     DWORD dwPopId, boolean fMarked)
860 {
861     FIXME("(%u, %u, %d)\n", marktype, dwPopId, fMarked);
862     return E_NOTIMPL;
863 }
864 
865 static HRESULT WINAPI POP3Transport_CommandAUTH(IPOP3Transport *iface, LPSTR pszAuthType)
866 {
867     FIXME("(%s)\n", pszAuthType);
868     return E_NOTIMPL;
869 }
870 
871 static HRESULT WINAPI POP3Transport_CommandUSER(IPOP3Transport *iface, LPSTR username)
872 {
873     static const char user[] = "USER ";
874     POP3Transport *This = (POP3Transport *)iface;
875     char *command;
876     int len;
877 
878     TRACE("(%s)\n", username);
879 
880     len = sizeof(user) + strlen(username) + 2; /* "\r\n" */
881     if (!(command = HeapAlloc(GetProcessHeap(), 0, len))) return S_FALSE;
882 
883     strcpy(command, user);
884     strcat(command, username);
885     strcat(command, "\r\n");
886 
887     init_parser(This, POP3_USER);
888     InternetTransport_DoCommand(&This->InetTransport, command, POP3Transport_CallbackRecvUSERResp);
889 
890     HeapFree(GetProcessHeap(), 0, command);
891     return S_OK;
892 }
893 
894 static HRESULT WINAPI POP3Transport_CommandPASS(IPOP3Transport *iface, LPSTR password)
895 {
896     static const char pass[] = "PASS ";
897     POP3Transport *This = (POP3Transport *)iface;
898     char *command;
899     int len;
900 
901     TRACE("(%p)\n", password);
902 
903     len = sizeof(pass) + strlen(password) + 2; /* "\r\n" */
904     if (!(command = HeapAlloc(GetProcessHeap(), 0, len))) return S_FALSE;
905 
906     strcpy(command, pass);
907     strcat(command, password);
908     strcat(command, "\r\n");
909 
910     init_parser(This, POP3_PASS);
911     InternetTransport_DoCommand(&This->InetTransport, command, POP3Transport_CallbackRecvPASSResp);
912 
913     HeapFree(GetProcessHeap(), 0, command);
914     return S_OK;
915 }
916 
917 static HRESULT WINAPI POP3Transport_CommandLIST(
918     IPOP3Transport *iface, POP3CMDTYPE cmdtype, DWORD dwPopId)
919 {
920     static const char list[] = "LIST %u\r\n";
921     static char list_all[] = "LIST\r\n";
922     POP3Transport *This = (POP3Transport *)iface;
923     char *command;
924     int len;
925 
926     TRACE("(%u, %u)\n", cmdtype, dwPopId);
927 
928     if (dwPopId)
929     {
930         len = sizeof(list) + 10 + 2; /* "4294967296" + "\r\n" */
931         if (!(command = HeapAlloc(GetProcessHeap(), 0, len))) return S_FALSE;
932         sprintf(command, list, dwPopId);
933     }
934     else command = list_all;
935 
936     init_parser(This, POP3_LIST);
937     This->type = 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 const 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);
960     This->type = cmdtype;
961     InternetTransport_DoCommand(&This->InetTransport, command, POP3Transport_CallbackRecvTOPResp);
962 
963     HeapFree(GetProcessHeap(), 0, command);
964     return S_OK;
965 }
966 
967 static HRESULT WINAPI POP3Transport_CommandQUIT(IPOP3Transport *iface)
968 {
969     static const char command[] = "QUIT\r\n";
970     POP3Transport *This = (POP3Transport *)iface;
971 
972     TRACE("()\n");
973 
974     InternetTransport_ChangeStatus(&This->InetTransport, IXP_DISCONNECTING);
975 
976     init_parser(This, POP3_QUIT);
977     return InternetTransport_DoCommand(&This->InetTransport, command, POP3Transport_CallbackRecvQUITResp);
978 }
979 
980 static HRESULT WINAPI POP3Transport_CommandSTAT(IPOP3Transport *iface)
981 {
982     static const char stat[] = "STAT\r\n";
983     POP3Transport *This = (POP3Transport *)iface;
984 
985     TRACE("\n");
986 
987     init_parser(This, POP3_STAT);
988     InternetTransport_DoCommand(&This->InetTransport, stat, POP3Transport_CallbackRecvSTATResp);
989     return S_OK;
990 }
991 
992 static HRESULT WINAPI POP3Transport_CommandNOOP(IPOP3Transport *iface)
993 {
994     static const char noop[] = "NOOP\r\n";
995     POP3Transport *This = (POP3Transport *)iface;
996 
997     TRACE("\n");
998 
999     init_parser(This, POP3_NOOP);
1000     InternetTransport_DoCommand(&This->InetTransport, noop, POP3Transport_CallbackRecvNOOPResp);
1001     return S_OK;
1002 }
1003 
1004 static HRESULT WINAPI POP3Transport_CommandRSET(IPOP3Transport *iface)
1005 {
1006     static const char rset[] = "RSET\r\n";
1007     POP3Transport *This = (POP3Transport *)iface;
1008 
1009     TRACE("\n");
1010 
1011     init_parser(This, POP3_RSET);
1012     InternetTransport_DoCommand(&This->InetTransport, rset, POP3Transport_CallbackRecvRSETResp);
1013     return S_OK;
1014 }
1015 
1016 static HRESULT WINAPI POP3Transport_CommandUIDL(
1017     IPOP3Transport *iface, POP3CMDTYPE cmdtype, DWORD dwPopId)
1018 {
1019     static const char uidl[] = "UIDL %u\r\n";
1020     static char uidl_all[] = "UIDL\r\n";
1021     POP3Transport *This = (POP3Transport *)iface;
1022     char *command;
1023     int len;
1024 
1025     TRACE("(%u, %u)\n", cmdtype, dwPopId);
1026 
1027     if (dwPopId)
1028     {
1029         len = sizeof(uidl) + 10 + 2; /* "4294967296" + "\r\n" */
1030         if (!(command = HeapAlloc(GetProcessHeap(), 0, len))) return S_FALSE;
1031         sprintf(command, uidl, dwPopId);
1032     }
1033     else command = uidl_all;
1034 
1035     init_parser(This, POP3_UIDL);
1036     This->type = cmdtype;
1037     InternetTransport_DoCommand(&This->InetTransport, command, POP3Transport_CallbackRecvUIDLResp);
1038 
1039     if (dwPopId) HeapFree(GetProcessHeap(), 0, command);
1040     return S_OK;
1041 }
1042 
1043 static HRESULT WINAPI POP3Transport_CommandDELE(
1044     IPOP3Transport *iface, POP3CMDTYPE cmdtype, DWORD dwPopId)
1045 {
1046     static const char dele[] = "DELE %u\r\n";
1047     POP3Transport *This = (POP3Transport *)iface;
1048     char *command;
1049     int len;
1050 
1051     TRACE("(%u, %u)\n", cmdtype, dwPopId);
1052 
1053     len = sizeof(dele) + 10 + 2; /* "4294967296" + "\r\n" */
1054     if (!(command = HeapAlloc(GetProcessHeap(), 0, len))) return S_FALSE;
1055     sprintf(command, dele, dwPopId);
1056 
1057     init_parser(This, POP3_DELE);
1058     This->type = cmdtype;
1059     InternetTransport_DoCommand(&This->InetTransport, command, POP3Transport_CallbackRecvDELEResp);
1060 
1061     HeapFree(GetProcessHeap(), 0, command);
1062     return S_OK;
1063 }
1064 
1065 static HRESULT WINAPI POP3Transport_CommandRETR(
1066     IPOP3Transport *iface, POP3CMDTYPE cmdtype, DWORD dwPopId)
1067 {
1068     static const char retr[] = "RETR %u\r\n";
1069     POP3Transport *This = (POP3Transport *)iface;
1070     char *command;
1071     int len;
1072 
1073     TRACE("(%u, %u)\n", cmdtype, dwPopId);
1074 
1075     len = sizeof(retr) + 10 + 2; /* "4294967296" + "\r\n" */
1076     if (!(command = HeapAlloc(GetProcessHeap(), 0, len))) return S_FALSE;
1077     sprintf(command, retr, dwPopId);
1078 
1079     init_parser(This, POP3_RETR);
1080     This->type = cmdtype;
1081     InternetTransport_DoCommand(&This->InetTransport, command, POP3Transport_CallbackRecvRETRResp);
1082 
1083     HeapFree(GetProcessHeap(), 0, command);
1084     return S_OK;
1085 }
1086 
1087 static const IPOP3TransportVtbl POP3TransportVtbl =
1088 {
1089     POP3Transport_QueryInterface,
1090     POP3Transport_AddRef,
1091     POP3Transport_Release,
1092     POP3Transport_GetServerInfo,
1093     POP3Transport_GetIXPType,
1094     POP3Transport_IsState,
1095     POP3Transport_InetServerFromAccount,
1096     POP3Transport_Connect,
1097     POP3Transport_HandsOffCallback,
1098     POP3Transport_Disconnect,
1099     POP3Transport_DropConnection,
1100     POP3Transport_GetStatus,
1101     POP3Transport_InitNew,
1102     POP3Transport_MarkItem,
1103     POP3Transport_CommandAUTH,
1104     POP3Transport_CommandUSER,
1105     POP3Transport_CommandPASS,
1106     POP3Transport_CommandLIST,
1107     POP3Transport_CommandTOP,
1108     POP3Transport_CommandQUIT,
1109     POP3Transport_CommandSTAT,
1110     POP3Transport_CommandNOOP,
1111     POP3Transport_CommandRSET,
1112     POP3Transport_CommandUIDL,
1113     POP3Transport_CommandDELE,
1114     POP3Transport_CommandRETR
1115 };
1116 
1117 HRESULT WINAPI CreatePOP3Transport(IPOP3Transport **ppTransport)
1118 {
1119     HRESULT hr;
1120     POP3Transport *This = HeapAlloc(GetProcessHeap(), 0, sizeof(*This));
1121     if (!This)
1122         return E_OUTOFMEMORY;
1123 
1124     This->InetTransport.u.vtblPOP3 = &POP3TransportVtbl;
1125     This->refs = 0;
1126     hr = InternetTransport_Init(&This->InetTransport);
1127     if (FAILED(hr))
1128     {
1129         HeapFree(GetProcessHeap(), 0, This);
1130         return hr;
1131     }
1132 
1133     *ppTransport = (IPOP3Transport *)&This->InetTransport.u.vtblPOP3;
1134     IPOP3Transport_AddRef(*ppTransport);
1135 
1136     return S_OK;
1137 }
1138 
1139 static HRESULT WINAPI POP3TransportCF_QueryInterface(LPCLASSFACTORY iface,
1140     REFIID riid, LPVOID *ppv)
1141 {
1142     *ppv = NULL;
1143     if (IsEqualIID(riid, &IID_IUnknown) || IsEqualIID(riid, &IID_IClassFactory))
1144     {
1145         *ppv = iface;
1146         IClassFactory_AddRef(iface);
1147         return S_OK;
1148     }
1149     return E_NOINTERFACE;
1150 }
1151 
1152 static ULONG WINAPI POP3TransportCF_AddRef(LPCLASSFACTORY iface)
1153 {
1154     return 2; /* non-heap based object */
1155 }
1156 
1157 static ULONG WINAPI POP3TransportCF_Release(LPCLASSFACTORY iface)
1158 {
1159     return 1; /* non-heap based object */
1160 }
1161 
1162 static HRESULT WINAPI POP3TransportCF_CreateInstance(LPCLASSFACTORY iface,
1163     LPUNKNOWN pUnk, REFIID riid, LPVOID *ppv)
1164 {
1165     HRESULT hr;
1166     IPOP3Transport *pPop3Transport;
1167 
1168     TRACE("(%p, %s, %p)\n", pUnk, debugstr_guid(riid), ppv);
1169 
1170     *ppv = NULL;
1171 
1172     if (pUnk)
1173         return CLASS_E_NOAGGREGATION;
1174 
1175     hr = CreatePOP3Transport(&pPop3Transport);
1176     if (FAILED(hr))
1177         return hr;
1178 
1179     hr = IPOP3Transport_QueryInterface(pPop3Transport, riid, ppv);
1180     IPOP3Transport_Release(pPop3Transport);
1181 
1182     return hr;
1183 }
1184 
1185 static HRESULT WINAPI POP3TransportCF_LockServer(LPCLASSFACTORY iface, BOOL fLock)
1186 {
1187     FIXME("(%d)\n",fLock);
1188     return S_OK;
1189 }
1190 
1191 static const IClassFactoryVtbl POP3TransportCFVtbl =
1192 {
1193     POP3TransportCF_QueryInterface,
1194     POP3TransportCF_AddRef,
1195     POP3TransportCF_Release,
1196     POP3TransportCF_CreateInstance,
1197     POP3TransportCF_LockServer
1198 };
1199 static const IClassFactoryVtbl *POP3TransportCF = &POP3TransportCFVtbl;
1200 
1201 HRESULT POP3TransportCF_Create(REFIID riid, LPVOID *ppv)
1202 {
1203     return IClassFactory_QueryInterface((IClassFactory *)&POP3TransportCF, riid, ppv);
1204 }
1205