]> git.ipfire.org Git - thirdparty/dovecot/core.git/commitdiff
*-login: Use a common client_send_line() API.
authorTimo Sirainen <tss@iki.fi>
Sun, 9 Aug 2009 21:55:43 +0000 (17:55 -0400)
committerTimo Sirainen <tss@iki.fi>
Sun, 9 Aug 2009 21:55:43 +0000 (17:55 -0400)
--HG--
branch : HEAD

src/imap-login/client-authenticate.c
src/imap-login/client-authenticate.h
src/imap-login/client.c
src/imap-login/client.h
src/imap-login/imap-proxy.c
src/login-common/client-common.h
src/pop3-login/client-authenticate.c
src/pop3-login/client.c
src/pop3-login/client.h
src/pop3-login/pop3-proxy.c

index 2c9ec1045e052af3c14a015cdb3c53e80c9690de..94caaf4914dcb124372eb98df27972bdc12fc038 100644 (file)
@@ -189,6 +189,8 @@ static bool client_handle_args(struct imap_client *client,
                   .. [REFERRAL ..] (Reason from auth server)
                */
                reply = t_str_new(128);
+               str_append(reply, client->cmd_tag);
+               str_append_c(reply, ' ');
                str_append(reply, nologin ? "NO " : "OK ");
                str_printfa(reply, "[REFERRAL imap://%s;AUTH=%s@%s",
                            destuser, client->common.auth_mech_name, host);
@@ -203,7 +205,8 @@ static bool client_handle_args(struct imap_client *client,
                        str_append(reply, "Logged in, but you should use "
                                   "this server instead.");
                }
-               client_send_tagline(client, str_c(reply));
+               str_append(reply, "\r\n");
+               client_send_raw(client, str_c(reply));
                if (!nologin) {
                        client_destroy_success(client, "Login with referral");
                        return TRUE;
@@ -211,18 +214,23 @@ static bool client_handle_args(struct imap_client *client,
        } else if (nologin) {
                /* Authentication went ok, but for some reason user isn't
                   allowed to log in. Shouldn't probably happen. */
-               reply = t_str_new(128);
-               if (reason != NULL)
-                       str_printfa(reply, "NO [ALERT] %s", reason);
-               else if (temp) {
-                       str_append(reply, "NO ["IMAP_RESP_CODE_UNAVAILABLE"] "
-                                  AUTH_TEMP_FAILED_MSG);
+               if (reason != NULL) {
+                       client_send_line(&client->common,
+                                        CLIENT_CMD_REPLY_AUTH_FAIL_REASON,
+                                        reason);
+               } else if (temp) {
+                       client_send_line(&client->common,
+                                        CLIENT_CMD_REPLY_AUTH_FAIL_TEMP,
+                                        AUTH_TEMP_FAILED_MSG);
                } else if (authz_failure) {
-                       str_append(reply, "NO "IMAP_AUTHZ_FAILED_MSG);
+                       client_send_line(&client->common,
+                                        CLIENT_CMD_REPLY_AUTHZ_FAILED,
+                                        "Authorization failed");
                } else {
-                       str_append(reply, "NO "IMAP_AUTH_FAILED_MSG);
+                       client_send_line(&client->common,
+                                        CLIENT_CMD_REPLY_AUTH_FAILED,
+                                        AUTH_FAILED_MSG);
                }
-               client_send_tagline(client, str_c(reply));
        } else {
                /* normal login/failure */
                return FALSE;
@@ -240,7 +248,6 @@ static void sasl_callback(struct client *_client, enum sasl_server_reply reply,
 {
        struct imap_client *client = (struct imap_client *)_client;
        struct const_iovec iov[3];
-       const char *msg;
        size_t data_len;
        bool nodelay;
 
@@ -267,13 +274,18 @@ static void sasl_callback(struct client *_client, enum sasl_server_reply reply,
                                break;
                }
 
-               if (reply == SASL_SERVER_REPLY_AUTH_ABORTED)
-                       msg = "BAD Authentication aborted by client.";
-               else if (data == NULL)
-                       msg = "NO "IMAP_AUTH_FAILED_MSG;
-               else
-                       msg = t_strconcat("NO [ALERT] ", data, NULL);
-               client_send_tagline(client, msg);
+               if (reply == SASL_SERVER_REPLY_AUTH_ABORTED) {
+                       client_send_line(&client->common, CLIENT_CMD_REPLY_BAD,
+                                        "Authentication aborted by client.");
+               } else if (data == NULL) {
+                       client_send_line(&client->common,
+                                        CLIENT_CMD_REPLY_AUTH_FAILED,
+                                        AUTH_FAILED_MSG);
+               } else {
+                       client_send_line(&client->common,
+                                        CLIENT_CMD_REPLY_AUTH_FAIL_REASON,
+                                        data);
+               }
 
                if (!client->destroyed)
                        client_auth_failed(client, nodelay);
@@ -282,8 +294,8 @@ static void sasl_callback(struct client *_client, enum sasl_server_reply reply,
                if (data == NULL)
                        client_destroy_internal_failure(client);
                else {
-                       client_send_tagline(client,
-                                           t_strconcat("NO ", data, NULL));
+                       client_send_line(&client->common,
+                                        CLIENT_CMD_REPLY_AUTH_FAIL_TEMP, data);
                        /* authentication itself succeeded, we just hit some
                           internal failure. */
                        client_destroy_success(client, data);
@@ -365,8 +377,8 @@ int cmd_authenticate(struct imap_client *client, const struct imap_arg *args)
                                      "SSL required for authentication");
                }
                client->common.auth_attempts++;
-               client_send_tagline(client,
-                       "NO ["IMAP_RESP_CODE_PRIVACYREQUIRED"] "
+               client_send_line(&client->common,
+                       CLIENT_CMD_REPLY_AUTH_FAIL_NOSSL,
                        "Authentication not allowed until SSL/TLS is enabled.");
                return 1;
        }
@@ -401,12 +413,13 @@ int cmd_login(struct imap_client *client, const struct imap_arg *args)
                }
                client->common.auth_tried_disabled_plaintext = TRUE;
                client->common.auth_attempts++;
-               client_send_line(client,
+               client_send_raw(client,
                        "* BAD [ALERT] Plaintext authentication not allowed "
                        "without SSL/TLS, but your client did it anyway. "
-                       "If anyone was listening, the password was exposed.");
-               client_send_tagline(client, "NO ["IMAP_RESP_CODE_CLIENTBUG"] "
-                                   AUTH_PLAINTEXT_DISABLED_MSG);
+                       "If anyone was listening, the password was exposed.\r\n");
+               client_send_line(&client->common,
+                                CLIENT_CMD_REPLY_AUTH_FAIL_NOSSL,
+                                AUTH_PLAINTEXT_DISABLED_MSG);
                return 1;
        }
 
index b5e2d393d2f98e9ffaa23ea56836d965a5edb431..1f2a347e476dcec2089809b08155e63857d891c2 100644 (file)
@@ -3,11 +3,6 @@
 
 struct imap_arg;
 
-#define IMAP_AUTH_FAILED_MSG \
-       "["IMAP_RESP_CODE_AUTHFAILED"] "AUTH_FAILED_MSG
-#define IMAP_AUTHZ_FAILED_MSG \
-       "["IMAP_RESP_CODE_AUTHZFAILED"] Authorization failed"
-
 const char *client_authenticate_get_capabilities(struct imap_client *client);
 
 int cmd_login(struct imap_client *client, const struct imap_arg *args);
index 0aebede1004d8dda1bb1314a2340e3387c23bd63..fbafcf1027cebe1569a31ca98d6b1e9b5c2e938c 100644 (file)
@@ -11,6 +11,7 @@
 #include "strescape.h"
 #include "imap-parser.h"
 #include "imap-id.h"
+#include "imap-resp-code.h"
 #include "master-service.h"
 #include "master-auth.h"
 #include "client.h"
@@ -45,9 +46,9 @@
 #endif
 
 #define AUTH_SERVER_WAITING_MSG \
-       "* OK Waiting for authentication process to respond.."
+       "Waiting for authentication process to respond.."
 #define AUTH_MASTER_WAITING_MSG \
-       "* OK Waiting for authentication master process to respond.."
+       "Waiting for authentication master process to respond.."
 
 const char *login_protocol = "imap";
 const char *login_process_name = "imap-login";
@@ -116,9 +117,10 @@ static int cmd_capability(struct imap_client *client)
           CAPABILITY commands. */
        if (!client->starttls)
                client->client_ignores_capability_resp_code = TRUE;
-       client_send_line(client, t_strconcat(
-               "* CAPABILITY ", get_capability(client), NULL));
-       client_send_tagline(client, "OK Capability completed.");
+       client_send_raw(client, t_strconcat(
+               "* CAPABILITY ", get_capability(client), "\r\n", NULL));
+       client_send_line(&client->common, CLIENT_CMD_REPLY_OK,
+                        "Capability completed.");
        return 1;
 }
 
@@ -133,7 +135,8 @@ static void client_start_tls(struct imap_client *client)
        fd_ssl = ssl_proxy_new(client->common.fd, &client->common.ip,
                               client->common.set, &client->common.proxy);
        if (fd_ssl == -1) {
-               client_send_line(client, "* BYE TLS initialization failed.");
+               client_send_line(&client->common, CLIENT_CMD_REPLY_BYE,
+                                "TLS initialization failed.");
                client_destroy(client,
                               "Disconnected: TLS initialization failed.");
                return;
@@ -176,12 +179,14 @@ static int client_output_starttls(struct imap_client *client)
 static int cmd_starttls(struct imap_client *client)
 {
        if (client->common.tls) {
-               client_send_tagline(client, "BAD TLS is already active.");
+               client_send_line(&client->common, CLIENT_CMD_REPLY_BAD,
+                                "TLS is already active.");
                return 1;
        }
 
        if (!ssl_initialized) {
-               client_send_tagline(client, "BAD TLS support isn't enabled.");
+               client_send_line(&client->common, CLIENT_CMD_REPLY_BAD,
+                                "TLS support isn't enabled.");
                return 1;
        }
 
@@ -190,7 +195,8 @@ static int cmd_starttls(struct imap_client *client)
        if (client->io != NULL)
                io_remove(&client->io);
 
-       client_send_tagline(client, "OK Begin TLS negotiation now.");
+       client_send_line(&client->common, CLIENT_CMD_REPLY_OK,
+                        "Begin TLS negotiation now.");
 
        /* uncork the old fd */
        o_stream_uncork(client->output);
@@ -249,31 +255,34 @@ static int cmd_id(struct imap_client *client, const struct imap_arg *args)
        }
 
        env = getenv("IMAP_ID_SEND");
-       client_send_line(client, t_strdup_printf("* ID %s",
-                                                imap_id_reply_generate(env)));
-       client_send_tagline(client, "OK ID completed.");
+       client_send_raw(client,
+               t_strdup_printf("* ID %s\r\n", imap_id_reply_generate(env)));
+       client_send_line(&client->common, CLIENT_CMD_REPLY_OK, "ID completed.");
        return 1;
 }
 
 static int cmd_noop(struct imap_client *client)
 {
-       client_send_tagline(client, "OK NOOP completed.");
+       client_send_line(&client->common, CLIENT_CMD_REPLY_OK,
+                        "NOOP completed.");
        return 1;
 }
 
 static int cmd_logout(struct imap_client *client)
 {
-       client_send_line(client, "* BYE Logging out");
-       client_send_tagline(client, "OK Logout completed.");
+       client_send_line(&client->common, CLIENT_CMD_REPLY_BYE,
+                        "Logging out");
+       client_send_line(&client->common, CLIENT_CMD_REPLY_OK,
+                        "Logout completed.");
        client_destroy(client, "Aborted login");
        return 1;
 }
 
 static int cmd_enable(struct imap_client *client)
 {
-       client_send_line(client, "* ENABLED");
-       client_send_tagline(client,
-                           "OK ENABLE ignored in non-authenticated state.");
+       client_send_raw(client, "* ENABLED\r\n");
+       client_send_line(&client->common, CLIENT_CMD_REPLY_OK,
+                        "ENABLE ignored in non-authenticated state.");
        return 1;
 }
 
@@ -345,14 +354,14 @@ static bool client_handle_input(struct imap_client *client)
                /* error */
                msg = imap_parser_get_error(client->parser, &fatal);
                if (fatal) {
-                       client_send_line(client, t_strconcat("* BYE ",
-                                                            msg, NULL));
+                       client_send_line(&client->common,
+                                        CLIENT_CMD_REPLY_BYE, msg);
                        client_destroy(client,
                                t_strconcat("Disconnected: ", msg, NULL));
                        return FALSE;
                }
 
-               client_send_tagline(client, t_strconcat("BAD ", msg, NULL));
+               client_send_line(&client->common, CLIENT_CMD_REPLY_BAD, msg);
                client->cmd_finished = TRUE;
                client->skip_line = TRUE;
                return TRUE;
@@ -374,14 +383,14 @@ static bool client_handle_input(struct imap_client *client)
                if (*client->cmd_tag == '\0')
                        client->cmd_tag = "*";
                if (++client->bad_counter >= CLIENT_MAX_BAD_COMMANDS) {
-                       client_send_line(client,
-                               "* BYE Too many invalid IMAP commands.");
+                       client_send_line(&client->common, CLIENT_CMD_REPLY_BYE,
+                               "Too many invalid IMAP commands.");
                        client_destroy(client,
                                "Disconnected: Too many invalid commands");
                        return FALSE;
                }  
-               client_send_tagline(client,
-                       "BAD Error in IMAP command received by server.");
+               client_send_line(&client->common, CLIENT_CMD_REPLY_BAD,
+                       "Error in IMAP command received by server.");
        }
 
        return ret != 0;
@@ -392,7 +401,8 @@ bool client_read(struct imap_client *client)
        switch (i_stream_read(client->common.input)) {
        case -2:
                /* buffer full */
-               client_send_line(client, "* BYE Input buffer full, aborting");
+               client_send_line(&client->common, CLIENT_CMD_REPLY_BYE,
+                                "Input buffer full, aborting");
                client_destroy(client, "Disconnected: Input buffer full");
                return FALSE;
        case -1:
@@ -419,7 +429,8 @@ void client_input(struct imap_client *client)
        if (!auth_client_is_connected(auth_client)) {
                /* we're not yet connected to auth process -
                   don't allow any commands */
-               client_send_line(client, AUTH_SERVER_WAITING_MSG);
+               client_send_line(&client->common, CLIENT_CMD_REPLY_STATUS,
+                                AUTH_SERVER_WAITING_MSG);
                if (client->to_auth_waiting != NULL)
                        timeout_remove(&client->to_auth_waiting);
 
@@ -480,20 +491,23 @@ static void client_send_greeting(struct imap_client *client)
        str_append(greet, "* OK ");
        str_printfa(greet, "[CAPABILITY %s] ", get_capability(client));
        str_append(greet, client->common.set->login_greeting);
+       str_append(greet, "\r\n");
 
-       client_send_line(client, str_c(greet));
+       client_send_raw(client, str_c(greet));
        client->greeting_sent = TRUE;
 }
 
 static void client_idle_disconnect_timeout(struct imap_client *client)
 {
-       client_send_line(client, "* BYE Disconnected for inactivity.");
+       client_send_line(&client->common, CLIENT_CMD_REPLY_BAD,
+                        "Disconnected for inactivity.");
        client_destroy(client, "Disconnected: Inactivity");
 }
 
 static void client_auth_waiting_timeout(struct imap_client *client)
 {
-       client_send_line(client, client->common.master_tag == 0 ?
+       client_send_line(&client->common, CLIENT_CMD_REPLY_STATUS,
+                        client->common.master_tag == 0 ?
                         AUTH_SERVER_WAITING_MSG : AUTH_MASTER_WAITING_MSG);
        timeout_remove(&client->to_auth_waiting);
 }
@@ -631,7 +645,8 @@ void client_destroy_success(struct imap_client *client, const char *reason)
 
 void client_destroy_internal_failure(struct imap_client *client)
 {
-       client_send_line(client, "* BYE Internal login failure. "
+       client_send_line(&client->common, CLIENT_CMD_REPLY_BYE,
+                        "Internal login failure. "
                         "Refer to server log for more information.");
        client_destroy(client, "Internal login failure");
 }
@@ -667,29 +682,84 @@ bool client_unref(struct imap_client *client)
        return FALSE;
 }
 
-void client_send_line(struct imap_client *client, const char *line)
+static void
+client_send_raw_data(struct imap_client *client, const void *data, size_t size)
 {
-       struct const_iovec iov[2];
        ssize_t ret;
 
-       iov[0].iov_base = line;
-       iov[0].iov_len = strlen(line);
-       iov[1].iov_base = "\r\n";
-       iov[1].iov_len = 2;
-
-       ret = o_stream_sendv(client->output, iov, 2);
-       if (ret < 0 || (size_t)ret != iov[0].iov_len + iov[1].iov_len) {
-               /* either disconnection or buffer full. in either case we
-                  want this connection destroyed. however destroying it here
-                  might break things if client is still tried to be accessed
-                  without being referenced.. */
+       ret = o_stream_send(client->output, data, size);
+       if (ret < 0 || (size_t)ret != size) {
+               /* either disconnection or buffer full. in either case we want
+                  this connection destroyed. however destroying it here might
+                  break things if client is still tried to be accessed without
+                  being referenced.. */
                i_stream_close(client->common.input);
        }
 }
 
-void client_send_tagline(struct imap_client *client, const char *line)
-{
-       client_send_line(client, t_strconcat(client->cmd_tag, " ", line, NULL));
+void client_send_raw(struct imap_client *client, const char *data)
+{
+       client_send_raw_data(client, data, strlen(data));
+}
+
+void client_send_line(struct client *client, enum client_cmd_reply reply,
+                     const char *text)
+{
+       struct imap_client *imap_client = (struct imap_client *)client;
+       const char *resp_code = NULL;
+       const char *prefix = "NO";
+       bool tagged = TRUE;
+
+       switch (reply) {
+       case CLIENT_CMD_REPLY_OK:
+               prefix = "OK";
+               break;
+       case CLIENT_CMD_REPLY_AUTH_FAILED:
+               resp_code = IMAP_RESP_CODE_AUTHFAILED;
+               break;
+       case CLIENT_CMD_REPLY_AUTHZ_FAILED:
+               resp_code = IMAP_RESP_CODE_AUTHZFAILED;
+               break;
+       case CLIENT_CMD_REPLY_AUTH_FAIL_TEMP:
+               resp_code = IMAP_RESP_CODE_UNAVAILABLE;
+               break;
+       case CLIENT_CMD_REPLY_AUTH_FAIL_REASON:
+               resp_code = "ALERT";
+               break;
+       case CLIENT_CMD_REPLY_AUTH_FAIL_NOSSL:
+               resp_code = IMAP_RESP_CODE_PRIVACYREQUIRED;
+               break;
+       case CLIENT_CMD_REPLY_BAD:
+               prefix = "BAD";
+               break;
+       case CLIENT_CMD_REPLY_BYE:
+               prefix = "BYE";
+               tagged = FALSE;
+               break;
+       case CLIENT_CMD_REPLY_STATUS:
+               prefix = "OK";
+               tagged = FALSE;
+               break;
+       }
+
+       T_BEGIN {
+               string_t *line = t_str_new(256);
+
+               if (tagged)
+                       str_append(line, imap_client->cmd_tag);
+               else
+                       str_append_c(line, '*');
+               str_append_c(line, ' ');
+               str_append(line, prefix);
+               str_append_c(line, ' ');
+               if (resp_code != NULL)
+                       str_printfa(line, "[%s] ", resp_code);
+               str_append(line, text);
+               str_append(line, "\r\n");
+
+               client_send_raw_data(imap_client, str_data(line),
+                                    str_len(line));
+       } T_END;
 }
 
 void clients_notify_auth_connected(void)
index 13277fbdaab46323959c901c652c822193e273e9..76f1f38294e1f9dc5d27cac0c1d9512601d64091 100644 (file)
@@ -45,13 +45,11 @@ void client_destroy(struct imap_client *client, const char *reason);
 void client_destroy_success(struct imap_client *client, const char *reason);
 void client_destroy_internal_failure(struct imap_client *client);
 
-void client_send_line(struct imap_client *client, const char *line);
-void client_send_tagline(struct imap_client *client, const char *line);
-
 bool client_read(struct imap_client *client);
 bool client_skip_line(struct imap_client *client);
 void client_input(struct imap_client *client);
 
+void client_send_raw(struct imap_client *client, const char *data);
 void client_ref(struct imap_client *client);
 bool client_unref(struct imap_client *client);
 
index f4aa33692036d011f98f9abbbdfd83de5a2799da..cb4d44da66158aaa4b365088bf85c43e35ab545f 100644 (file)
@@ -17,9 +17,6 @@
 
 #include <stdlib.h>
 
-#define PROXY_FAILURE_MSG \
-       "NO ["IMAP_RESP_CODE_UNAVAILABLE"] "AUTH_TEMP_FAILED_MSG
-
 static void proxy_write_id(struct imap_client *client, string_t *str)
 {
        str_printfa(str, "I ID ("
@@ -44,8 +41,11 @@ static void proxy_free_password(struct imap_client *client)
 
 static void proxy_failed(struct imap_client *client, bool send_tagline)
 {
-       if (send_tagline)
-               client_send_tagline(client, PROXY_FAILURE_MSG);
+       if (send_tagline) {
+               client_send_line(&client->common,
+                                CLIENT_CMD_REPLY_AUTH_FAIL_TEMP,
+                                AUTH_TEMP_FAILED_MSG);
+       }
 
        login_proxy_free(&client->proxy);
        proxy_free_password(client);
@@ -271,9 +271,13 @@ static int proxy_input_line(struct imap_client *client, const char *line)
                           the remote is sending a different error message
                           an attacker can't find out what users exist in
                           the system. */
-                       line = "NO "IMAP_AUTH_FAILED_MSG;
+                       client_send_line(&client->common,
+                                        CLIENT_CMD_REPLY_AUTH_FAILED,
+                                        AUTH_FAILED_MSG);
                } else if (strncmp(line, "NO [", 4) == 0) {
                        /* remote sent some other resp-code. forward it. */
+                       client_send_raw(client, t_strconcat(
+                               client->cmd_tag, " ", line, "\r\n", NULL));
                } else {
                        /* there was no [resp-code], so remote isn't Dovecot
                           v1.2+. we could either forward the line as-is and
@@ -282,9 +286,10 @@ static int proxy_input_line(struct imap_client *client, const char *line)
                           failures. since other errors are pretty rare,
                           it's safer to just hide them. they're still
                           available in logs though. */
-                       line = "NO "IMAP_AUTH_FAILED_MSG;
+                       client_send_line(&client->common,
+                                        CLIENT_CMD_REPLY_AUTH_FAILED,
+                                        AUTH_FAILED_MSG);
                }
-               client_send_tagline(client, line);
 
                proxy_failed(client, FALSE);
                return -1;
@@ -298,7 +303,7 @@ static int proxy_input_line(struct imap_client *client, const char *line)
                return 0;
        } else if (strncmp(line, "* ", 2) == 0) {
                /* untagged reply. just foward it. */
-               client_send_line(client, line);
+               client_send_raw(client, t_strconcat(line, "\r\n", NULL));
                return 0;
        } else {
                /* tagged reply, shouldn't happen. */
@@ -358,7 +363,9 @@ int imap_proxy_new(struct imap_client *client, const char *host,
 
        if (password == NULL) {
                client_syslog_err(&client->common, "proxy: password not given");
-               client_send_tagline(client, PROXY_FAILURE_MSG);
+               client_send_line(&client->common,
+                                CLIENT_CMD_REPLY_AUTH_FAIL_TEMP,
+                                AUTH_TEMP_FAILED_MSG);
                return -1;
        }
 
@@ -371,14 +378,18 @@ int imap_proxy_new(struct imap_client *client, const char *host,
        }
        if (login_proxy_is_ourself(&client->common, host, port, user)) {
                client_syslog_err(&client->common, "Proxying loops to itself");
-               client_send_tagline(client, PROXY_FAILURE_MSG);
+               client_send_line(&client->common,
+                                CLIENT_CMD_REPLY_AUTH_FAIL_TEMP,
+                                AUTH_TEMP_FAILED_MSG);
                return -1;
        }
 
        client->proxy = login_proxy_new(&client->common, host, port, ssl_flags,
                                        proxy_input, client);
        if (client->proxy == NULL) {
-               client_send_tagline(client, PROXY_FAILURE_MSG);
+               client_send_line(&client->common,
+                                CLIENT_CMD_REPLY_AUTH_FAIL_TEMP,
+                                AUTH_TEMP_FAILED_MSG);
                return -1;
        }
 
index 9c744106721ad1b885882f4db98ca0f463efc0b9..2a0f1f39d4cb2802760c01a5ed26ba030a788a8c 100644 (file)
 */
 #define LOGIN_MAX_INBUF_SIZE 4096
 
+enum client_cmd_reply {
+       CLIENT_CMD_REPLY_OK,
+       CLIENT_CMD_REPLY_AUTH_FAILED,
+       CLIENT_CMD_REPLY_AUTHZ_FAILED,
+       CLIENT_CMD_REPLY_AUTH_FAIL_TEMP,
+       CLIENT_CMD_REPLY_AUTH_FAIL_REASON,
+       CLIENT_CMD_REPLY_AUTH_FAIL_NOSSL,
+       CLIENT_CMD_REPLY_BAD,
+       CLIENT_CMD_REPLY_BYE,
+       CLIENT_CMD_REPLY_STATUS
+};
+
 struct client {
        struct client *prev, *next;
        pool_t pool;
@@ -57,6 +69,9 @@ void client_link(struct client *client);
 void client_unlink(struct client *client);
 unsigned int clients_get_count(void) ATTR_PURE;
 
+void client_send_line(struct client *client, enum client_cmd_reply reply,
+                     const char *text);
+
 void client_syslog(struct client *client, const char *msg);
 void client_syslog_err(struct client *client, const char *msg);
 const char *client_get_extra_disconnect_reason(struct client *client);
index 6a4e29cc6e406d943c1bf326091cf5fc75d075d2..2c3ec1d6ff95cbefc3addff1da62cb1f795834fc 100644 (file)
@@ -46,9 +46,9 @@ bool cmd_capa(struct pop3_client *client, const char *args ATTR_UNUSED)
                str_append_c(str, ' ');
                str_append(str, mech[i].name);
        }
-       str_append(str, "\r\n.");
+       str_append(str, "\r\n.\r\n");
 
-       client_send_line(client, str_c(str));
+       client_send_raw(client, str_c(str));
        return TRUE;
 }
 
@@ -121,7 +121,6 @@ static bool client_handle_args(struct pop3_client *client,
        const char *master_user = NULL;
        const char *key, *value, *p;
        enum login_proxy_ssl_flags ssl_flags = 0;
-       string_t *reply;
        unsigned int port = 110;
        bool proxy = FALSE, temp = FALSE, nologin = !success;
 
@@ -181,16 +180,17 @@ static bool client_handle_args(struct pop3_client *client,
        if (!nologin)
                return FALSE;
 
-       reply = t_str_new(128);
-       str_append(reply, "-ERR ");
-       if (reason != NULL)
-               str_append(reply, reason);
-       else if (temp)
-               str_append(reply, "[IN-USE] "AUTH_TEMP_FAILED_MSG);
-       else
-               str_append(reply, AUTH_FAILED_MSG);
-
-       client_send_line(client, str_c(reply));
+       if (reason != NULL) {
+               client_send_line(&client->common, CLIENT_CMD_REPLY_AUTH_FAILED,
+                                reason);
+       } else if (temp) {
+               client_send_line(&client->common,
+                                CLIENT_CMD_REPLY_AUTH_FAIL_TEMP,
+                                AUTH_TEMP_FAILED_MSG);
+       } else {
+               client_send_line(&client->common, CLIENT_CMD_REPLY_AUTH_FAILED,
+                                AUTH_FAILED_MSG);
+       }
 
        if (!client->destroyed)
                client_auth_failed(client, *nodelay_r);
@@ -202,7 +202,6 @@ static void sasl_callback(struct client *_client, enum sasl_server_reply reply,
 {
        struct pop3_client *client = (struct pop3_client *)_client;
        struct const_iovec iov[3];
-       const char *msg;
        size_t data_len;
        bool nodelay;
 
@@ -226,13 +225,18 @@ static void sasl_callback(struct client *_client, enum sasl_server_reply reply,
                                break;
                }
 
-               if (reply == SASL_SERVER_REPLY_AUTH_ABORTED)
-                       msg = "-ERR Authentication aborted by client.";
-               else if (data == NULL)
-                       msg = "-ERR "AUTH_FAILED_MSG;
-               else
-                       msg = t_strconcat("-ERR ", data, NULL);
-               client_send_line(client, msg);
+               if (reply == SASL_SERVER_REPLY_AUTH_ABORTED) {
+                       client_send_line(&client->common, CLIENT_CMD_REPLY_BAD,
+                                        "Authentication aborted by client.");
+               } else if (data == NULL) {
+                       client_send_line(&client->common,
+                                        CLIENT_CMD_REPLY_AUTH_FAILED,
+                                        AUTH_FAILED_MSG);
+               } else {
+                       client_send_line(&client->common,
+                                        CLIENT_CMD_REPLY_AUTH_FAIL_REASON,
+                                        data);
+               }
 
                if (!client->destroyed)
                        client_auth_failed(client, nodelay);
@@ -241,8 +245,10 @@ static void sasl_callback(struct client *_client, enum sasl_server_reply reply,
                if (data == NULL)
                        client_destroy_internal_failure(client);
                else {
-                       client_send_line(client,
-                               t_strconcat("-ERR [IN-USE] ", data, NULL));
+                       client_send_line(&client->common,
+                                        CLIENT_CMD_REPLY_AUTH_FAIL_TEMP, data);
+                       /* authentication itself succeeded, we just hit some
+                          internal failure. */
                        client_destroy_success(client, data);
                }
                break;
@@ -281,8 +287,9 @@ bool cmd_auth(struct pop3_client *client, const char *args)
                                      "SSL required for authentication");
                }
                client->common.auth_attempts++;
-               client_send_line(client, "-ERR Authentication not allowed "
-                                "until SSL/TLS is enabled.");
+               client_send_line(&client->common,
+                       CLIENT_CMD_REPLY_AUTH_FAIL_NOSSL,
+                       "Authentication not allowed until SSL/TLS is enabled.");
                return TRUE;
        }
 
@@ -290,16 +297,18 @@ bool cmd_auth(struct pop3_client *client, const char *args)
                /* Old-style SASL discovery, used by MS Outlook */
                unsigned int i, count;
 
-               client_send_line(client, "+OK");
+               client_send_raw(client, "+OK\r\n");
                mech = auth_client_get_available_mechs(auth_client, &count);
                for (i = 0; i < count; i++) {
                        if ((mech[i].flags & MECH_SEC_PRIVATE) == 0 &&
                            (client->common.secured ||
                             client->common.set->disable_plaintext_auth ||
-                            (mech[i].flags & MECH_SEC_PLAINTEXT) == 0))
-                               client_send_line(client, mech[i].name);
+                            (mech[i].flags & MECH_SEC_PLAINTEXT) == 0)) {
+                               client_send_raw(client, mech[i].name);
+                               client_send_raw(client, "\r\n");
+                       }
                }
-               client_send_line(client, ".");
+               client_send_raw(client, ".\r\n");
                return TRUE;
        }
 
@@ -335,7 +344,8 @@ static bool check_plaintext_auth(struct pop3_client *client)
                client_syslog(&client->common, "Login failed: "
                              "Plaintext authentication disabled");
        }
-       client_send_line(client, "-ERR "AUTH_PLAINTEXT_DISABLED_MSG);
+       client_send_line(&client->common, CLIENT_CMD_REPLY_AUTH_FAIL_NOSSL,
+                        AUTH_PLAINTEXT_DISABLED_MSG);
        client->common.auth_tried_disabled_plaintext = TRUE;
        client->common.auth_attempts++;
        return FALSE;
@@ -349,7 +359,7 @@ bool cmd_user(struct pop3_client *client, const char *args)
        i_free(client->last_user);
        client->last_user = i_strdup(args);
 
-       client_send_line(client, "+OK");
+       client_send_raw(client, "+OK\r\n");
        return TRUE;
 }
 
@@ -363,7 +373,8 @@ bool cmd_pass(struct pop3_client *client, const char *args)
                if (!check_plaintext_auth(client))
                        return TRUE;
 
-               client_send_line(client, "-ERR No username given.");
+               client_send_line(&client->common, CLIENT_CMD_REPLY_BAD,
+                                "No username given.");
                return TRUE;
        }
 
@@ -405,7 +416,8 @@ bool cmd_apop(struct pop3_client *client, const char *args)
                        client_syslog(&client->common,
                                      "APOP failed: APOP not enabled");
                }
-               client_send_line(client, "-ERR APOP not enabled.");
+               client_send_line(&client->common, CLIENT_CMD_REPLY_BAD,
+                                "APOP not enabled.");
                return TRUE;
        }
 
@@ -416,7 +428,8 @@ bool cmd_apop(struct pop3_client *client, const char *args)
                        client_syslog(&client->common,
                                      "APOP failed: Invalid parameters");
                }
-               client_send_line(client, "-ERR Invalid parameters.");
+               client_send_line(&client->common, CLIENT_CMD_REPLY_BAD,
+                                "Invalid parameters.");
                return TRUE;
        }
 
