client.c revision 5f5870385cff47efd2f58e7892f251cf13761528
02c335c23bf5fa225a467c19f2c063fb0dc7b8c3Timo Sirainen/* Copyright (c) 2002-2012 Dovecot authors, see the included COPYING file */
381daab1e3b56a0bc94d2191cf62beba0df51af9Pascal Volk
381daab1e3b56a0bc94d2191cf62beba0df51af9Pascal Volk#include "login-common.h"
381daab1e3b56a0bc94d2191cf62beba0df51af9Pascal Volk#include "base64.h"
bdd36cfdba3ff66d25570a9ff568d69e1eb543cfTimo Sirainen#include "buffer.h"
381daab1e3b56a0bc94d2191cf62beba0df51af9Pascal Volk#include "ioloop.h"
381daab1e3b56a0bc94d2191cf62beba0df51af9Pascal Volk#include "istream.h"
381daab1e3b56a0bc94d2191cf62beba0df51af9Pascal Volk#include "ostream.h"
633a1bf10559b9a8d85bfab662fa2a7ba97d45baAki Tuomi#include "randgen.h"
381daab1e3b56a0bc94d2191cf62beba0df51af9Pascal Volk#include "hostpid.h"
381daab1e3b56a0bc94d2191cf62beba0df51af9Pascal Volk#include "safe-memset.h"
381daab1e3b56a0bc94d2191cf62beba0df51af9Pascal Volk#include "str.h"
381daab1e3b56a0bc94d2191cf62beba0df51af9Pascal Volk#include "strescape.h"
381daab1e3b56a0bc94d2191cf62beba0df51af9Pascal Volk#include "master-service.h"
381daab1e3b56a0bc94d2191cf62beba0df51af9Pascal Volk#include "client.h"
381daab1e3b56a0bc94d2191cf62beba0df51af9Pascal Volk#include "client-authenticate.h"
381daab1e3b56a0bc94d2191cf62beba0df51af9Pascal Volk#include "auth-client.h"
381daab1e3b56a0bc94d2191cf62beba0df51af9Pascal Volk#include "ssl-proxy.h"
381daab1e3b56a0bc94d2191cf62beba0df51af9Pascal Volk#include "pop3-proxy.h"
381daab1e3b56a0bc94d2191cf62beba0df51af9Pascal Volk#include "pop3-login-settings.h"
381daab1e3b56a0bc94d2191cf62beba0df51af9Pascal Volk
381daab1e3b56a0bc94d2191cf62beba0df51af9Pascal Volk/* Disconnect client when it sends too many bad commands */
381daab1e3b56a0bc94d2191cf62beba0df51af9Pascal Volk#define CLIENT_MAX_BAD_COMMANDS 10
4ee00532a265bdfb38539d811fcd12d51210ac35Timo Sirainen
381daab1e3b56a0bc94d2191cf62beba0df51af9Pascal Volkstatic bool cmd_stls(struct pop3_client *client)
381daab1e3b56a0bc94d2191cf62beba0df51af9Pascal Volk{
381daab1e3b56a0bc94d2191cf62beba0df51af9Pascal Volk client_cmd_starttls(&client->common);
381daab1e3b56a0bc94d2191cf62beba0df51af9Pascal Volk return TRUE;
381daab1e3b56a0bc94d2191cf62beba0df51af9Pascal Volk}
a75d470c9223a75801418fcdda258885c36317e0Timo Sirainen
381daab1e3b56a0bc94d2191cf62beba0df51af9Pascal Volkstatic bool cmd_quit(struct pop3_client *client)
4ee00532a265bdfb38539d811fcd12d51210ac35Timo Sirainen{
381daab1e3b56a0bc94d2191cf62beba0df51af9Pascal Volk client_send_line(&client->common, CLIENT_CMD_REPLY_OK, "Logging out");
381daab1e3b56a0bc94d2191cf62beba0df51af9Pascal Volk client_destroy(&client->common, "Aborted login");
381daab1e3b56a0bc94d2191cf62beba0df51af9Pascal Volk return TRUE;
381daab1e3b56a0bc94d2191cf62beba0df51af9Pascal Volk}
381daab1e3b56a0bc94d2191cf62beba0df51af9Pascal Volk
381daab1e3b56a0bc94d2191cf62beba0df51af9Pascal Volkstatic bool client_command_execute(struct pop3_client *client, const char *cmd,
381daab1e3b56a0bc94d2191cf62beba0df51af9Pascal Volk const char *args)
381daab1e3b56a0bc94d2191cf62beba0df51af9Pascal Volk{
381daab1e3b56a0bc94d2191cf62beba0df51af9Pascal Volk cmd = t_str_ucase(cmd);
381daab1e3b56a0bc94d2191cf62beba0df51af9Pascal Volk if (strcmp(cmd, "CAPA") == 0)
381daab1e3b56a0bc94d2191cf62beba0df51af9Pascal Volk return cmd_capa(client, args);
381daab1e3b56a0bc94d2191cf62beba0df51af9Pascal Volk if (strcmp(cmd, "USER") == 0)
a75d470c9223a75801418fcdda258885c36317e0Timo Sirainen return cmd_user(client, args);
381daab1e3b56a0bc94d2191cf62beba0df51af9Pascal Volk if (strcmp(cmd, "PASS") == 0)
381daab1e3b56a0bc94d2191cf62beba0df51af9Pascal Volk return cmd_pass(client, args);
381daab1e3b56a0bc94d2191cf62beba0df51af9Pascal Volk if (strcmp(cmd, "AUTH") == 0)
381daab1e3b56a0bc94d2191cf62beba0df51af9Pascal Volk return cmd_auth(client, args);
a75d470c9223a75801418fcdda258885c36317e0Timo Sirainen if (strcmp(cmd, "APOP") == 0)
381daab1e3b56a0bc94d2191cf62beba0df51af9Pascal Volk return cmd_apop(client, args);
381daab1e3b56a0bc94d2191cf62beba0df51af9Pascal Volk if (strcmp(cmd, "STLS") == 0)
381daab1e3b56a0bc94d2191cf62beba0df51af9Pascal Volk return cmd_stls(client);
381daab1e3b56a0bc94d2191cf62beba0df51af9Pascal Volk if (strcmp(cmd, "QUIT") == 0)
381daab1e3b56a0bc94d2191cf62beba0df51af9Pascal Volk return cmd_quit(client);
381daab1e3b56a0bc94d2191cf62beba0df51af9Pascal Volk
381daab1e3b56a0bc94d2191cf62beba0df51af9Pascal Volk client_send_line(&client->common, CLIENT_CMD_REPLY_BAD,
381daab1e3b56a0bc94d2191cf62beba0df51af9Pascal Volk "Unknown command.");
381daab1e3b56a0bc94d2191cf62beba0df51af9Pascal Volk return FALSE;
381daab1e3b56a0bc94d2191cf62beba0df51af9Pascal Volk}
381daab1e3b56a0bc94d2191cf62beba0df51af9Pascal Volk
381daab1e3b56a0bc94d2191cf62beba0df51af9Pascal Volkstatic void pop3_client_input(struct client *client)
381daab1e3b56a0bc94d2191cf62beba0df51af9Pascal Volk{
381daab1e3b56a0bc94d2191cf62beba0df51af9Pascal Volk struct pop3_client *pop3_client = (struct pop3_client *)client;
381daab1e3b56a0bc94d2191cf62beba0df51af9Pascal Volk char *line, *args;
381daab1e3b56a0bc94d2191cf62beba0df51af9Pascal Volk
381daab1e3b56a0bc94d2191cf62beba0df51af9Pascal Volk i_assert(!client->authenticating);
381daab1e3b56a0bc94d2191cf62beba0df51af9Pascal Volk
381daab1e3b56a0bc94d2191cf62beba0df51af9Pascal Volk if (!client_read(client))
381daab1e3b56a0bc94d2191cf62beba0df51af9Pascal Volk return;
381daab1e3b56a0bc94d2191cf62beba0df51af9Pascal Volk
381daab1e3b56a0bc94d2191cf62beba0df51af9Pascal Volk client_ref(client);
381daab1e3b56a0bc94d2191cf62beba0df51af9Pascal Volk
381daab1e3b56a0bc94d2191cf62beba0df51af9Pascal Volk o_stream_cork(client->output);
381daab1e3b56a0bc94d2191cf62beba0df51af9Pascal Volk /* if a command starts an authentication, stop processing further
381daab1e3b56a0bc94d2191cf62beba0df51af9Pascal Volk commands until the authentication is finished. */
381daab1e3b56a0bc94d2191cf62beba0df51af9Pascal Volk while (!client->output->closed && !client->authenticating &&
381daab1e3b56a0bc94d2191cf62beba0df51af9Pascal Volk auth_client_is_connected(auth_client) &&
381daab1e3b56a0bc94d2191cf62beba0df51af9Pascal Volk (line = i_stream_next_line(client->input)) != NULL) {
381daab1e3b56a0bc94d2191cf62beba0df51af9Pascal Volk args = strchr(line, ' ');
381daab1e3b56a0bc94d2191cf62beba0df51af9Pascal Volk if (args != NULL)
381daab1e3b56a0bc94d2191cf62beba0df51af9Pascal Volk *args++ = '\0';
381daab1e3b56a0bc94d2191cf62beba0df51af9Pascal Volk
381daab1e3b56a0bc94d2191cf62beba0df51af9Pascal Volk if (client_command_execute(pop3_client, line,
381daab1e3b56a0bc94d2191cf62beba0df51af9Pascal Volk args != NULL ? args : ""))
381daab1e3b56a0bc94d2191cf62beba0df51af9Pascal Volk client->bad_counter = 0;
381daab1e3b56a0bc94d2191cf62beba0df51af9Pascal Volk else if (++client->bad_counter > CLIENT_MAX_BAD_COMMANDS) {
381daab1e3b56a0bc94d2191cf62beba0df51af9Pascal Volk client_send_line(client, CLIENT_CMD_REPLY_BYE,
381daab1e3b56a0bc94d2191cf62beba0df51af9Pascal Volk "Too many invalid bad commands.");
381daab1e3b56a0bc94d2191cf62beba0df51af9Pascal Volk client_destroy(client,
381daab1e3b56a0bc94d2191cf62beba0df51af9Pascal Volk "Disconnected: Too many bad commands");
381daab1e3b56a0bc94d2191cf62beba0df51af9Pascal Volk }
381daab1e3b56a0bc94d2191cf62beba0df51af9Pascal Volk }
381daab1e3b56a0bc94d2191cf62beba0df51af9Pascal Volk
381daab1e3b56a0bc94d2191cf62beba0df51af9Pascal Volk if (auth_client != NULL && !auth_client_is_connected(auth_client))
381daab1e3b56a0bc94d2191cf62beba0df51af9Pascal Volk client->input_blocked = TRUE;
381daab1e3b56a0bc94d2191cf62beba0df51af9Pascal Volk
381daab1e3b56a0bc94d2191cf62beba0df51af9Pascal Volk if (client_unref(&client))
381daab1e3b56a0bc94d2191cf62beba0df51af9Pascal Volk o_stream_uncork(client->output);
381daab1e3b56a0bc94d2191cf62beba0df51af9Pascal Volk}
381daab1e3b56a0bc94d2191cf62beba0df51af9Pascal Volk
381daab1e3b56a0bc94d2191cf62beba0df51af9Pascal Volkstatic struct client *pop3_client_alloc(pool_t pool)
381daab1e3b56a0bc94d2191cf62beba0df51af9Pascal Volk{
381daab1e3b56a0bc94d2191cf62beba0df51af9Pascal Volk struct pop3_client *pop3_client;
381daab1e3b56a0bc94d2191cf62beba0df51af9Pascal Volk
5fbccc935e3f7b916aa7c6e302a212821072e83aTimo Sirainen pop3_client = p_new(pool, struct pop3_client, 1);
381daab1e3b56a0bc94d2191cf62beba0df51af9Pascal Volk return &pop3_client->common;
381daab1e3b56a0bc94d2191cf62beba0df51af9Pascal Volk}
381daab1e3b56a0bc94d2191cf62beba0df51af9Pascal Volk
381daab1e3b56a0bc94d2191cf62beba0df51af9Pascal Volkstatic void pop3_client_create(struct client *client ATTR_UNUSED,
381daab1e3b56a0bc94d2191cf62beba0df51af9Pascal Volk void **other_sets ATTR_UNUSED)
381daab1e3b56a0bc94d2191cf62beba0df51af9Pascal Volk{
381daab1e3b56a0bc94d2191cf62beba0df51af9Pascal Volk}
381daab1e3b56a0bc94d2191cf62beba0df51af9Pascal Volk
381daab1e3b56a0bc94d2191cf62beba0df51af9Pascal Volkstatic void pop3_client_destroy(struct client *client)
381daab1e3b56a0bc94d2191cf62beba0df51af9Pascal Volk{
381daab1e3b56a0bc94d2191cf62beba0df51af9Pascal Volk struct pop3_client *pop3_client = (struct pop3_client *)client;
633a1bf10559b9a8d85bfab662fa2a7ba97d45baAki Tuomi
381daab1e3b56a0bc94d2191cf62beba0df51af9Pascal Volk i_free_and_null(pop3_client->last_user);
381daab1e3b56a0bc94d2191cf62beba0df51af9Pascal Volk i_free_and_null(pop3_client->apop_challenge);
633a1bf10559b9a8d85bfab662fa2a7ba97d45baAki Tuomi}
381daab1e3b56a0bc94d2191cf62beba0df51af9Pascal Volk
381daab1e3b56a0bc94d2191cf62beba0df51af9Pascal Volkstatic char *get_apop_challenge(struct pop3_client *client)
381daab1e3b56a0bc94d2191cf62beba0df51af9Pascal Volk{
381daab1e3b56a0bc94d2191cf62beba0df51af9Pascal Volk unsigned char buffer[16];
381daab1e3b56a0bc94d2191cf62beba0df51af9Pascal Volk unsigned char buffer_base64[MAX_BASE64_ENCODED_SIZE(sizeof(buffer)) + 1];
381daab1e3b56a0bc94d2191cf62beba0df51af9Pascal Volk buffer_t buf;
381daab1e3b56a0bc94d2191cf62beba0df51af9Pascal Volk
381daab1e3b56a0bc94d2191cf62beba0df51af9Pascal Volk if (auth_client_find_mech(auth_client, "APOP") == NULL) {
381daab1e3b56a0bc94d2191cf62beba0df51af9Pascal Volk /* disabled, no need to present the challenge */
381daab1e3b56a0bc94d2191cf62beba0df51af9Pascal Volk return NULL;
381daab1e3b56a0bc94d2191cf62beba0df51af9Pascal Volk }
a75d470c9223a75801418fcdda258885c36317e0Timo Sirainen
a75d470c9223a75801418fcdda258885c36317e0Timo Sirainen auth_client_get_connect_id(auth_client, &client->apop_server_pid,
381daab1e3b56a0bc94d2191cf62beba0df51af9Pascal Volk &client->apop_connect_uid);
381daab1e3b56a0bc94d2191cf62beba0df51af9Pascal Volk
381daab1e3b56a0bc94d2191cf62beba0df51af9Pascal Volk random_fill(buffer, sizeof(buffer));
381daab1e3b56a0bc94d2191cf62beba0df51af9Pascal Volk buffer_create_data(&buf, buffer_base64, sizeof(buffer_base64));
381daab1e3b56a0bc94d2191cf62beba0df51af9Pascal Volk base64_encode(buffer, sizeof(buffer), &buf);
a75d470c9223a75801418fcdda258885c36317e0Timo Sirainen buffer_append_c(&buf, '\0');
381daab1e3b56a0bc94d2191cf62beba0df51af9Pascal Volk
381daab1e3b56a0bc94d2191cf62beba0df51af9Pascal Volk return i_strdup_printf("<%x.%x.%lx.%s@%s>",
381daab1e3b56a0bc94d2191cf62beba0df51af9Pascal Volk client->apop_server_pid,
381daab1e3b56a0bc94d2191cf62beba0df51af9Pascal Volk client->apop_connect_uid,
381daab1e3b56a0bc94d2191cf62beba0df51af9Pascal Volk (unsigned long)ioloop_time,
381daab1e3b56a0bc94d2191cf62beba0df51af9Pascal Volk (const char *)buf.data, my_hostname);
381daab1e3b56a0bc94d2191cf62beba0df51af9Pascal Volk}
381daab1e3b56a0bc94d2191cf62beba0df51af9Pascal Volk
381daab1e3b56a0bc94d2191cf62beba0df51af9Pascal Volkstatic void pop3_client_send_greeting(struct client *client)
381daab1e3b56a0bc94d2191cf62beba0df51af9Pascal Volk{
381daab1e3b56a0bc94d2191cf62beba0df51af9Pascal Volk struct pop3_client *pop3_client = (struct pop3_client *)client;
a75d470c9223a75801418fcdda258885c36317e0Timo Sirainen
381daab1e3b56a0bc94d2191cf62beba0df51af9Pascal Volk client->io = io_add(client->fd, IO_READ, client_input, client);
381daab1e3b56a0bc94d2191cf62beba0df51af9Pascal Volk
381daab1e3b56a0bc94d2191cf62beba0df51af9Pascal Volk pop3_client->apop_challenge = get_apop_challenge(pop3_client);
381daab1e3b56a0bc94d2191cf62beba0df51af9Pascal Volk if (pop3_client->apop_challenge == NULL) {
381daab1e3b56a0bc94d2191cf62beba0df51af9Pascal Volk client_send_line(client, CLIENT_CMD_REPLY_OK,
381daab1e3b56a0bc94d2191cf62beba0df51af9Pascal Volk client->set->login_greeting);
381daab1e3b56a0bc94d2191cf62beba0df51af9Pascal Volk } else {
381daab1e3b56a0bc94d2191cf62beba0df51af9Pascal Volk client_send_line(client, CLIENT_CMD_REPLY_OK,
381daab1e3b56a0bc94d2191cf62beba0df51af9Pascal Volk t_strconcat(client->set->login_greeting, " ",
381daab1e3b56a0bc94d2191cf62beba0df51af9Pascal Volk pop3_client->apop_challenge, NULL));
381daab1e3b56a0bc94d2191cf62beba0df51af9Pascal Volk }
381daab1e3b56a0bc94d2191cf62beba0df51af9Pascal Volk client->greeting_sent = TRUE;
381daab1e3b56a0bc94d2191cf62beba0df51af9Pascal Volk}
381daab1e3b56a0bc94d2191cf62beba0df51af9Pascal Volk
381daab1e3b56a0bc94d2191cf62beba0df51af9Pascal Volkstatic void pop3_client_starttls(struct client *client ATTR_UNUSED)
381daab1e3b56a0bc94d2191cf62beba0df51af9Pascal Volk{
381daab1e3b56a0bc94d2191cf62beba0df51af9Pascal Volk}
381daab1e3b56a0bc94d2191cf62beba0df51af9Pascal Volk
381daab1e3b56a0bc94d2191cf62beba0df51af9Pascal Volkstatic void
381daab1e3b56a0bc94d2191cf62beba0df51af9Pascal Volkpop3_client_send_line(struct client *client, enum client_cmd_reply reply,
381daab1e3b56a0bc94d2191cf62beba0df51af9Pascal Volk const char *text)
381daab1e3b56a0bc94d2191cf62beba0df51af9Pascal Volk{
381daab1e3b56a0bc94d2191cf62beba0df51af9Pascal Volk const char *prefix = "-ERR";
381daab1e3b56a0bc94d2191cf62beba0df51af9Pascal Volk
1c7b846a17612b1fe7dcee797d0d8115201e58f4Timo Sirainen switch (reply) {
381daab1e3b56a0bc94d2191cf62beba0df51af9Pascal Volk case CLIENT_CMD_REPLY_OK:
381daab1e3b56a0bc94d2191cf62beba0df51af9Pascal Volk prefix = "+OK";
678d0463849ba777106eb7875f27db07a5d8e3dfTimo Sirainen break;
381daab1e3b56a0bc94d2191cf62beba0df51af9Pascal Volk case CLIENT_CMD_REPLY_AUTH_FAIL_TEMP:
381daab1e3b56a0bc94d2191cf62beba0df51af9Pascal Volk prefix = "-ERR [IN-USE]";
381daab1e3b56a0bc94d2191cf62beba0df51af9Pascal Volk break;
381daab1e3b56a0bc94d2191cf62beba0df51af9Pascal Volk case CLIENT_CMD_REPLY_AUTH_FAILED:
381daab1e3b56a0bc94d2191cf62beba0df51af9Pascal Volk case CLIENT_CMD_REPLY_AUTHZ_FAILED:
381daab1e3b56a0bc94d2191cf62beba0df51af9Pascal Volk case CLIENT_CMD_REPLY_AUTH_FAIL_REASON:
381daab1e3b56a0bc94d2191cf62beba0df51af9Pascal Volk case CLIENT_CMD_REPLY_AUTH_FAIL_NOSSL:
381daab1e3b56a0bc94d2191cf62beba0df51af9Pascal Volk case CLIENT_CMD_REPLY_BAD:
381daab1e3b56a0bc94d2191cf62beba0df51af9Pascal Volk case CLIENT_CMD_REPLY_BYE:
381daab1e3b56a0bc94d2191cf62beba0df51af9Pascal Volk break;
381daab1e3b56a0bc94d2191cf62beba0df51af9Pascal Volk case CLIENT_CMD_REPLY_STATUS:
381daab1e3b56a0bc94d2191cf62beba0df51af9Pascal Volk case CLIENT_CMD_REPLY_STATUS_BAD:
381daab1e3b56a0bc94d2191cf62beba0df51af9Pascal Volk /* can't send status notifications */
381daab1e3b56a0bc94d2191cf62beba0df51af9Pascal Volk return;
381daab1e3b56a0bc94d2191cf62beba0df51af9Pascal Volk }
5fbccc935e3f7b916aa7c6e302a212821072e83aTimo Sirainen
5fbccc935e3f7b916aa7c6e302a212821072e83aTimo Sirainen T_BEGIN {
5fbccc935e3f7b916aa7c6e302a212821072e83aTimo Sirainen string_t *line = t_str_new(256);
5fbccc935e3f7b916aa7c6e302a212821072e83aTimo Sirainen
a7fc192e2177fd869bca779e9f6014f7149cda2dAki Tuomi str_append(line, prefix);
a7fc192e2177fd869bca779e9f6014f7149cda2dAki Tuomi str_append_c(line, ' ');
381daab1e3b56a0bc94d2191cf62beba0df51af9Pascal Volk str_append(line, text);
633a1bf10559b9a8d85bfab662fa2a7ba97d45baAki Tuomi str_append(line, "\r\n");
633a1bf10559b9a8d85bfab662fa2a7ba97d45baAki Tuomi
633a1bf10559b9a8d85bfab662fa2a7ba97d45baAki Tuomi client_send_raw_data(client, str_data(line),
633a1bf10559b9a8d85bfab662fa2a7ba97d45baAki Tuomi str_len(line));
381daab1e3b56a0bc94d2191cf62beba0df51af9Pascal Volk } T_END;
381daab1e3b56a0bc94d2191cf62beba0df51af9Pascal Volk}
381daab1e3b56a0bc94d2191cf62beba0df51af9Pascal Volk
381daab1e3b56a0bc94d2191cf62beba0df51af9Pascal Volkstatic void pop3_login_die(void)
381daab1e3b56a0bc94d2191cf62beba0df51af9Pascal Volk{
381daab1e3b56a0bc94d2191cf62beba0df51af9Pascal Volk /* do nothing. pop3 connections typically die pretty quick anyway. */
381daab1e3b56a0bc94d2191cf62beba0df51af9Pascal Volk}
381daab1e3b56a0bc94d2191cf62beba0df51af9Pascal Volk
381daab1e3b56a0bc94d2191cf62beba0df51af9Pascal Volkstatic void pop3_login_preinit(void)
bde78a7bf5f9000f1ae4dc7ce6cabd012e1f8b79Pascal Volk{
381daab1e3b56a0bc94d2191cf62beba0df51af9Pascal Volk login_set_roots = pop3_login_setting_roots;
}
static void pop3_login_init(void)
{
/* override the default login_die() */
master_service_set_die_callback(master_service, pop3_login_die);
}
static void pop3_login_deinit(void)
{
clients_destroy_all();
}
static struct client_vfuncs pop3_client_vfuncs = {
pop3_client_alloc,
pop3_client_create,
pop3_client_destroy,
pop3_client_send_greeting,
pop3_client_starttls,
pop3_client_input,
pop3_client_send_line,
pop3_client_auth_handle_reply,
NULL,
NULL,
pop3_proxy_reset,
pop3_proxy_parse_line
};
static const struct login_binary pop3_login_binary = {
.protocol = "pop3",
.process_name = "pop3-login",
.default_port = 110,
.default_ssl_port = 995,
.client_vfuncs = &pop3_client_vfuncs,
.preinit = pop3_login_preinit,
.init = pop3_login_init,
.deinit = pop3_login_deinit,
.sasl_support_final_reply = FALSE
};
int main(int argc, char *argv[])
{
return login_binary_run(&pop3_login_binary, argc, argv);
}