unsigned use_stls : 2;
cmd_status cmd_capa : 2; /* optional command CAPA */
cmd_status cmd_stls : 2; /* optional command STLS */
- cmd_status cmd_user : 2; /* optional command USER */
cmd_status cmd_uidl : 2; /* optional command UIDL */
cmd_status cmd_top : 2; /* optional command TOP */
unsigned resp_codes : 1; /* server supports extended response codes */
return;
}
+/* }}} */
+/* Authentication {{{ */
+
+/* SASL authenticator */
+static pop_auth_res_t pop_auth_sasl(pop_data_t *pop_data, const char *method)
+{
+ sasl_conn_t *saslconn;
+ sasl_interact_t *interaction = NULL;
+
+ char buf[LONG_STRING], inbuf[LONG_STRING];
+ const char *mech, *pc = NULL;
+ unsigned int len, olen;
+ unsigned char client_start;
+ int rc;
+
+ if (mutt_sasl_client_new(pop_data->conn, &saslconn) < 0) {
+ return POP_A_FAILURE;
+ }
+
+ if (!method)
+ method = pop_data->auth_list;
+
+ for (;;) {
+ rc = sasl_client_start(saslconn, method, &interaction, &pc, &olen,
+ &mech);
+ if (rc != SASL_INTERACT)
+ break;
+ mutt_sasl_interact(interaction);
+ }
+
+ if (rc != SASL_OK && rc != SASL_CONTINUE) {
+ return POP_A_UNAVAIL;
+ }
+
+ client_start = (olen > 0);
+ mutt_message(_("Authenticating (SASL)..."));
+ snprintf(buf, sizeof (buf), "AUTH %s", mech);
+ olen = strlen (buf);
+
+ /* looping protocol */
+ for (;;) {
+ m_strcpy(buf + olen, sizeof(buf) - olen, "\r\n");
+ mutt_socket_write(pop_data->conn, buf);
+ if (mutt_socket_readln(inbuf, sizeof(inbuf), pop_data->conn) < 0) {
+ sasl_dispose(&saslconn);
+ pop_data->status = POP_DISCONNECTED;
+ return POP_A_SOCKET;
+ }
+
+ if (rc != SASL_CONTINUE)
+ break;
+
+ if (!m_strncmp(inbuf, "+ ", 2)
+ && sasl_decode64(inbuf, strlen(inbuf),
+ buf, sizeof(buf) - 1, &len) != SASL_OK)
+ {
+ goto bail;
+ }
+
+ if (!client_start) {
+ for (;;) {
+ rc = sasl_client_step(saslconn, buf, len, &interaction, &pc,
+ &olen);
+ if (rc != SASL_INTERACT)
+ break;
+ mutt_sasl_interact(interaction);
+ }
+ } else {
+ client_start = 0;
+ }
+
+ if (rc != SASL_CONTINUE && (olen == 0 || rc != SASL_OK))
+ break;
+
+ /* send out response, or line break if none needed */
+ if (pc) {
+ if (sasl_encode64(pc, olen, buf, sizeof(buf), &olen) != SASL_OK) {
+ goto bail;
+ }
+ p_delete((char **)&pc);
+ }
+ }
+
+ if (rc != SASL_OK)
+ goto bail;
+
+ if (!m_strncmp(inbuf, "+OK", 3)) {
+ mutt_sasl_setup_conn(pop_data->conn, saslconn);
+ return POP_A_SUCCESS;
+ }
+
+ bail:
+ sasl_dispose(&saslconn);
+ p_delete((char **)&pc);
+
+ /* terminate SASL session if the last responce is not +OK nor -ERR */
+ if (!m_strncmp(inbuf, "+ ", 2)) {
+ snprintf(buf, sizeof(buf), "*\r\n");
+ if (pop_query(pop_data, buf, sizeof(buf)) == PQ_NOT_CONNECTED)
+ return POP_A_SOCKET;
+ }
+
+ mutt_error(_("SASL authentication failed."));
+ mutt_sleep(2);
+
+ return POP_A_FAILURE;
+}
+
+/* APOP authenticator */
+static pop_auth_res_t pop_auth_apop(pop_data_t *pop_data, const char *method)
+{
+ MD5_CTX mdContext;
+ unsigned char digest[16];
+ char hash[33];
+ char buf[LONG_STRING];
+ int i;
+
+ if (!pop_data->timestamp)
+ return POP_A_UNAVAIL;
+
+ mutt_message _("Authenticating (APOP)...");
+
+ /* Compute the authentication hash to send to the server */
+ MD5Init(&mdContext);
+ MD5Update(&mdContext, (unsigned char *)pop_data->timestamp,
+ strlen(pop_data->timestamp));
+ MD5Update(&mdContext, (unsigned char *)pop_data->conn->account.pass,
+ strlen(pop_data->conn->account.pass));
+ MD5Final(digest, &mdContext);
+
+ for (i = 0; i < countof(digest); i++)
+ sprintf(hash + 2 * i, "%02x", digest[i]);
+
+ /* Send APOP command to server */
+ snprintf(buf, sizeof(buf), "APOP %s %s\r\n", pop_data->conn->account.user,
+ hash);
+
+ switch (pop_query(pop_data, buf, sizeof(buf))) {
+ case PQ_OK:
+ return POP_A_SUCCESS;
+ case PQ_NOT_CONNECTED:
+ return POP_A_SOCKET;
+ case PFD_FUNCT_ERROR:
+ case PQ_ERR:
+ default:
+ break;
+ }
+
+ mutt_error("%s %s", _("APOP authentication failed."), pop_data->err_msg);
+ mutt_sleep(2);
+
+ return POP_A_FAILURE;
+}
+
+/* USER authenticator */
+static pop_auth_res_t pop_auth_user(pop_data_t *pop_data, const char *method)
+{
+ char buf[LONG_STRING];
+ pop_query_status ret;
+
+ mutt_message(_("Logging in..."));
+ snprintf(buf, sizeof(buf), "USER %s\r\n", pop_data->conn->account.user);
+ ret = pop_query(pop_data, buf, sizeof (buf));
+
+ if (ret == PQ_ERR) {
+ snprintf(pop_data->err_msg, sizeof(pop_data->err_msg),
+ _("Command USER is not supported by server."));
+ }
+
+ if (ret == PQ_OK) {
+ snprintf(buf, sizeof(buf), "PASS %s\r\n",
+ pop_data->conn->account.pass);
+ ret = pop_query(pop_data, buf, sizeof(buf));
+ }
+
+ switch (ret) {
+ case PQ_OK:
+ return POP_A_SUCCESS;
+ case PQ_NOT_CONNECTED:
+ return POP_A_SOCKET;
+ default:
+ mutt_error ("%s %s", _("Login failed."), pop_data->err_msg);
+ mutt_sleep (2);
+ return POP_A_FAILURE;
+ }
+}
+
+typedef struct {
+ pop_auth_res_t (*do_auth)(pop_data_t *, const char *);
+ const char *method;
+} pop_auth_t;
+
+static pop_query_status pop_authenticate (pop_data_t * pop_data)
+{
+ static pop_auth_t const pop_authenticators[] = {
+ {pop_auth_sasl, NULL},
+ {pop_auth_apop, "apop"},
+ {pop_auth_user, "user"},
+ {NULL, NULL}
+ };
+
+ ACCOUNT *act = &pop_data->conn->account;
+ int attempts = 0, ret = POP_A_UNAVAIL;
+ const pop_auth_t *auth;
+
+ if (mutt_account_getuser(act) || !act->user[0]
+ || mutt_account_getpass(act) || !act->pass[0])
+ {
+ return PFD_FUNCT_ERROR;
+ }
+
+ if (!m_strisempty(PopAuthenticators)) {
+ const char *p, *q;
+ char buf[STRING];
+
+ for (p = PopAuthenticators; ; p = q) {
+ while (*p == ':')
+ p++;
+ if (!*p)
+ break;
+
+ q = strchrnul(p, ':');
+ m_strncpy(buf, sizeof(buf), p, q - p);
+
+ for (auth = pop_authenticators; auth->do_auth; auth++) {
+ if (auth->method && ascii_strcasecmp(auth->method, buf))
+ continue;
+
+ ret = auth->do_auth(pop_data, buf);
+ if (ret == POP_A_SOCKET) {
+ if (pop_connect(pop_data) == PQ_OK) {
+ ret = auth->do_auth(pop_data, buf);
+ } else {
+ ret = POP_A_FAILURE;
+ }
+ }
+
+ if (ret != POP_A_UNAVAIL)
+ attempts++;
+ if (ret == POP_A_SUCCESS || ret == POP_A_SOCKET) {
+ goto ok;
+ }
+ }
+ }
+ } else {
+ /* Fall back to default: any authenticator */
+ for (auth = pop_authenticators; auth->do_auth; auth++) {
+ ret = auth->do_auth(pop_data, auth->method);
+ if (ret == POP_A_SOCKET) {
+ if (pop_connect(pop_data) == PQ_OK) {
+ ret = auth->do_auth(pop_data, auth->method);
+ } else {
+ ret = POP_A_FAILURE;
+ }
+ }
+
+ if (ret != POP_A_UNAVAIL)
+ attempts++;
+ if (ret == POP_A_SUCCESS || ret == POP_A_SOCKET)
+ break;
+ }
+ }
+
+ ok:
+ switch (ret) {
+ case POP_A_SUCCESS:
+ return PQ_OK;
+ case POP_A_SOCKET:
+ return PQ_NOT_CONNECTED;
+ case POP_A_UNAVAIL:
+ if (!attempts)
+ mutt_error(_("No authenticators available"));
+ }
+
+ return PQ_ERR;
+}
+
/* }}} */
/*
else if (!ascii_strncasecmp (line, "STLS", 4))
pop_data->cmd_stls = CMD_AVAILABLE;
- else if (!ascii_strncasecmp (line, "USER", 4))
- pop_data->cmd_user = CMD_AVAILABLE;
-
else if (!ascii_strncasecmp (line, "UIDL", 4))
pop_data->cmd_uidl = CMD_AVAILABLE;
if (mode == 0) {
pop_data->cmd_capa = CMD_NOT_AVAILABLE;
pop_data->cmd_stls = CMD_NOT_AVAILABLE;
- pop_data->cmd_user = CMD_NOT_AVAILABLE;
pop_data->cmd_uidl = CMD_NOT_AVAILABLE;
pop_data->cmd_top = CMD_NOT_AVAILABLE;
pop_data->resp_codes = 0;
m_strcpy(buf, sizeof(buf), "CAPA\r\n");
switch (pop_fetch_data (pop_data, buf, NULL, fetch_capa, pop_data)) {
case PQ_OK:
- {
- pop_data->cmd_capa = CMD_AVAILABLE;
- break;
- }
+ pop_data->cmd_capa = CMD_AVAILABLE;
+ break;
case PFD_FUNCT_ERROR:
case PQ_ERR:
- {
- pop_data->cmd_capa = CMD_NOT_AVAILABLE;
- break;
- }
+ pop_data->cmd_capa = CMD_NOT_AVAILABLE;
+ break;
case PQ_NOT_CONNECTED:
return PQ_NOT_CONNECTED;
}
/* CAPA not supported, use defaults */
if (mode == 0 && pop_data->cmd_capa == CMD_NOT_AVAILABLE) {
- pop_data->cmd_user = CMD_UNKNOWN;
pop_data->cmd_uidl = CMD_UNKNOWN;
pop_data->cmd_top = CMD_UNKNOWN;
return PQ_OK;
}
-/* Authentication {{{ */
-
-/* SASL authenticator */
-static pop_auth_res_t pop_auth_sasl (pop_data_t * pop_data, const char *method)
-{
- sasl_conn_t *saslconn;
- sasl_interact_t *interaction = NULL;
- int rc;
- char buf[LONG_STRING];
- char inbuf[LONG_STRING];
- const char *mech;
-
- const char *pc = NULL;
- unsigned int len, olen;
- unsigned char client_start;
-
- if (mutt_sasl_client_new (pop_data->conn, &saslconn) < 0) {
- return POP_A_FAILURE;
- }
-
- if (!method)
- method = pop_data->auth_list;
-
- for (;;) {
- rc = sasl_client_start (saslconn, method, &interaction, &pc, &olen,
- &mech);
- if (rc != SASL_INTERACT)
- break;
- mutt_sasl_interact (interaction);
- }
-
- if (rc != SASL_OK && rc != SASL_CONTINUE) {
- /* SASL doesn't support suggested mechanisms, so fall back */
- return POP_A_UNAVAIL;
- }
-
- client_start = (olen > 0);
-
- mutt_message _("Authenticating (SASL)...");
-
- snprintf (buf, sizeof (buf), "AUTH %s", mech);
- olen = strlen (buf);
-
- /* looping protocol */
- for (;;) {
- m_strcpy(buf + olen, sizeof(buf) - olen, "\r\n");
- mutt_socket_write (pop_data->conn, buf);
- if (mutt_socket_readln (inbuf, sizeof (inbuf), pop_data->conn) < 0) {
- sasl_dispose (&saslconn);
- pop_data->status = POP_DISCONNECTED;
- return POP_A_SOCKET;
- }
-
- if (rc != SASL_CONTINUE)
- break;
-
- if (!m_strncmp(inbuf, "+ ", 2)
- && sasl_decode64 (inbuf, strlen (inbuf), buf, LONG_STRING - 1,
- &len) != SASL_OK)
- {
- goto bail;
- }
-
- if (!client_start)
- for (;;) {
- rc = sasl_client_step (saslconn, buf, len, &interaction, &pc, &olen);
- if (rc != SASL_INTERACT)
- break;
- mutt_sasl_interact (interaction);
- }
- else
- client_start = 0;
-
- if (rc != SASL_CONTINUE && (olen == 0 || rc != SASL_OK))
- break;
-
- /* send out response, or line break if none needed */
- if (pc) {
- if (sasl_encode64 (pc, olen, buf, sizeof (buf), &olen) != SASL_OK) {
- goto bail;
- }
-
- /* sasl_client_st(art|ep) allocate pc with malloc, expect me to
- * free it */
- p_delete((char **)&pc);
- }
- }
-
- if (rc != SASL_OK)
- goto bail;
-
- if (!m_strncmp(inbuf, "+OK", 3)) {
- mutt_sasl_setup_conn (pop_data->conn, saslconn);
- return POP_A_SUCCESS;
- }
-
-bail:
- sasl_dispose (&saslconn);
-
- /* terminate SASL sessoin if the last responce is not +OK nor -ERR */
- if (!m_strncmp(inbuf, "+ ", 2)) {
- snprintf (buf, sizeof (buf), "*\r\n");
- if (pop_query (pop_data, buf, sizeof (buf)) == PQ_NOT_CONNECTED)
- return POP_A_SOCKET;
- }
-
- mutt_error _("SASL authentication failed.");
-
- mutt_sleep (2);
-
- return POP_A_FAILURE;
-}
-
-/* APOP authenticator */
-static pop_auth_res_t pop_auth_apop (pop_data_t * pop_data,
- const char *method __attribute__ ((unused)))
-{
- MD5_CTX mdContext;
- unsigned char digest[16];
- char hash[33];
- char buf[LONG_STRING];
- int i;
-
- if (!pop_data->timestamp)
- return POP_A_UNAVAIL;
-
- mutt_message _("Authenticating (APOP)...");
-
- /* Compute the authentication hash to send to the server */
- MD5Init (&mdContext);
- MD5Update (&mdContext, (unsigned char *) pop_data->timestamp,
- strlen (pop_data->timestamp));
- MD5Update (&mdContext, (unsigned char *) pop_data->conn->account.pass,
- strlen (pop_data->conn->account.pass));
- MD5Final (digest, &mdContext);
-
- for (i = 0; i < ssizeof(digest); i++)
- sprintf (hash + 2 * i, "%02x", digest[i]);
-
- /* Send APOP command to server */
- snprintf(buf, sizeof(buf), "APOP %s %s\r\n", pop_data->conn->account.user,
- hash);
-
- switch (pop_query (pop_data, buf, sizeof (buf))) {
- case PQ_OK:
- return POP_A_SUCCESS;
- case PQ_NOT_CONNECTED:
- return POP_A_SOCKET;
- case PFD_FUNCT_ERROR:
- case PQ_ERR:
- default:
- break;
- }
-
- mutt_error ("%s %s", _("APOP authentication failed."), pop_data->err_msg);
- mutt_sleep (2);
-
- return POP_A_FAILURE;
-}
-
-/* USER authenticator */
-static pop_auth_res_t pop_auth_user (pop_data_t * pop_data,
- const char *method __attribute__ ((unused)))
-{
- char buf[LONG_STRING];
- pop_query_status ret;
-
- if (pop_data->cmd_user == CMD_NOT_AVAILABLE)
- return POP_A_UNAVAIL;
-
- mutt_message _("Logging in...");
-
- snprintf (buf, sizeof (buf), "USER %s\r\n", pop_data->conn->account.user);
- ret = pop_query (pop_data, buf, sizeof (buf));
-
- if (pop_data->cmd_user == CMD_UNKNOWN) {
- if (ret == PQ_OK) {
- pop_data->cmd_user = CMD_AVAILABLE;
- }
-
- if (ret == PQ_ERR) {
- pop_data->cmd_user = CMD_NOT_AVAILABLE;
-
- snprintf (pop_data->err_msg, sizeof (pop_data->err_msg),
- _("Command USER is not supported by server."));
- }
- }
-
- if (ret == PQ_OK) {
- snprintf (buf, sizeof (buf), "PASS %s\r\n", pop_data->conn->account.pass);
- ret = pop_query (pop_data, buf, sizeof (buf));
- }
-
- switch (ret) {
- case PQ_OK:
- return POP_A_SUCCESS;
- case PQ_NOT_CONNECTED:
- return POP_A_SOCKET;
- case PFD_FUNCT_ERROR:
- case PQ_ERR:
- default:
- break;
- }
-
- mutt_error ("%s %s", _("Login failed."), pop_data->err_msg);
- mutt_sleep (2);
-
- return POP_A_FAILURE;
-}
-
-typedef struct {
- /* do authentication, using named method or any available if method is NULL */
- pop_auth_res_t (*authenticate) (pop_data_t *, const char *);
- /* name of authentication method supported, NULL means variable. If this
- * is not null, authenticate may ignore the second parameter. */
- const char *method;
-} pop_auth_t;
-
-/*
- * Authentication
- * 0 - successful,
- * -1 - conection lost,
- * -2 - login failed,
- * -3 - authentication canceled.
-*/
-static pop_query_status pop_authenticate (pop_data_t * pop_data)
-{
- static pop_auth_t const pop_authenticators[] = {
- {pop_auth_sasl, NULL},
- {pop_auth_apop, "apop"},
- {pop_auth_user, "user"},
- {NULL, NULL}
- };
-
- ACCOUNT *act = &pop_data->conn->account;
- const pop_auth_t *authenticator;
- char *methods;
- char *comma;
- char *method;
- int attempts = 0;
- int ret = POP_A_UNAVAIL;
-
- if (mutt_account_getuser (act) || !act->user[0] ||
- mutt_account_getpass (act) || !act->pass[0])
- return PFD_FUNCT_ERROR;
-
- if (PopAuthenticators && *PopAuthenticators) {
- /* Try user-specified list of authentication methods */
- methods = m_strdup(PopAuthenticators);
- method = methods;
-
- while (method) {
- comma = strchr (method, ':');
- if (comma)
- *comma++ = '\0';
- authenticator = pop_authenticators;
-
- while (authenticator->authenticate) {
- if (!authenticator->method ||
- !ascii_strcasecmp (authenticator->method, method)) {
- ret = authenticator->authenticate (pop_data, method);
- if (ret == POP_A_SOCKET) {
- if (pop_connect(pop_data) == PQ_OK) {
- ret = authenticator->authenticate (pop_data, method);
- } else {
- ret = POP_A_FAILURE;
- }
- }
-
- if (ret != POP_A_UNAVAIL)
- attempts++;
- if (ret == POP_A_SUCCESS || ret == POP_A_SOCKET ||
- (ret == POP_A_FAILURE && !option (OPTPOPAUTHTRYALL))) {
- comma = NULL;
- break;
- }
- }
- authenticator++;
- }
-
- method = comma;
- }
-
- p_delete(&methods);
- }
- else {
- /* Fall back to default: any authenticator */
- authenticator = pop_authenticators;
-
- while (authenticator->authenticate) {
- ret = authenticator->authenticate (pop_data, authenticator->method);
- if (ret == POP_A_SOCKET) {
- if (pop_connect(pop_data) == PQ_OK) {
- ret = authenticator->authenticate(pop_data, authenticator->method);
- } else {
- ret = POP_A_FAILURE;
- }
- }
-
- if (ret != POP_A_UNAVAIL)
- attempts++;
- if (ret == POP_A_SUCCESS || ret == POP_A_SOCKET ||
- (ret == POP_A_FAILURE && !option (OPTPOPAUTHTRYALL)))
- break;
-
- authenticator++;
- }
- }
-
- switch (ret) {
- case POP_A_SUCCESS:
- return PQ_OK;
- case POP_A_SOCKET:
- return PQ_NOT_CONNECTED;
- case POP_A_UNAVAIL:
- if (!attempts)
- mutt_error (_("No authenticators available"));
- }
-
- return PQ_ERR;
-}
-
/* given an POP mailbox name, return host, port, username and password */
static int pop_parse_path (const char *path, ACCOUNT * act)
{
return ret;
}
-/* }}} */
-
/*
* Open connection and authenticate
* 0 - successful,
static pop_query_status pop_open_connection (pop_data_t * pop_data)
{
pop_query_status ret;
- unsigned int n, size;
+ int n, size;
char buf[LONG_STRING];
ret = pop_connect(pop_data);