+
+ /*
+ * Limit this to what we can deal with. Shouldn't matter much because
+ * this is only outgoing data (which should be small)
+ */
+
+ if (maxoutbuf == 0 || maxoutbuf > BUFSIZ)
+ maxoutbuf = BUFSIZ;
+
+ sasl_complete = 1;
+
+ return status;
+}
+
+/*
+ * Callback to return the userid sent down via the user parameter
+ */
+
+static int
+sasl_get_user(void *context, int id, const char **result, unsigned *len)
+{
+ char *user = (char *) context;
+
+ if (! result || id != SASL_CB_USER)
+ return SASL_BADPARAM;
+
+ *result = user;
+ if (len)
+ *len = strlen(user);
+
+ return SASL_OK;
+}
+
+/*
+ * Callback to return the password (we call ruserpass, which can get it
+ * out of the .netrc
+ */
+
+static int
+sasl_get_pass(sasl_conn_t *conn, void *context, int id, sasl_secret_t **psecret)
+{
+ struct pass_context *p_context = (struct pass_context *) context;
+ struct nmh_creds creds = { 0, 0, 0 };
+ int len;
+
+ NMH_UNUSED (conn);
+
+ if (! psecret || id != SASL_CB_PASS)
+ return SASL_BADPARAM;
+
+ if (creds.password == NULL) {
+ /*
+ * Pass the 0 third argument to nmh_get_credentials() so
+ * that the default password isn't used. With legacy/.netrc
+ * credentials support, we'll only get here if the -user
+ * switch to send(1)/post(8) wasn't used.
+ */
+ if (nmh_get_credentials (p_context->host, p_context->user, 0, &creds)
+ != OK) {
+ return SASL_BADPARAM;
+ }
+ }
+
+ len = strlen (creds.password);
+
+ *psecret = (sasl_secret_t *) mh_xmalloc(sizeof(sasl_secret_t) + len);
+
+ (*psecret)->len = len;
+ strcpy((char *) (*psecret)->data, creds.password);
+
+ return SASL_OK;
+}
+#endif /* CYRUS_SASL */
+
+int
+pop_auth_xoauth(const char *client_res)
+{
+ char server_mechs[256];
+ int status = check_mech(server_mechs, sizeof(server_mechs), "XOAUTH");
+
+ if (status != OK) return status;
+
+ if ((status = command("AUTH XOAUTH2 %s", client_res)) != OK) {
+ return status;
+ }
+ if (strncmp(response, "+OK", 3) == 0) {
+ return OK;
+ }
+
+ /* response contains base64-encoded JSON, which is always the same.
+ * See mts/smtp/smtp.c for more notes on that. */
+ /* Then we're supposed to send an empty response ("\r\n"). */
+ return command("");
+}
+
+/*
+ * Split string containing proxy command into an array of arguments
+ * suitable for passing to exec. Returned array must be freed. Shouldn't
+ * be possible to call this with host set to NULL.
+ */
+char **
+parse_proxy(char *proxy, char *host)
+{
+ char **pargv, **p;
+ int pargc = 2;
+ int hlen = strlen(host);
+ int plen = 1;
+ unsigned char *cur, *pro;
+ char *c;
+
+ /* skip any initial space */
+ for (pro = (unsigned char *) proxy; isspace(*pro); pro++)
+ continue;
+
+ /* calculate required size for argument array */
+ for (cur = pro; *cur; cur++) {
+ if (isspace(*cur) && cur[1] && !isspace(cur[1]))
+ plen++, pargc++;
+ else if (*cur == '%' && cur[1] == 'h') {
+ plen += hlen;
+ cur++;
+ } else if (!isspace(*cur))
+ plen++;
+ }
+
+ /* put together list of arguments */
+ p = pargv = mh_xmalloc(pargc * sizeof(char *));
+ c = *pargv = mh_xmalloc(plen * sizeof(char));
+ for (cur = pro; *cur; cur++) {
+ if (isspace(*cur) && cur[1] && !isspace(cur[1])) {
+ *c++ = '\0';
+ *++p = c;
+ } else if (*cur == '%' && cur[1] == 'h') {
+ strcpy (c, host);
+ c += hlen;
+ cur++;
+ } else if (!isspace(*cur))
+ *c++ = *cur;
+ }
+ *c = '\0';
+ *++p = NULL;
+ return pargv;
+}
+
+int
+pop_init (char *host, char *port, char *user, char *pass, char *proxy,
+ int snoop, int sasl, char *mech, const char *oauth_svc)
+{
+ int fd1, fd2;
+ char buffer[BUFSIZ];
+ const char *xoauth_client_res = NULL;
+#ifndef CYRUS_SASL
+ NMH_UNUSED (sasl);
+ NMH_UNUSED (mech);
+#endif /* ! CYRUS_SASL */
+
+#ifdef OAUTH_SUPPORT
+ if (oauth_svc != NULL) {
+ xoauth_client_res = mh_oauth_do_xoauth(user, oauth_svc,
+ snoop ? stderr : NULL);
+ }
+#else
+ NMH_UNUSED (oauth_svc);
+ NMH_UNUSED (xoauth_client_res);
+#endif /* OAUTH_SUPPORT */
+
+ if (proxy && *proxy) {
+ int pid;
+ int inpipe[2]; /* for reading from the server */
+ int outpipe[2]; /* for sending to the server */
+
+ if (pipe(inpipe) < 0) {
+ adios ("inpipe", "pipe");
+ }
+ if (pipe(outpipe) < 0) {
+ adios ("outpipe", "pipe");
+ }
+
+ pid=fork();
+ if (pid==0) {
+ char **argv;
+
+ /* in child */
+ close(0);
+ close(1);
+ dup2(outpipe[0],0); /* connect read end of connection */
+ dup2(inpipe[1], 1); /* connect write end of connection */
+ if(inpipe[0]>1) close(inpipe[0]);
+ if(inpipe[1]>1) close(inpipe[1]);
+ if(outpipe[0]>1) close(outpipe[0]);
+ if(outpipe[1]>1) close(outpipe[1]);
+
+ /* run the proxy command */
+ argv=parse_proxy(proxy, host);
+ execvp(argv[0],argv);
+
+ perror(argv[0]);
+ close(0);
+ close(1);
+ free(*argv);
+ free(argv);
+ exit(10);
+ }
+
+ /* okay in the parent we do some stuff */
+ close(inpipe[1]); /* child uses this */
+ close(outpipe[0]); /* child uses this */
+
+ /* we read on fd1 */
+ fd1=inpipe[0];
+ /* and write on fd2 */
+ fd2=outpipe[1];
+
+ } else {
+
+ if ((fd1 = client (host, port ? port : "pop3", response,
+ sizeof(response), snoop)) == NOTOK) {
+ return NOTOK;
+ }
+
+ if ((fd2 = dup (fd1)) == NOTOK) {
+ char *s;
+
+ if ((s = strerror(errno)))
+ snprintf (response, sizeof(response),
+ "unable to dup connection descriptor: %s", s);
+ else
+ snprintf (response, sizeof(response),
+ "unable to dup connection descriptor: unknown error");
+ close (fd1);
+ return NOTOK;
+ }
+ }