@@ -432,8 +445,8 @@ bool cmd_apop(struct pop3_client *client, const char *args)
                        client_syslog(&client->common, "APOP failed: "
                                      "Invalid characters in MD5 response");
                }
-               client_send_line(client,
-                                "-ERR Invalid characters in MD5 response.");
+               client_send_line(&client->common, CLIENT_CMD_REPLY_BAD,
+                                "Invalid characters in MD5 response.");
                return TRUE;
        }
 
index 5c1922b1d4a55c2ef590bcac6185c4df7977cb8a..d8683233bf5a0bb96e9d3e57fbfdc7e80df62935 100644 (file)
@@ -9,6 +9,7 @@
 #include "randgen.h"
 #include "process-title.h"
 #include "safe-memset.h"
+#include "str.h"
 #include "strescape.h"
 #include "master-service.h"
 #include "master-auth.h"
@@ -72,7 +73,8 @@ static void client_start_tls(struct pop3_client *client)
        fd_ssl = ssl_proxy_new(client->common.fd, &client->common.ip,
                               client->common.set, &client->common.proxy);
        if (fd_ssl == -1) {
-               client_send_line(client, "-ERR TLS initialization failed.");
+               client_send_line(&client->common, CLIENT_CMD_REPLY_BYE,
+                                "TLS initialization failed.");
                client_destroy(client,
                               "Disconnected: TLS initialization failed.");
                return;
@@ -111,12 +113,14 @@ static int client_output_starttls(struct pop3_client *client)
 static bool cmd_stls(struct pop3_client *client)
 {
        if (client->common.tls) {
-               client_send_line(client, "-ERR TLS is already active.");
+               client_send_line(&client->common, CLIENT_CMD_REPLY_BAD,
+                                "TLS is already active.");
                return TRUE;
        }
 
        if (!ssl_initialized) {
-               client_send_line(client, "-ERR TLS support isn't enabled.");
+               client_send_line(&client->common, CLIENT_CMD_REPLY_BAD,
+                                "TLS support isn't enabled.");
                return TRUE;
        }
 
@@ -125,7 +129,8 @@ static bool cmd_stls(struct pop3_client *client)
        if (client->io != NULL)
                io_remove(&client->io);
 
-       client_send_line(client, "+OK Begin TLS negotiation now.");
+       client_send_line(&client->common, CLIENT_CMD_REPLY_OK,
+                        "Begin TLS negotiation now.");
 
        /* uncork the old fd */
        o_stream_uncork(client->output);
@@ -143,7 +148,7 @@ static bool cmd_stls(struct pop3_client *client)
 
 static bool cmd_quit(struct pop3_client *client)
 {
-       client_send_line(client, "+OK Logging out");
+       client_send_line(&client->common, CLIENT_CMD_REPLY_OK, "Logging out");
        client_destroy(client, "Aborted login");
        return TRUE;
 }
@@ -167,7 +172,8 @@ static bool client_command_execute(struct pop3_client *client, const char *cmd,
        if (strcmp(cmd, "QUIT") == 0)
                return cmd_quit(client);
 
-       client_send_line(client, "-ERR Unknown command.");
+       client_send_line(&client->common, CLIENT_CMD_REPLY_BAD,
+                        "Unknown command.");
        return FALSE;
 }
 
@@ -176,7 +182,8 @@ bool client_read(struct pop3_client *client)
        switch (i_stream_read(client->common.input)) {
        case -2:
                /* buffer full */
-               client_send_line(client, "-ERR Input line too long, aborting");
+               client_send_line(&client->common, CLIENT_CMD_REPLY_BYE,
+                                "Input buffer full, aborting");
                client_destroy(client, "Disconnected: Input buffer full");
                return FALSE;
        case -1:
@@ -217,7 +224,8 @@ void client_input(struct pop3_client *client)
                                           args != NULL ? args : ""))
                        client->bad_counter = 0;
                else if (++client->bad_counter > CLIENT_MAX_BAD_COMMANDS) {
-                       client_send_line(client, "-ERR Too many bad commands.");
+                       client_send_line(&client->common, CLIENT_CMD_REPLY_BYE,
+                               "Too many invalid IMAP commands.");
                        client_destroy(client,
                                       "Disconnected: Too many bad commands");
                }
@@ -292,11 +300,14 @@ static void client_auth_ready(struct pop3_client *client)
        client->io = io_add(client->common.fd, IO_READ, client_input, client);
 
        client->apop_challenge = get_apop_challenge(client);
-       client_send_line(client, t_strconcat("+OK ",
-                                            client->common.set->login_greeting,
-                                            client->apop_challenge != NULL ?
-                                            " " : NULL,
-                                            client->apop_challenge, NULL));
+       if (client->apop_challenge == NULL) {
+               client_send_line(&client->common, CLIENT_CMD_REPLY_OK,
+                                client->common.set->login_greeting);
+       } else {
+               client_send_line(&client->common, CLIENT_CMD_REPLY_OK,
+                       t_strconcat(client->common.set->login_greeting, " ",
+                                   client->apop_challenge, NULL));
+       }
 }
 
 static void client_idle_disconnect_timeout(struct pop3_client *client)
