]>
diplodocus.org Git - nmh/blob - uip/popsbr.c
c1d520e9a9678c060ced1f956ee5cc1952a66faa
1 /* popsbr.c -- POP client subroutines
3 * This code is Copyright (c) 2002, by the authors of nmh. See the
4 * COPYRIGHT file in the root directory of the nmh distribution for
5 * complete copyright information.
9 #include "sbr/credentials.h"
10 #include "sbr/client.h"
11 #include "sbr/error.h"
17 #include "h/signals.h"
18 #include "sbr/base64.h"
22 static int poprint
= 0;
24 char response
[BUFSIZ
];
25 static netsec_context
*nsc
= NULL
;
31 static int command(const char *, ...) CHECK_PRINTF(1, 2);
32 static int multiline(void);
34 static int traverse(int (*)(void *, char *), void *closure
,
35 const char *, ...) CHECK_PRINTF(3, 4);
36 static int vcommand(const char *, va_list) CHECK_PRINTF(1, 0);
37 static int pop_getline (char *, int, netsec_context
*);
38 static int pop_sasl_callback(enum sasl_message_type
, unsigned const char *,
39 unsigned int, unsigned char **, unsigned int *,
43 check_mech(char *server_mechs
, size_t server_mechs_size
)
46 bool sasl_capability
= false;
49 * First off, we're going to send the CAPA command to see if we can
50 * even support the AUTH command, and if we do, then we'll get a
51 * list of mechanisms the server supports. If we don't support
52 * the CAPA command, then it's unlikely that we will support
56 if (command("CAPA") == NOTOK
) {
57 snprintf(response
, sizeof(response
),
58 "The POP CAPA command failed; POP server does not "
63 while ((status
= multiline()) != DONE
) {
67 if (strncasecmp(response
, "SASL ", 5) == 0) {
68 /* We've seen the SASL capability. Grab the mech list. */
69 sasl_capability
= true;
70 strncpy(server_mechs
, response
+ 5, server_mechs_size
- 1);
74 if (!sasl_capability
) {
75 snprintf(response
, sizeof(response
), "POP server does not support "
84 * If capable, issue the STLS command and start the TLS negotiation
95 * Issue the CAPA command and see if we have the STLS capability
98 if (command("CAPA") == NOTOK
) {
99 snprintf(response
, sizeof(response
),
100 "The POP CAPA command failed; POP server does not "
105 while ((status
= multiline()) != DONE
) {
109 if (strcasecmp(response
, "STLS") == 0)
114 snprintf(response
, sizeof(response
), "POP server does not support "
120 * Issue STLS and then start the actual TLS negotiation
123 if (command("STLS") == NOTOK
)
126 if (netsec_negotiate_tls(nsc
, &errstr
) != OK
) {
127 snprintf(response
, sizeof(response
), "%s", errstr
);
136 * Split string containing proxy command into an array of arguments
137 * suitable for passing to exec. Returned array must be freed. Shouldn't
138 * be possible to call this with host set to NULL.
141 parse_proxy(char *proxy
, char *host
)
145 int hlen
= strlen(host
);
147 unsigned char *cur
, *pro
;
150 /* skip any initial space */
151 for (pro
= (unsigned char *) proxy
; isspace(*pro
); pro
++)
154 /* calculate required size for argument array */
155 for (cur
= pro
; *cur
; cur
++) {
156 if (isspace(*cur
) && cur
[1] && !isspace(cur
[1]))
158 else if (*cur
== '%' && cur
[1] == 'h') {
161 } else if (!isspace(*cur
))
165 /* put together list of arguments */
166 p
= pargv
= mh_xmalloc(pargc
* sizeof(char *));
167 c
= *pargv
= mh_xmalloc(plen
);
168 for (cur
= pro
; *cur
; cur
++) {
169 if (isspace(*cur
) && cur
[1] && !isspace(cur
[1])) {
172 } else if (*cur
== '%' && cur
[1] == 'h') {
176 } else if (!isspace(*cur
))
185 pop_init (char *host
, char *port
, char *user
, char *proxy
, int snoop
,
186 int sasl
, char *mech
, int tls
, const char *oauth_svc
)
195 netsec_set_userid(nsc
, user
);
197 netsec_set_hostname(nsc
, host
);
199 if (oauth_svc
!= NULL
) {
200 if (netsec_set_oauth_service(nsc
, oauth_svc
) != OK
) {
201 snprintf(response
, sizeof(response
), "OAuth2 not supported");
206 if (proxy
&& *proxy
) {
208 int inpipe
[2]; /* for reading from the server */
209 int outpipe
[2]; /* for sending to the server */
211 if (pipe(inpipe
) < 0) {
212 adios ("inpipe", "pipe");
214 if (pipe(outpipe
) < 0) {
215 adios ("outpipe", "pipe");
225 dup2(outpipe
[0],0); /* connect read end of connection */
226 dup2(inpipe
[1], 1); /* connect write end of connection */
227 if(inpipe
[0]>1) close(inpipe
[0]);
228 if(inpipe
[1]>1) close(inpipe
[1]);
229 if(outpipe
[0]>1) close(outpipe
[0]);
230 if(outpipe
[1]>1) close(outpipe
[1]);
232 /* run the proxy command */
233 argv
=parse_proxy(proxy
, host
);
234 execvp(argv
[0],argv
);
244 /* okay in the parent we do some stuff */
245 close(inpipe
[1]); /* child uses this */
246 close(outpipe
[0]); /* child uses this */
250 /* and write on fd2 */
254 if ((fd1
= client (host
, port
? port
: "pop3", response
,
255 sizeof(response
), snoop
)) == NOTOK
) {
261 SIGNAL (SIGPIPE
, SIG_IGN
);
263 netsec_set_fd(nsc
, fd1
, fd2
);
264 netsec_set_snoop(nsc
, snoop
);
266 if (tls
& P_TLSENABLEMASK
) {
267 if (netsec_set_tls(nsc
, 1, tls
& P_NOVERIFY
, &errstr
) != OK
) {
268 snprintf(response
, sizeof(response
), "%s", errstr
);
273 if (tls
& P_INITTLS
) {
274 if (netsec_negotiate_tls(nsc
, &errstr
) != OK
) {
275 snprintf(response
, sizeof(response
), "%s", errstr
);
283 if (netsec_set_sasl_params(nsc
, "pop", mech
, pop_sasl_callback
,
284 NULL
, &errstr
) != OK
) {
285 snprintf(response
, sizeof(response
), "%s", errstr
);
291 switch (pop_getline (response
, sizeof response
, nsc
)) {
294 fprintf (stderr
, "<--- %s\n", response
);
295 if (*response
== '+') {
298 if (tls
& P_STARTTLS
)
299 if (pop_start_tls() != OK
)
303 char server_mechs
[256];
304 if (check_mech(server_mechs
, sizeof(server_mechs
)) != OK
)
306 if (netsec_negotiate_sasl(nsc
, server_mechs
,
308 strncpy(response
, errstr
, sizeof(response
));
309 response
[sizeof(response
) - 1] = '\0';
316 if (!(creds
= nmh_get_credentials(host
, user
)))
318 if (command ("USER %s", nmh_cred_get_user(creds
))
320 if (command("PASS %s", nmh_cred_get_password(creds
))
322 nmh_credentials_free(creds
);
326 nmh_credentials_free(creds
);
328 strncpy (buffer
, response
, sizeof(buffer
));
330 strncpy (response
, buffer
, sizeof(response
));
336 fputs(response
, stderr
);
339 netsec_shutdown(nsc
);
344 return NOTOK
; /* NOTREACHED */
349 * Our SASL callback; we are given SASL tokens and then have to format
350 * them according to the protocol requirements, and then process incoming
351 * messages and feed them back into the SASL library.
355 pop_sasl_callback(enum sasl_message_type mtype
, unsigned const char *indata
,
356 unsigned int indatalen
, unsigned char **outdata
,
357 unsigned int *outdatalen
, void *context
, char **errstr
)
365 case NETSEC_SASL_START
:
367 * Generate our AUTH message, but there is a wrinkle.
369 * Technically, according to RFC 5034, if your command INCLUDING
370 * an initial response exceeds 255 octets (including CRLF), you
371 * can't issue this all in one go, but have to just issue the
372 * AUTH command, wait for a blank initial response, and then
376 mech
= netsec_get_sasl_mechanism(nsc
);
380 b64data
= mh_xmalloc(BASE64SIZE(indatalen
));
381 writeBase64raw(indata
, indatalen
, (unsigned char *) b64data
);
382 b64len
= strlen(b64data
);
384 /* Formula here is AUTH + SP + mech + SP + out + CR + LF */
385 len
= b64len
+ 8 + strlen(mech
);
387 rc
= netsec_printf(nsc
, errstr
, "AUTH %s\r\n", mech
);
390 if (netsec_flush(nsc
, errstr
) != OK
)
392 line
= netsec_readline(nsc
, &len
, errstr
);
396 * If the protocol is being followed correctly, should just
397 * be a "+ ", nothing else.
399 if (len
!= 2 || strcmp(line
, "+ ") != 0) {
400 netsec_err(errstr
, "Did not get expected blank response "
401 "for initial challenge response");
404 rc
= netsec_printf(nsc
, errstr
, "%s\r\n", b64data
);
408 netsec_set_snoop_callback(nsc
, netsec_b64_snoop_decoder
, NULL
);
409 rc
= netsec_flush(nsc
, errstr
);
410 netsec_set_snoop_callback(nsc
, NULL
, NULL
);
414 rc
= netsec_printf(nsc
, errstr
, "AUTH %s %s\r\n", mech
,
419 netsec_set_snoop_callback(nsc
, netsec_b64_snoop_decoder
,
421 snoopoffset
= 6 + strlen(mech
);
422 rc
= netsec_flush(nsc
, errstr
);
423 netsec_set_snoop_callback(nsc
, NULL
, NULL
);
428 if (netsec_printf(nsc
, errstr
, "AUTH %s\r\n", mech
) != OK
)
430 if (netsec_flush(nsc
, errstr
) != OK
)
437 * We should get one line back, with our base64 data. Decode that
438 * and feed it back into the SASL library.
440 case NETSEC_SASL_READ
:
441 netsec_set_snoop_callback(nsc
, netsec_b64_snoop_decoder
, &snoopoffset
);
443 line
= netsec_readline(nsc
, &len
, errstr
);
444 netsec_set_snoop_callback(nsc
, NULL
, NULL
);
450 * If we get an -ERR reply, bubble that back up
453 if (has_prefix(line
, "-ERR")) {
454 netsec_err(errstr
, "%s", line
);
459 * Make sure we get someting back a valid response
462 if (!has_prefix(line
, "+ ")) {
463 netsec_err(errstr
, "Invalid format for SASL response");
471 rc
= decodeBase64(line
+ 2, outdata
, &len
, 0);
474 netsec_err(errstr
, "Unable to decode base64 response");
481 * Our encoding is pretty simple, so this is easy.
484 case NETSEC_SASL_WRITE
:
485 if (indatalen
== 0) {
486 rc
= netsec_printf(nsc
, errstr
, "\r\n");
488 unsigned char *b64data
;
489 b64data
= mh_xmalloc(BASE64SIZE(indatalen
));
490 writeBase64raw(indata
, indatalen
, b64data
);
491 rc
= netsec_printf(nsc
, errstr
, "%s\r\n", b64data
);
499 netsec_set_snoop_callback(nsc
, netsec_b64_snoop_decoder
, NULL
);
500 rc
= netsec_flush(nsc
, errstr
);
501 netsec_set_snoop_callback(nsc
, NULL
, NULL
);
507 * Finish the protocol; we're looking for an +OK
510 case NETSEC_SASL_FINISH
:
511 line
= netsec_readline(nsc
, &len
, errstr
);
515 if (!has_prefix(line
, "+OK")) {
516 netsec_err(errstr
, "Authentication failed: %s", line
);
522 * Cancel the SASL exchange in the middle of the commands; for
523 * POP, that's a single "*".
525 * It's unclear to me if I should be returning errors up; I finally
526 * decided the answer should be "yes", and if the upper layer wants to
527 * ignore them that's their choice.
530 case NETSEC_SASL_CANCEL
:
531 rc
= netsec_printf(nsc
, errstr
, "*\r\n");
533 rc
= netsec_flush(nsc
, errstr
);
543 * Find out number of messages available
544 * and their total size.
548 pop_stat (int *nmsgs
, int *nbytes
)
551 if (command ("STAT") == NOTOK
)
554 *nmsgs
= *nbytes
= 0;
555 sscanf (response
, "+OK %d %d", nmsgs
, nbytes
);
562 pop_retr (int msgno
, int (*action
)(void *, char *), void *closure
)
564 return traverse (action
, closure
, "RETR %d", msgno
);
569 traverse (int (*action
)(void *, char *), void *closure
, const char *fmt
, ...)
571 int result
, snoopstate
;
573 char buffer
[sizeof(response
)];
576 result
= vcommand (fmt
, ap
);
581 strncpy (buffer
, response
, sizeof(buffer
));
583 if ((snoopstate
= netsec_get_snoop(nsc
)))
584 netsec_set_snoop(nsc
, 0);
587 result
= multiline();
589 result
= (*action
)(closure
, response
);
592 } else if (result
== DONE
) {
593 strncpy(response
, buffer
, sizeof(response
));
599 netsec_set_snoop(nsc
, snoopstate
);
607 return command ("DELE %d", msgno
);
616 i
= command ("QUIT");
627 netsec_shutdown(nsc
);
634 command(const char *fmt
, ...)
640 result
= vcommand(fmt
, ap
);
648 vcommand (const char *fmt
, va_list ap
)
653 if (netsec_vprintf(nsc
, &errstr
, fmt
, ap
) != OK
) {
654 strncpy(response
, errstr
, sizeof(response
));
655 response
[sizeof(response
) - 1] = '\0';
660 if (netsec_printf(nsc
, &errstr
, "\r\n") != OK
) {
661 strncpy(response
, errstr
, sizeof(response
));
662 response
[sizeof(response
) - 1] = '\0';
667 if (netsec_flush(nsc
, &errstr
) != OK
) {
668 strncpy(response
, errstr
, sizeof(response
));
669 response
[sizeof(response
) - 1] = '\0';
674 switch (pop_getline (response
, sizeof response
, nsc
)) {
677 fprintf (stderr
, "<--- %s\n", response
);
678 return *response
== '+' ? OK
: NOTOK
;
683 fputs(response
, stderr
);
689 return NOTOK
; /* NOTREACHED */
696 char buffer
[BUFSIZ
+ LEN(TRM
)];
698 if (pop_getline (buffer
, sizeof buffer
, nsc
) != OK
)
700 if (has_prefix(buffer
, TRM
)) {
701 if (buffer
[LEN(TRM
)] == 0)
703 strncpy (response
, buffer
+ LEN(TRM
), sizeof(response
));
706 strncpy (response
, buffer
, sizeof(response
));
712 * This is now just a thin wrapper around netsec_readline().
716 pop_getline (char *s
, int n
, netsec_context
*ns
)
724 p
= netsec_readline(ns
, &len
, &errstr
);
727 strncpy(response
, errstr
, sizeof(response
));
728 response
[sizeof(response
) - 1] = '\0';
734 * If we had an error, it should have been returned already. Since
735 * netsec_readline() strips off the CR-LF ending, just copy the existing
736 * buffer into response now.
738 * We get a length back from netsec_readline, but the rest of the POP
739 * code doesn't handle it; the assumptions are that everything from
740 * the network can be represented as C strings. That should get fixed
744 destlen
= min(len
, (size_t)(n
- 1));
746 memcpy(s
, p
, destlen
);