]>
diplodocus.org Git - nmh/blob - uip/popsbr.c
638fa4116b7f4bce3fb8a1728760be3da89b6cf9
2 * popsbr.c -- POP client subroutines
4 * This code is Copyright (c) 2002, by the authors of nmh. See the
5 * COPYRIGHT file in the root directory of the nmh distribution for
6 * complete copyright information.
15 #include <h/signals.h>
18 #define TRMLEN (sizeof TRM - 1)
20 static int poprint
= 0;
21 static int pophack
= 0;
23 char response
[BUFSIZ
];
24 static netsec_context
*nsc
= NULL
;
30 static int command(const char *, ...);
31 static int multiline(void);
33 static int traverse (int (*)(char *), const char *, ...);
34 static int vcommand(const char *, va_list);
35 static int pop_getline (char *, int, netsec_context
*);
36 static int pop_sasl_callback(enum sasl_message_type
, unsigned const char *,
37 unsigned int, unsigned char **, unsigned int *,
41 check_mech(char *server_mechs
, size_t server_mechs_size
)
43 int status
, sasl_capability
= 0;
46 * First off, we're going to send the CAPA command to see if we can
47 * even support the AUTH command, and if we do, then we'll get a
48 * list of mechanisms the server supports. If we don't support
49 * the CAPA command, then it's unlikely that we will support
53 if (command("CAPA") == NOTOK
) {
54 snprintf(response
, sizeof(response
),
55 "The POP CAPA command failed; POP server does not "
60 while ((status
= multiline()) != DONE
)
65 case DONE
: /* Shouldn't be possible, but just in case */
68 if (strncasecmp(response
, "SASL ", 5) == 0) {
70 * We've seen the SASL capability. Grab the mech list
73 strncpy(server_mechs
, response
+ 5, server_mechs_size
);
78 if (!sasl_capability
) {
79 snprintf(response
, sizeof(response
), "POP server does not support "
88 * Split string containing proxy command into an array of arguments
89 * suitable for passing to exec. Returned array must be freed. Shouldn't
90 * be possible to call this with host set to NULL.
93 parse_proxy(char *proxy
, char *host
)
97 int hlen
= strlen(host
);
99 unsigned char *cur
, *pro
;
102 /* skip any initial space */
103 for (pro
= (unsigned char *) proxy
; isspace(*pro
); pro
++)
106 /* calculate required size for argument array */
107 for (cur
= pro
; *cur
; cur
++) {
108 if (isspace(*cur
) && cur
[1] && !isspace(cur
[1]))
110 else if (*cur
== '%' && cur
[1] == 'h') {
113 } else if (!isspace(*cur
))
117 /* put together list of arguments */
118 p
= pargv
= mh_xmalloc(pargc
* sizeof(char *));
119 c
= *pargv
= mh_xmalloc(plen
* sizeof(char));
120 for (cur
= pro
; *cur
; cur
++) {
121 if (isspace(*cur
) && cur
[1] && !isspace(cur
[1])) {
124 } else if (*cur
== '%' && cur
[1] == 'h') {
128 } else if (!isspace(*cur
))
137 pop_init (char *host
, char *port
, char *user
, char *pass
, char *proxy
,
138 int snoop
, int sasl
, char *mech
, int tls
, const char *oauth_svc
)
147 netsec_set_userid(nsc
, user
);
150 if (netsec_set_tls(nsc
, 1, &errstr
) != OK
) {
151 snprintf(response
, sizeof(response
), "%s", errstr
);
157 if (oauth_svc
!= NULL
) {
158 if (netsec_set_oauth_service(nsc
, oauth_svc
) != OK
) {
159 snprintf(response
, sizeof(response
), "OAuth2 not supported");
164 if (proxy
&& *proxy
) {
166 int inpipe
[2]; /* for reading from the server */
167 int outpipe
[2]; /* for sending to the server */
169 if (pipe(inpipe
) < 0) {
170 adios ("inpipe", "pipe");
172 if (pipe(outpipe
) < 0) {
173 adios ("outpipe", "pipe");
183 dup2(outpipe
[0],0); /* connect read end of connection */
184 dup2(inpipe
[1], 1); /* connect write end of connection */
185 if(inpipe
[0]>1) close(inpipe
[0]);
186 if(inpipe
[1]>1) close(inpipe
[1]);
187 if(outpipe
[0]>1) close(outpipe
[0]);
188 if(outpipe
[1]>1) close(outpipe
[1]);
190 /* run the proxy command */
191 argv
=parse_proxy(proxy
, host
);
192 execvp(argv
[0],argv
);
202 /* okay in the parent we do some stuff */
203 close(inpipe
[1]); /* child uses this */
204 close(outpipe
[0]); /* child uses this */
208 /* and write on fd2 */
212 if ((fd1
= client (host
, port
? port
: "pop3", response
,
213 sizeof(response
), snoop
)) == NOTOK
) {
219 SIGNAL (SIGPIPE
, SIG_IGN
);
221 netsec_set_fd(nsc
, fd1
, fd2
);
222 netsec_set_snoop(nsc
, snoop
);
225 if (netsec_negotiate_tls(nsc
, &errstr
) != OK
) {
226 snprintf(response
, sizeof(response
), "%s", errstr
);
233 if (netsec_set_sasl_params(nsc
, host
, "pop", mech
,
234 pop_sasl_callback
, &errstr
) != OK
) {
235 snprintf(response
, sizeof(response
), "%s", errstr
);
241 switch (pop_getline (response
, sizeof response
, nsc
)) {
244 fprintf (stderr
, "<--- %s\n", response
);
245 if (*response
== '+') {
247 char server_mechs
[256];
248 if (check_mech(server_mechs
, sizeof(server_mechs
)) != OK
)
250 if (netsec_negotiate_sasl(nsc
, server_mechs
,
252 strncpy(response
, errstr
, sizeof(response
));
253 response
[sizeof(response
) - 1] = '\0';
259 if (command ("USER %s", user
) != NOTOK
260 && command ("%s %s", (pophack
++, "PASS"),
264 strncpy (buffer
, response
, sizeof(buffer
));
266 strncpy (response
, buffer
, sizeof(response
));
272 fprintf (stderr
, "%s\n", response
);
273 netsec_shutdown(nsc
, 1);
278 return NOTOK
; /* NOTREACHED */
283 * Our SASL callback; we are given SASL tokens and then have to format
284 * them according to the protocol requirements, and then process incoming
285 * messages and feed them back into the SASL library.
289 pop_sasl_callback(enum sasl_message_type mtype
, unsigned const char *indata
,
290 unsigned int indatalen
, unsigned char **outdata
,
291 unsigned int *outdatalen
, char **errstr
)
298 case NETSEC_SASL_START
:
300 * Generate our AUTH message, but there is a wrinkle.
302 * Technically, according to RFC 5034, if your command INCLUDING
303 * an initial response exceeds 255 octets (including CRLF), you
304 * can't issue this all in one go, but have to just issue the
305 * AUTH command, wait for a blank initial response, and then
309 mech
= netsec_get_sasl_mechanism(nsc
);
313 b64data
= mh_xmalloc(BASE64SIZE(indatalen
));
314 writeBase64raw(indata
, indatalen
, (unsigned char *) b64data
);
315 b64len
= strlen(b64data
);
317 /* Formula here is AUTH + SP + mech + SP + out + CR + LF */
318 len
= b64len
+ 8 + strlen(mech
);
320 rc
= netsec_printf(nsc
, errstr
, "AUTH %s\r\n", mech
);
323 if (netsec_flush(nsc
, errstr
) != OK
)
325 line
= netsec_readline(nsc
, &len
, errstr
);
329 * If the protocol is being followed correctly, should just
330 * be a "+ ", nothing else.
332 if (len
!= 2 || strcmp(line
, "+ ") != 0) {
333 netsec_err(errstr
, "Did not get expected blank response "
334 "for initial challenge response");
337 netsec_set_snoop_callback(nsc
, netsec_b64_snoop_decoder
, NULL
);
338 rc
= netsec_printf(nsc
, errstr
, "%s\r\n", b64data
);
339 netsec_set_snoop_callback(nsc
, NULL
, NULL
);
343 if (netsec_flush(nsc
, errstr
) != OK
)
346 netsec_set_snoop_callback(nsc
, netsec_b64_snoop_decoder
,
348 snoopoffset
= 6 + strlen(mech
);
349 rc
= netsec_printf(nsc
, errstr
, "AUTH %s %s\r\n", mech
,
354 if (netsec_flush(nsc
, errstr
) != OK
)
358 if (netsec_printf(nsc
, errstr
, "AUTH %s\r\n", mech
) != OK
)
360 if (netsec_flush(nsc
, errstr
) != OK
)
367 * We should get one line back, with our base64 data. Decode that
368 * and feed it back into the SASL library.
370 case NETSEC_SASL_READ
:
371 netsec_set_snoop_callback(nsc
, netsec_b64_snoop_decoder
, &snoopoffset
);
373 line
= netsec_readline(nsc
, &len
, errstr
);
374 netsec_set_snoop_callback(nsc
, NULL
, NULL
);
378 if (len
< 2 || (len
== 2 && strcmp(line
, "+ ") != 0)) {
379 netsec_err(errstr
, "Invalid format for SASL response");
387 rc
= decodeBase64(line
+ 2, (const char **) outdata
, &len
, 0, NULL
);
395 * Our encoding is pretty simple, so this is easy.
398 case NETSEC_SASL_WRITE
:
399 if (indatalen
== 0) {
400 rc
= netsec_printf(nsc
, errstr
, "\r\n");
402 unsigned char *b64data
;
403 b64data
= mh_xmalloc(BASE64SIZE(indatalen
));
404 writeBase64raw(indata
, indatalen
, b64data
);
405 netsec_set_snoop_callback(nsc
, netsec_b64_snoop_decoder
, NULL
);
406 rc
= netsec_printf(nsc
, errstr
, "%s\r\n", b64data
);
407 netsec_set_snoop_callback(nsc
, NULL
, NULL
);
414 if (netsec_flush(nsc
, errstr
) != OK
)
421 * Finish the protocol; we're looking for an +OK
424 case NETSEC_SASL_FINISH
:
425 line
= netsec_readline(nsc
, &len
, errstr
);
429 if (strncmp(line
, "+OK", 3) != 0) {
430 netsec_err(errstr
, "Authentication failed: %s", line
);
436 * Cancel the SASL exchange in the middle of the commands; for
437 * POP, that's a single "*".
439 * It's unclear to me if I should be returning errors up; I finally
440 * decided the answer should be "yes", and if the upper layer wants to
441 * ignore them that's their choice.
444 case NETSEC_SASL_CANCEL
:
445 rc
= netsec_printf(nsc
, errstr
, "*\r\n");
447 rc
= netsec_flush(nsc
, errstr
);
456 * Find out number of messages available
457 * and their total size.
461 pop_stat (int *nmsgs
, int *nbytes
)
464 if (command ("STAT") == NOTOK
)
467 *nmsgs
= *nbytes
= 0;
468 sscanf (response
, "+OK %d %d", nmsgs
, nbytes
);
475 pop_list (int msgno
, int *nmsgs
, int *msgs
, int *bytes
)
481 if (command ("LIST %d", msgno
) == NOTOK
)
486 sscanf (response
, "+OK %d %d %d", msgs
, bytes
, ids
);
489 sscanf (response
, "+OK %d %d", msgs
, bytes
);
493 if (command ("LIST") == NOTOK
)
496 for (i
= 0; i
< *nmsgs
; i
++)
497 switch (multiline ()) {
507 sscanf (response
, "%d %d %d",
508 msgs
++, bytes
++, ids
++);
511 sscanf (response
, "%d %d", msgs
++, bytes
++);
515 switch (multiline ()) {
527 pop_retr (int msgno
, int (*action
)(char *))
529 return traverse (action
, "RETR %d", msgno
);
534 traverse (int (*action
)(char *), const char *fmt
, ...)
538 char buffer
[sizeof(response
)];
541 result
= vcommand (fmt
, ap
);
546 strncpy (buffer
, response
, sizeof(buffer
));
549 switch (multiline ()) {
554 strncpy (response
, buffer
, sizeof(response
));
558 (*action
) (response
);
567 return command ("DELE %d", msgno
);
574 return command ("NOOP");
581 return command ("RSET");
586 pop_top (int msgno
, int lines
, int (*action
)(char *))
588 return traverse (action
, "TOP %d %d", msgno
, lines
);
597 i
= command ("QUIT");
608 netsec_shutdown(nsc
, 1);
615 command(const char *fmt
, ...)
621 result
= vcommand(fmt
, ap
);
629 vcommand (const char *fmt
, va_list ap
)
634 if (netsec_vprintf(nsc
, &errstr
, fmt
, ap
) != OK
) {
635 strncpy(response
, errstr
, sizeof(response
));
636 response
[sizeof(response
) - 1] = '\0';
641 if (netsec_printf(nsc
, &errstr
, "\r\n") != OK
) {
642 strncpy(response
, errstr
, sizeof(response
));
643 response
[sizeof(response
) - 1] = '\0';
648 if (netsec_flush(nsc
, &errstr
) != OK
) {
649 strncpy(response
, errstr
, sizeof(response
));
650 response
[sizeof(response
) - 1] = '\0';
655 switch (pop_getline (response
, sizeof response
, nsc
)) {
658 fprintf (stderr
, "<--- %s\n", response
);
659 return (*response
== '+' ? OK
: NOTOK
);
664 fprintf (stderr
, "%s\n", response
);
668 return NOTOK
; /* NOTREACHED */
675 char buffer
[BUFSIZ
+ TRMLEN
];
677 if (pop_getline (buffer
, sizeof buffer
, nsc
) != OK
)
679 if (strncmp (buffer
, TRM
, TRMLEN
) == 0) {
680 if (buffer
[TRMLEN
] == 0)
683 strncpy (response
, buffer
+ TRMLEN
, sizeof(response
));
686 strncpy (response
, buffer
, sizeof(response
));
692 * This is now just a thin wrapper around netsec_readline().
696 pop_getline (char *s
, int n
, netsec_context
*ns
)
704 p
= netsec_readline(ns
, &len
, &errstr
);
707 strncpy(response
, errstr
, sizeof(response
));
708 response
[sizeof(response
) - 1] = '\0';
714 * If we had an error, it should have been returned already. Since
715 * netsec_readline() strips off the CR-LF ending, just copy the existing
716 * buffer into response now.
718 * We get a length back from netsec_readline, but the rest of the POP
719 * code doesn't handle it; the assumptions are that everything from
720 * the network can be respresented as C strings. That should get fixed
724 destlen
= len
< ((size_t) (n
- 1)) ? len
: n
- 1;
726 memcpy(s
, p
, destlen
);