@@ -423,7 +434,8 @@ void client_destroy(struct pop3_client *client, const char *reason)
 
 void client_destroy_internal_failure(struct pop3_client *client)
 {
-       client_send_line(client, "-ERR [IN-USE] Internal login failure. "
+       client_send_line(&client->common, CLIENT_CMD_REPLY_AUTH_FAIL_TEMP,
+                        "Internal login failure. "
                         "Refer to server log for more information.");
        client_destroy(client, "Internal login failure");
 }
@@ -459,26 +471,65 @@ bool client_unref(struct pop3_client *client)
        return FALSE;
 }
 
-void client_send_line(struct pop3_client *client, const char *line)
+static void
+client_send_raw_data(struct pop3_client *client, const void *data, size_t size)
 {
-       struct const_iovec iov[2];
        ssize_t ret;
 
-       iov[0].iov_base = line;
-       iov[0].iov_len = strlen(line);
-       iov[1].iov_base = "\r\n";
-       iov[1].iov_len = 2;
-
-       ret = o_stream_sendv(client->output, iov, 2);
-       if (ret < 0 || (size_t)ret != iov[0].iov_len + iov[1].iov_len) {
-               /* either disconnection or buffer full. in either case we
-                  want this connection destroyed. however destroying it here
-                  might break things if client is still tried to be accessed
-                  without being referenced.. */
+       ret = o_stream_send(client->output, data, size);
+       if (ret < 0 || (size_t)ret != size) {
+               /* either disconnection or buffer full. in either case we want
+                  this connection destroyed. however destroying it here might
+                  break things if client is still tried to be accessed without
+                  being referenced.. */
                i_stream_close(client->common.input);
        }
 }
 
+void client_send_raw(struct pop3_client *client, const char *data)
+{
+       client_send_raw_data(client, data, strlen(data));
+}
+
+void client_send_line(struct client *client, enum client_cmd_reply reply,
+                     const char *text)
+{
+       struct pop3_client *pop3_client = (struct pop3_client *)client;
+       const char *prefix = "-ERR";
+
+       switch (reply) {
+       case CLIENT_CMD_REPLY_OK:
+               prefix = "+OK";
+               break;
+       case CLIENT_CMD_REPLY_AUTH_FAIL_TEMP:
+               prefix = "-ERR [IN-USE]";
+               break;
+       case CLIENT_CMD_REPLY_AUTH_FAILED:
+       case CLIENT_CMD_REPLY_AUTHZ_FAILED:
+       case CLIENT_CMD_REPLY_AUTH_FAIL_REASON:
+       case CLIENT_CMD_REPLY_AUTH_FAIL_NOSSL:
+       case CLIENT_CMD_REPLY_BAD:
+       case CLIENT_CMD_REPLY_BYE:
+               break;
+       case CLIENT_CMD_REPLY_STATUS:
+               /* can't send status notifications */
+               return;
+       }
+
+       T_BEGIN {
+               string_t *line = t_str_new(256);
+
+               str_append(line, prefix);
+               str_append_c(line, ' ');
+               str_append(line, text);
+               str_append(line, "\r\n");
+
+               client_send_raw_data(pop3_client, str_data(line),
+                                    str_len(line));
+       } T_END;
+}
+
+
 void clients_notify_auth_connected(void)
 {
        struct client *client;
index c142427c54c2f742930f38b43ea6157d2bb8cd6e..00763e55ae17aee46d27f6cfdbb07bf1860dc035 100644 (file)
@@ -46,11 +46,11 @@ void client_destroy(struct pop3_client *client, const char *reason);
 void client_destroy_success(struct pop3_client *client, const char *reason);
 void client_destroy_internal_failure(struct pop3_client *client);
 
-void client_send_line(struct pop3_client *client, const char *line);
-
 bool client_read(struct pop3_client *client);
 void client_input(struct pop3_client *client);
 
+void client_send_raw(struct pop3_client *client, const char *data);
+
 void client_ref(struct pop3_client *client);
 bool client_unref(struct pop3_client *client);
 
index 7081ac7d943b1026eecf48bebf17c33bdd7e9c29..805b7b4ddf65717048b5c912a9906b14ba97ab0b 100644 (file)
@@ -11,8 +11,6 @@
 #include "client.h"
 #include "pop3-proxy.h"
 
-#define PROXY_FAILURE_MSG "-ERR [IN-USE] "AUTH_TEMP_FAILED_MSG
-
 static void proxy_free_password(struct pop3_client *client)
 {
        if (client->proxy_password == NULL)
@@ -24,8 +22,11 @@ static void proxy_free_password(struct pop3_client *client)
 
 static void proxy_failed(struct pop3_client *client, bool send_line)
 {
-       if (send_line)
-               client_send_line(client, PROXY_FAILURE_MSG);
+       if (send_line) {
+               client_send_line(&client->common,
+                                CLIENT_CMD_REPLY_AUTH_FAIL_TEMP,
+                                AUTH_TEMP_FAILED_MSG);
+       }
 
        login_proxy_free(&client->proxy);
        proxy_free_password(client);
@@ -184,9 +185,11 @@ static int proxy_input_line(struct pop3_client *client, const char *line)
           shouldn't be a real problem since of course everyone will
           be using only Dovecot as their backend :) */
        if (strncmp(line, "-ERR ", 5) != 0)
-               client_send_line(client, "-ERR "AUTH_FAILED_MSG);
-       else
-               client_send_line(client, line);
+               client_send_line(&client->common, CLIENT_CMD_REPLY_AUTH_FAILED,
+                                AUTH_FAILED_MSG);
+       else {
+               client_send_raw(client, t_strconcat(line, "\r\n", NULL));
+       }
 
        if (client->common.set->verbose_auth) {
                str = t_str_new(128);
@@ -267,7 +270,9 @@ int pop3_proxy_new(struct pop3_client *client, const char *host,
 
        if (password == NULL) {
                client_syslog_err(&client->common, "proxy: password not given");
-               client_send_line(client, PROXY_FAILURE_MSG);
+               client_send_line(&client->common,
+                                CLIENT_CMD_REPLY_AUTH_FAIL_TEMP,
+                                AUTH_TEMP_FAILED_MSG);
                return -1;
        }
 
@@ -280,14 +285,18 @@ int pop3_proxy_new(struct pop3_client *client, const char *host,
        }
        if (login_proxy_is_ourself(&client->common, host, port, user)) {
                client_syslog_err(&client->common, "Proxying loops to itself");
-               client_send_line(client, PROXY_FAILURE_MSG);
+               client_send_line(&client->common,
+                                CLIENT_CMD_REPLY_AUTH_FAIL_TEMP,
+                                AUTH_TEMP_FAILED_MSG);
                return -1;
        }
 
        client->proxy = login_proxy_new(&client->common, host, port, ssl_flags,
                                        proxy_input, client);
        if (client->proxy == NULL) {
-               client_send_line(client, PROXY_FAILURE_MSG);
+               client_send_line(&client->common,
+                                CLIENT_CMD_REPLY_AUTH_FAIL_TEMP,
+                                AUTH_TEMP_FAILED_MSG);
                return -1;
        }