1 /***************************************************************************
3 * Project ___| | | | _ \| |
5 * | (__| |_| | _ <| |___
6 * \___|\___/|_| \_\_____|
8 * Copyright (C) 1998 - 2010, Daniel Stenberg, <daniel@haxx.se>, et al.
10 * This software is licensed as described in the file COPYING, which
11 * you should have received as part of this distribution. The terms
12 * are also available at http://curl.haxx.se/docs/copyright.html.
14 * You may opt to use, copy, modify, merge, publish, distribute and/or sell
15 * copies of the Software, and permit persons to whom the Software is
16 * furnished to do so, under the terms of the COPYING file.
18 * This software is distributed on an "AS IS" basis, WITHOUT WARRANTY OF ANY
19 * KIND, either express or implied.
21 * RFC1939 POP3 protocol
22 * RFC2384 POP URL Scheme
23 * RFC2595 Using TLS with IMAP, POP3 and ACAP
25 ***************************************************************************/
29 #ifndef CURL_DISABLE_POP3
40 #ifdef HAVE_SYS_SOCKET_H
41 #include <sys/socket.h>
43 #ifdef HAVE_NETINET_IN_H
44 #include <netinet/in.h>
46 #ifdef HAVE_ARPA_INET_H
47 #include <arpa/inet.h>
50 #include <sys/utsname.h>
60 #if (defined(NETWARE) && defined(__NOVELL_LIBC__))
62 #define in_addr_t unsigned long
65 #include <curl/curl.h>
68 #include "easyif.h" /* for Curl_convert_... prototypes */
75 #include "http.h" /* for HTTP proxy tunnel stuff */
79 #include "strtoofft.h"
88 #include "strtoofft.h"
90 #define _MPRINTF_REPLACE /* use our functions only */
91 #include <curl/mprintf.h>
93 #include "curl_memory.h"
94 /* The last #include file should be: */
97 /* Local API functions */
98 static CURLcode pop3_parse_url_path(struct connectdata *conn);
99 static CURLcode pop3_regular_transfer(struct connectdata *conn, bool *done);
100 static CURLcode pop3_do(struct connectdata *conn, bool *done);
101 static CURLcode pop3_done(struct connectdata *conn,
102 CURLcode, bool premature);
103 static CURLcode pop3_connect(struct connectdata *conn, bool *done);
104 static CURLcode pop3_disconnect(struct connectdata *conn, bool dead_connection);
105 static CURLcode pop3_multi_statemach(struct connectdata *conn, bool *done);
106 static int pop3_getsock(struct connectdata *conn,
107 curl_socket_t *socks,
109 static CURLcode pop3_doing(struct connectdata *conn,
111 static CURLcode pop3_setup_connection(struct connectdata * conn);
114 * POP3 protocol handler.
117 const struct Curl_handler Curl_handler_pop3 = {
119 pop3_setup_connection, /* setup_connection */
121 pop3_done, /* done */
122 ZERO_NULL, /* do_more */
123 pop3_connect, /* connect_it */
124 pop3_multi_statemach, /* connecting */
125 pop3_doing, /* doing */
126 pop3_getsock, /* proto_getsock */
127 pop3_getsock, /* doing_getsock */
128 ZERO_NULL, /* perform_getsock */
129 pop3_disconnect, /* disconnect */
130 PORT_POP3, /* defport */
131 PROT_POP3 /* protocol */
137 * POP3S protocol handler.
140 const struct Curl_handler Curl_handler_pop3s = {
141 "POP3S", /* scheme */
142 pop3_setup_connection, /* setup_connection */
144 pop3_done, /* done */
145 ZERO_NULL, /* do_more */
146 pop3_connect, /* connect_it */
147 pop3_multi_statemach, /* connecting */
148 pop3_doing, /* doing */
149 pop3_getsock, /* proto_getsock */
150 pop3_getsock, /* doing_getsock */
151 ZERO_NULL, /* perform_getsock */
152 pop3_disconnect, /* disconnect */
153 PORT_POP3S, /* defport */
154 PROT_POP3 | PROT_POP3S | PROT_SSL /* protocol */
158 #ifndef CURL_DISABLE_HTTP
160 * HTTP-proxyed POP3 protocol handler.
163 static const struct Curl_handler Curl_handler_pop3_proxy = {
165 ZERO_NULL, /* setup_connection */
166 Curl_http, /* do_it */
167 Curl_http_done, /* done */
168 ZERO_NULL, /* do_more */
169 ZERO_NULL, /* connect_it */
170 ZERO_NULL, /* connecting */
171 ZERO_NULL, /* doing */
172 ZERO_NULL, /* proto_getsock */
173 ZERO_NULL, /* doing_getsock */
174 ZERO_NULL, /* perform_getsock */
175 ZERO_NULL, /* disconnect */
176 PORT_POP3, /* defport */
177 PROT_HTTP /* protocol */
183 * HTTP-proxyed POP3S protocol handler.
186 static const struct Curl_handler Curl_handler_pop3s_proxy = {
187 "POP3S", /* scheme */
188 ZERO_NULL, /* setup_connection */
189 Curl_http, /* do_it */
190 Curl_http_done, /* done */
191 ZERO_NULL, /* do_more */
192 ZERO_NULL, /* connect_it */
193 ZERO_NULL, /* connecting */
194 ZERO_NULL, /* doing */
195 ZERO_NULL, /* proto_getsock */
196 ZERO_NULL, /* doing_getsock */
197 ZERO_NULL, /* perform_getsock */
198 ZERO_NULL, /* disconnect */
199 PORT_POP3S, /* defport */
200 PROT_HTTP /* protocol */
206 /* function that checks for a pop3 status code at the start of the given
208 static int pop3_endofresp(struct pingpong *pp,
211 char *line = pp->linestart_resp;
212 size_t len = pp->nread_resp;
214 if( ((len >= 3) && !memcmp("+OK", line, 3)) ||
215 ((len >= 4) && !memcmp("-ERR", line, 4)) ) {
216 *resp=line[1]; /* O or E */
220 return FALSE; /* nothing for us */
223 /* This is the ONLY way to change POP3 state! */
224 static void state(struct connectdata *conn,
227 #if defined(DEBUGBUILD) && !defined(CURL_DISABLE_VERBOSE_STRINGS)
228 /* for debug purposes */
229 static const char * const names[]={
241 struct pop3_conn *pop3c = &conn->proto.pop3c;
242 #if defined(DEBUGBUILD) && !defined(CURL_DISABLE_VERBOSE_STRINGS)
243 if(pop3c->state != newstate)
244 infof(conn->data, "POP3 %p state change from %s to %s\n",
245 pop3c, names[pop3c->state], names[newstate]);
247 pop3c->state = newstate;
250 static CURLcode pop3_state_user(struct connectdata *conn)
253 struct FTP *pop3 = conn->data->state.proto.pop3;
256 result = Curl_pp_sendf(&conn->proto.pop3c.pp, "USER %s",
257 pop3->user?pop3->user:"");
261 state(conn, POP3_USER);
266 /* For the POP3 "protocol connect" and "doing" phases only */
267 static int pop3_getsock(struct connectdata *conn,
268 curl_socket_t *socks,
271 return Curl_pp_getsock(&conn->proto.pop3c.pp, socks, numsocks);
274 /* for STARTTLS responses */
275 static CURLcode pop3_state_starttls_resp(struct connectdata *conn,
279 CURLcode result = CURLE_OK;
280 struct SessionHandle *data = conn->data;
281 (void)instate; /* no use for this yet */
283 if(pop3code != 'O') {
284 failf(data, "STARTTLS denied. %c", pop3code);
285 result = CURLE_LOGIN_DENIED;
288 /* Curl_ssl_connect is BLOCKING */
289 result = Curl_ssl_connect(conn, FIRSTSOCKET);
290 if(CURLE_OK == result) {
291 conn->protocol |= PROT_POP3S;
292 result = pop3_state_user(conn);
295 state(conn, POP3_STOP);
299 /* for USER responses */
300 static CURLcode pop3_state_user_resp(struct connectdata *conn,
304 CURLcode result = CURLE_OK;
305 struct SessionHandle *data = conn->data;
306 struct FTP *pop3 = data->state.proto.pop3;
308 (void)instate; /* no use for this yet */
310 if(pop3code != 'O') {
311 failf(data, "Access denied. %c", pop3code);
312 result = CURLE_LOGIN_DENIED;
316 result = Curl_pp_sendf(&conn->proto.pop3c.pp, "PASS %s",
317 pop3->passwd?pop3->passwd:"");
321 state(conn, POP3_PASS);
325 /* for PASS responses */
326 static CURLcode pop3_state_pass_resp(struct connectdata *conn,
330 CURLcode result = CURLE_OK;
331 struct SessionHandle *data = conn->data;
332 (void)instate; /* no use for this yet */
334 if(pop3code != 'O') {
335 failf(data, "Access denied. %c", pop3code);
336 result = CURLE_LOGIN_DENIED;
339 state(conn, POP3_STOP);
343 /* for the retr response */
344 static CURLcode pop3_state_retr_resp(struct connectdata *conn,
348 CURLcode result = CURLE_OK;
349 struct SessionHandle *data = conn->data;
350 struct FTP *pop3 = data->state.proto.pop3;
351 struct pop3_conn *pop3c = &conn->proto.pop3c;
352 struct pingpong *pp = &pop3c->pp;
354 (void)instate; /* no use for this yet */
356 if('O' != pop3code) {
357 state(conn, POP3_STOP);
358 return CURLE_RECV_ERROR;
362 Curl_setup_transfer(conn, FIRSTSOCKET, -1, FALSE,
363 pop3->bytecountp, -1, NULL); /* no upload here */
366 /* At this point there is a bunch of data in the header "cache" that is
367 actually body content, send it as body and then skip it. Do note
368 that there may even be additional "headers" after the body. */
370 /* we may get the EOB already here! */
371 result = Curl_pop3_write(conn, pp->cache, pp->cache_size);
375 /* cache is drained */
381 state(conn, POP3_STOP);
386 /* for the list response */
387 static CURLcode pop3_state_list_resp(struct connectdata *conn,
391 CURLcode result = CURLE_OK;
392 struct SessionHandle *data = conn->data;
393 struct FTP *pop3 = data->state.proto.pop3;
394 struct pop3_conn *pop3c = &conn->proto.pop3c;
395 struct pingpong *pp = &pop3c->pp;
397 (void)instate; /* no use for this yet */
399 if('O' != pop3code) {
400 state(conn, POP3_STOP);
401 return CURLE_RECV_ERROR;
405 Curl_setup_transfer(conn, FIRSTSOCKET, -1, FALSE, pop3->bytecountp,
406 -1, NULL); /* no upload here */
409 /* cache holds the email ID listing */
411 /* we may get the EOB already here! */
412 result = Curl_pop3_write(conn, pp->cache, pp->cache_size);
416 /* cache is drained */
422 state(conn, POP3_STOP);
426 /* start the DO phase for RETR */
427 static CURLcode pop3_retr(struct connectdata *conn)
429 CURLcode result = CURLE_OK;
430 struct pop3_conn *pop3c = &conn->proto.pop3c;
432 result = Curl_pp_sendf(&conn->proto.pop3c.pp, "RETR %s", pop3c->mailbox);
436 state(conn, POP3_RETR);
440 /* start the DO phase for LIST */
441 static CURLcode pop3_list(struct connectdata *conn)
443 CURLcode result = CURLE_OK;
444 struct pop3_conn *pop3c = &conn->proto.pop3c;
446 result = Curl_pp_sendf(&conn->proto.pop3c.pp, "LIST %s", pop3c->mailbox);
450 state(conn, POP3_LIST);
454 static CURLcode pop3_statemach_act(struct connectdata *conn)
457 curl_socket_t sock = conn->sock[FIRSTSOCKET];
458 struct SessionHandle *data=conn->data;
460 struct pop3_conn *pop3c = &conn->proto.pop3c;
461 struct pingpong *pp = &pop3c->pp;
465 return Curl_pp_flushsend(pp);
467 /* we read a piece of response */
468 result = Curl_pp_readresp(sock, pp, &pop3code, &nread);
473 /* we have now received a full POP3 server response */
474 switch(pop3c->state) {
475 case POP3_SERVERGREET:
476 if(pop3code != 'O') {
477 failf(data, "Got unexpected pop3-server response");
478 return CURLE_FTP_WEIRD_SERVER_REPLY;
481 if(data->set.ftp_ssl && !conn->ssl[FIRSTSOCKET].use) {
482 /* We don't have a SSL/TLS connection yet, but SSL is requested. Switch
483 to TLS connection now */
484 result = Curl_pp_sendf(&pop3c->pp, "STARTTLS", NULL);
485 state(conn, POP3_STARTTLS);
488 result = pop3_state_user(conn);
494 result = pop3_state_user_resp(conn, pop3code, pop3c->state);
498 result = pop3_state_pass_resp(conn, pop3code, pop3c->state);
502 result = pop3_state_starttls_resp(conn, pop3code, pop3c->state);
506 result = pop3_state_retr_resp(conn, pop3code, pop3c->state);
510 result = pop3_state_list_resp(conn, pop3code, pop3c->state);
514 /* fallthrough, just stop! */
517 state(conn, POP3_STOP);
524 /* called repeatedly until done from multi.c */
525 static CURLcode pop3_multi_statemach(struct connectdata *conn, bool *done)
527 struct pop3_conn *pop3c = &conn->proto.pop3c;
528 CURLcode result = Curl_pp_multi_statemach(&pop3c->pp);
530 *done = (bool)(pop3c->state == POP3_STOP);
535 static CURLcode pop3_easy_statemach(struct connectdata *conn)
537 struct pop3_conn *pop3c = &conn->proto.pop3c;
538 struct pingpong *pp = &pop3c->pp;
539 CURLcode result = CURLE_OK;
541 while(pop3c->state != POP3_STOP) {
542 result = Curl_pp_easy_statemach(pp);
551 * Allocate and initialize the struct POP3 for the current SessionHandle. If
554 static CURLcode pop3_init(struct connectdata *conn)
556 struct SessionHandle *data = conn->data;
557 struct FTP *pop3 = data->state.proto.pop3;
559 pop3 = data->state.proto.pop3 = calloc(sizeof(struct FTP), 1);
561 return CURLE_OUT_OF_MEMORY;
564 /* get some initial data into the pop3 struct */
565 pop3->bytecountp = &data->req.bytecount;
567 /* No need to duplicate user+password, the connectdata struct won't change
568 during a session, but we re-init them here since on subsequent inits
569 since the conn struct may have changed or been replaced.
571 pop3->user = conn->user;
572 pop3->passwd = conn->passwd;
578 * pop3_connect() should do everything that is to be considered a part of
579 * the connection phase.
581 * The variable 'done' points to will be TRUE if the protocol-layer connect
582 * phase is done when this function returns, or FALSE is not. When called as
583 * a part of the easy interface, it will always be TRUE.
585 static CURLcode pop3_connect(struct connectdata *conn,
586 bool *done) /* see description above */
589 struct pop3_conn *pop3c = &conn->proto.pop3c;
590 struct SessionHandle *data=conn->data;
591 struct pingpong *pp = &pop3c->pp;
593 *done = FALSE; /* default to not done yet */
595 /* If there already is a protocol-specific struct allocated for this
596 sessionhandle, deal with it */
597 Curl_reset_reqproto(conn);
599 result = pop3_init(conn);
600 if(CURLE_OK != result)
603 /* We always support persistant connections on pop3 */
604 conn->bits.close = FALSE;
606 pp->response_time = RESP_TIMEOUT; /* set default response time-out */
607 pp->statemach_act = pop3_statemach_act;
608 pp->endofresp = pop3_endofresp;
611 #if !defined(CURL_DISABLE_HTTP) && !defined(CURL_DISABLE_PROXY)
612 if(conn->bits.tunnel_proxy && conn->bits.httpproxy) {
613 /* for POP3 over HTTP proxy */
614 struct HTTP http_proxy;
615 struct FTP *pop3_save;
618 /* We want "seamless" POP3 operations through HTTP proxy tunnel */
620 /* Curl_proxyCONNECT is based on a pointer to a struct HTTP at the member
621 * conn->proto.http; we want POP3 through HTTP and we have to change the
622 * member temporarily for connecting to the HTTP proxy. After
623 * Curl_proxyCONNECT we have to set back the member to the original struct
626 pop3_save = data->state.proto.pop3;
627 memset(&http_proxy, 0, sizeof(http_proxy));
628 data->state.proto.http = &http_proxy;
630 result = Curl_proxyCONNECT(conn, FIRSTSOCKET,
631 conn->host.name, conn->remote_port);
633 data->state.proto.pop3 = pop3_save;
635 if(CURLE_OK != result)
638 #endif /* !CURL_DISABLE_HTTP && !CURL_DISABLE_PROXY */
640 if(conn->protocol & PROT_POP3S) {
642 /* POP3S is simply pop3 with SSL for the control channel */
643 /* now, perform the SSL initialization for this socket */
644 result = Curl_ssl_connect(conn, FIRSTSOCKET);
649 Curl_pp_init(pp); /* init the response reader stuff */
651 /* When we connect, we start in the state where we await the server greet
653 state(conn, POP3_SERVERGREET);
655 if(data->state.used_interface == Curl_if_multi)
656 result = pop3_multi_statemach(conn, done);
658 result = pop3_easy_statemach(conn);
666 /***********************************************************************
670 * The DONE function. This does what needs to be done after a single DO has
673 * Input argument is already checked for validity.
675 static CURLcode pop3_done(struct connectdata *conn, CURLcode status,
678 struct SessionHandle *data = conn->data;
679 struct FTP *pop3 = data->state.proto.pop3;
680 CURLcode result=CURLE_OK;
684 /* When the easy handle is removed from the multi while libcurl is still
685 * trying to resolve the host name, it seems that the pop3 struct is not
686 * yet initialized, but the removal action calls Curl_done() which calls
687 * this function. So we simply return success if no pop3 pointer is set.
692 conn->bits.close = TRUE; /* marked for closure */
693 result = status; /* use the already set error code */
696 /* clear these for next connection */
697 pop3->transfer = FTPTRANSFER_BODY;
702 /***********************************************************************
706 * This is the actual DO function for POP3. Get a file/directory according to
707 * the options previously setup.
711 CURLcode pop3_perform(struct connectdata *conn,
712 bool *connected, /* connect status after PASV / PORT */
715 /* this is POP3 and no proxy */
716 CURLcode result=CURLE_OK;
717 struct pop3_conn *pop3c = &conn->proto.pop3c;
719 DEBUGF(infof(conn->data, "DO phase starts\n"));
721 if(conn->data->set.opt_no_body) {
722 /* requested no body means no transfer... */
723 struct FTP *pop3 = conn->data->state.proto.pop3;
724 pop3->transfer = FTPTRANSFER_INFO;
727 *dophase_done = FALSE; /* not done yet */
729 /* start the first command in the DO phase */
730 /* If mailbox is empty, then assume user wants listing for mail IDs,
731 * otherwise, attempt to retrieve the mail-id stored in mailbox
733 if (strlen(pop3c->mailbox))
734 result = pop3_retr(conn);
736 result = pop3_list(conn);
740 /* run the state-machine */
741 if(conn->data->state.used_interface == Curl_if_multi)
742 result = pop3_multi_statemach(conn, dophase_done);
744 result = pop3_easy_statemach(conn);
745 *dophase_done = TRUE; /* with the easy interface we are done here */
747 *connected = conn->bits.tcpconnect;
750 DEBUGF(infof(conn->data, "DO phase is complete\n"));
755 /***********************************************************************
759 * This function is registered as 'curl_do' function. It decodes the path
760 * parts etc as a wrapper to the actual DO function (pop3_perform).
762 * The input argument is already checked for validity.
764 static CURLcode pop3_do(struct connectdata *conn, bool *done)
766 CURLcode retcode = CURLE_OK;
768 *done = FALSE; /* default to false */
771 Since connections can be re-used between SessionHandles, this might be a
772 connection already existing but on a fresh SessionHandle struct so we must
773 make sure we have a good 'struct POP3' to play with. For new connections,
774 the struct POP3 is allocated and setup in the pop3_connect() function.
776 Curl_reset_reqproto(conn);
777 retcode = pop3_init(conn);
781 retcode = pop3_parse_url_path(conn);
785 retcode = pop3_regular_transfer(conn, done);
790 /***********************************************************************
794 * This should be called before calling sclose(). We should then wait for the
795 * response from the server before returning. The calling code should then try
796 * to close the connection.
799 static CURLcode pop3_quit(struct connectdata *conn)
801 CURLcode result = CURLE_OK;
803 result = Curl_pp_sendf(&conn->proto.pop3c.pp, "QUIT", NULL);
806 state(conn, POP3_QUIT);
808 result = pop3_easy_statemach(conn);
813 /***********************************************************************
817 * Disconnect from an POP3 server. Cleanup protocol-specific per-connection
818 * resources. BLOCKING.
820 static CURLcode pop3_disconnect(struct connectdata *conn, bool dead_connection)
822 struct pop3_conn *pop3c= &conn->proto.pop3c;
824 /* We cannot send quit unconditionally. If this connection is stale or
825 bad in any way, sending quit and waiting around here will make the
826 disconnect wait in vain and cause more problems than we need to.
829 /* The POP3 session may or may not have been allocated/setup at this
831 if(!dead_connection && pop3c->pp.conn)
832 (void)pop3_quit(conn); /* ignore errors on the LOGOUT */
835 Curl_pp_disconnect(&pop3c->pp);
840 /***********************************************************************
842 * pop3_parse_url_path()
844 * Parse the URL path into separate path components.
847 static CURLcode pop3_parse_url_path(struct connectdata *conn)
849 /* the pop3 struct is already inited in pop3_connect() */
850 struct pop3_conn *pop3c = &conn->proto.pop3c;
851 struct SessionHandle *data = conn->data;
852 const char *path = data->state.path;
854 /* url decode the path and use this mailbox */
855 pop3c->mailbox = curl_easy_unescape(data, path, 0, NULL);
857 return CURLE_OUT_OF_MEMORY;
862 /* call this when the DO phase has completed */
863 static CURLcode pop3_dophase_done(struct connectdata *conn,
866 struct FTP *pop3 = conn->data->state.proto.pop3;
867 struct pop3_conn *pop3c = &conn->proto.pop3c;
870 if(pop3->transfer != FTPTRANSFER_BODY)
871 /* no data to transfer */
872 Curl_setup_transfer(conn, -1, -1, FALSE, NULL, -1, NULL);
874 free(pop3c->mailbox);
879 /* called from multi.c while DOing */
880 static CURLcode pop3_doing(struct connectdata *conn,
884 result = pop3_multi_statemach(conn, dophase_done);
887 result = pop3_dophase_done(conn, FALSE /* not connected */);
889 DEBUGF(infof(conn->data, "DO phase is complete\n"));
894 /***********************************************************************
896 * pop3_regular_transfer()
898 * The input argument is already checked for validity.
900 * Performs all commands done before a regular transfer between a local and a
905 CURLcode pop3_regular_transfer(struct connectdata *conn,
908 CURLcode result=CURLE_OK;
909 bool connected=FALSE;
910 struct SessionHandle *data = conn->data;
911 data->req.size = -1; /* make sure this is unknown at this point */
913 Curl_pgrsSetUploadCounter(data, 0);
914 Curl_pgrsSetDownloadCounter(data, 0);
915 Curl_pgrsSetUploadSize(data, 0);
916 Curl_pgrsSetDownloadSize(data, 0);
918 result = pop3_perform(conn,
919 &connected, /* have we connected after PASV/PORT */
920 dophase_done); /* all commands in the DO-phase done? */
922 if(CURLE_OK == result) {
925 /* the DO phase has not completed yet */
928 result = pop3_dophase_done(conn, connected);
936 static CURLcode pop3_setup_connection(struct connectdata * conn)
938 struct SessionHandle *data = conn->data;
940 if(conn->bits.httpproxy && !data->set.tunnel_thru_httpproxy) {
941 /* Unless we have asked to tunnel pop3 operations through the proxy, we
942 switch and use HTTP operations only */
943 #ifndef CURL_DISABLE_HTTP
944 if(conn->handler == &Curl_handler_pop3)
945 conn->handler = &Curl_handler_pop3_proxy;
948 conn->handler = &Curl_handler_pop3s_proxy;
950 failf(data, "POP3S not supported!");
951 return CURLE_UNSUPPORTED_PROTOCOL;
955 * We explicitly mark this connection as persistent here as we're doing
956 * POP3 over HTTP and thus we accidentally avoid setting this value
959 conn->bits.close = FALSE;
961 failf(data, "POP3 over http proxy requires HTTP support built-in!");
962 return CURLE_UNSUPPORTED_PROTOCOL;
966 data->state.path++; /* don't include the initial slash */
971 /* this is the 5-bytes End-Of-Body marker for POP3 */
972 #define POP3_EOB "\x0d\x0a\x2e\x0d\x0a"
973 #define POP3_EOB_LEN 5
976 * This function scans the body after the end-of-body and writes everything
977 * until the end is found.
979 CURLcode Curl_pop3_write(struct connectdata *conn,
983 /* This code could be made into a special function in the handler struct. */
985 struct SessionHandle *data = conn->data;
986 struct SingleRequest *k = &data->req;
988 /* Detect the end-of-body marker, which is 5 bytes:
989 0d 0a 2e 0d 0a. This marker can of course be spread out
990 over up to 5 different data chunks. Deal with it! */
991 struct pop3_conn *pop3c = &conn->proto.pop3c;
992 size_t checkmax = (nread >= POP3_EOB_LEN?POP3_EOB_LEN:nread);
993 size_t checkleft = POP3_EOB_LEN-pop3c->eob;
994 size_t check = (checkmax >= checkleft?checkleft:checkmax);
996 if(!memcmp(POP3_EOB, &str[nread - check], check)) {
997 /* substring match */
999 if(pop3c->eob == POP3_EOB_LEN) {
1000 /* full match, the transfer is done! */
1001 str[nread - check] = '\0';
1003 k->keepon &= ~KEEP_RECV;
1007 else if(pop3c->eob) {
1008 /* not a match, but we matched a piece before so we must now
1009 send that part as body first, before we move on and send
1011 result = Curl_client_write(conn, CLIENTWRITE_BODY,
1012 (char *)POP3_EOB, pop3c->eob);
1018 result = Curl_client_write(conn, CLIENTWRITE_BODY, str, nread);
1023 #endif /* CURL_DISABLE_POP3 */