[Dovecot] PATCH: make local IP address available to auth modules
The attached patch makes the local IP address to which the client connected available to the authentication modules; i.e., the local IP address is available for substitution as %i for the mysql and pgsql modules. We needed this feature to support thousands of our legacy accounts which are authenticated by username/local_part (not the full email address) and IP address (one per domain). Timo, would this be acceptable to commit? One further improvement would be to make the remote IP address available as well, in case one wanted to implement a tcpwrapper-like functionality. Matt diff -u -r work.patched/dovecot-0.99.10/src/auth/auth-login-interface.h work/dovecot-0.99.10/src/auth/auth-login-interface.h --- work.patched/dovecot-0.99.10/src/auth/auth-login-interface.h Sun May 18 07:26:28 2003 +++ work/dovecot-0.99.10/src/auth/auth-login-interface.h Fri Oct 24 13:57:18 2003 @@ -1,6 +1,8 @@ #ifndef __AUTH_LOGIN_INTERFACE_H #define __AUTH_LOGIN_INTERFACE_H +#include "network.h" + /* max. size for auth_login_request_continue.data[] */ #define AUTH_LOGIN_MAX_REQUEST_DATA_SIZE 4096 @@ -47,6 +49,7 @@ struct auth_login_request_new { enum auth_login_request_type type; /* AUTH_LOGIN_REQUEST_NEW */ unsigned int id; /* unique ID for the request */ + struct ip_addr ip_local; enum auth_mech mech; enum auth_protocol protocol; @@ -56,6 +59,7 @@ struct auth_login_request_continue { enum auth_login_request_type type; /* AUTH_LOGIN_REQUEST_CONTINUE */ unsigned int id; + struct ip_addr ip_local; size_t data_size; /* unsigned char data[]; */ diff -u -r work.patched/dovecot-0.99.10/src/auth/master-connection.c work/dovecot-0.99.10/src/auth/master-connection.c --- work.patched/dovecot-0.99.10/src/auth/master-connection.c Fri Aug 22 17:56:18 2003 +++ work/dovecot-0.99.10/src/auth/master-connection.c Fri Oct 24 13:57:35 2003 @@ -133,8 +133,9 @@ } send_reply(&failure_reply, sizeof(failure_reply), request->tag); } else { - userdb->lookup(auth_request->user, userdb_callback, - POINTER_CAST(request->tag)); + userdb->lookup(auth_request->user, + net_ip2addr(&auth_request->ip_local), + userdb_callback, POINTER_CAST(request->tag)); mech_request_free(login_conn, auth_request, request->id); } } diff -u -r work.patched/dovecot-0.99.10/src/auth/mech.c work/dovecot-0.99.10/src/auth/mech.c --- work.patched/dovecot-0.99.10/src/auth/mech.c Sun May 18 07:26:28 2003 +++ work/dovecot-0.99.10/src/auth/mech.c Fri Oct 24 13:57:51 2003 @@ -97,6 +97,7 @@ auth_request->created = ioloop_time; auth_request->conn = conn; auth_request->id = request->id; + auth_request->ip_local = request->ip_local; auth_request->protocol = request->protocol; hash_insert(conn->auth_requests, POINTER_CAST(request->id), diff -u -r work.patched/dovecot-0.99.10/src/auth/mech.h work/dovecot-0.99.10/src/auth/mech.h --- work.patched/dovecot-0.99.10/src/auth/mech.h Sun May 18 07:26:28 2003 +++ work/dovecot-0.99.10/src/auth/mech.h Fri Oct 24 13:58:02 2003 @@ -14,6 +14,7 @@ struct login_connection *conn; unsigned int id; + struct ip_addr ip_local; time_t created; enum auth_protocol protocol; diff -u -r work.patched/dovecot-0.99.10/src/auth/passdb-ldap.c work/dovecot-0.99.10/src/auth/passdb-ldap.c --- work.patched/dovecot-0.99.10/src/auth/passdb-ldap.c Thu Mar 20 09:55:57 2003 +++ work/dovecot-0.99.10/src/auth/passdb-ldap.c Fri Oct 24 13:58:20 2003 @@ -145,7 +145,8 @@ passdb_ldap_conn->attr_names[ATTR_VIRTUAL_USER], user); } else { str = t_str_new(512); - var_expand(str, conn->set.pass_filter, user, NULL); + var_expand(str, conn->set.pass_filter, user, NULL, + net_ip2addr(&auth_request->ip_local)); filter = str_c(str); } diff -u -r work.patched/dovecot-0.99.10/src/auth/passdb-mysql.c work/dovecot-0.99.10/src/auth/passdb-mysql.c --- work.patched/dovecot-0.99.10/src/auth/passdb-mysql.c Fri Aug 29 17:47:24 2003 +++ work/dovecot-0.99.10/src/auth/passdb-mysql.c Fri Oct 24 13:58:31 2003 @@ -109,7 +109,8 @@ str = t_str_new(512); var_expand(str, conn->set.password_query, - str_escape(auth_request->user), NULL); + str_escape(auth_request->user), NULL, + net_ip2addr(&auth_request->ip_local)); query = str_c(str); mysql_request->callback = mysql_handle_request; diff -u -r work.patched/dovecot-0.99.10/src/auth/passdb-pgsql.c work/dovecot-0.99.10/src/auth/passdb-pgsql.c --- work.patched/dovecot-0.99.10/src/auth/passdb-pgsql.c Fri Apr 4 06:17:25 2003 +++ work/dovecot-0.99.10/src/auth/passdb-pgsql.c Fri Oct 24 14:01:08 2003 @@ -105,7 +105,8 @@ str = t_str_new(512); var_expand(str, conn->set.password_query, - str_escape(auth_request->user), NULL); + str_escape(auth_request->user), NULL, + net_ip2addr(&auth_request->ip_local)); query = str_c(str); pgsql_request->callback = pgsql_handle_request; diff -u -r work.patched/dovecot-0.99.10/src/auth/userdb-ldap.c work/dovecot-0.99.10/src/auth/userdb-ldap.c --- work.patched/dovecot-0.99.10/src/auth/userdb-ldap.c Sun May 18 07:26:28 2003 +++ work/dovecot-0.99.10/src/auth/userdb-ldap.c Fri Oct 24 15:46:21 2003 @@ -142,8 +142,8 @@ t_pop(); } -static void userdb_ldap_lookup(const char *user, userdb_callback_t *callback, - void *context) +static void userdb_ldap_lookup(const char *user, const char *ip_local, + userdb_callback_t *callback, void *context) { struct ldap_connection *conn = userdb_ldap_conn->conn; struct userdb_ldap_request *request; @@ -156,7 +156,7 @@ userdb_ldap_conn->attr_names[ATTR_VIRTUAL_USER], user); } else { str = t_str_new(512); - var_expand(str, conn->set.user_filter, user, NULL); + var_expand(str, conn->set.user_filter, user, NULL, ip_local); filter = str_c(str); } diff -u -r work.patched/dovecot-0.99.10/src/auth/userdb-mysql.c work/dovecot-0.99.10/src/auth/userdb-mysql.c --- work.patched/dovecot-0.99.10/src/auth/userdb-mysql.c Fri Oct 24 15:07:12 2003 +++ work/dovecot-0.99.10/src/auth/userdb-mysql.c Fri Oct 24 14:58:08 2003 @@ -115,8 +115,8 @@ } } -static void userdb_mysql_lookup(const char *user, userdb_callback_t *callback, - void *context) +static void userdb_mysql_lookup(const char *user, const char *ip_local, + userdb_callback_t *callback, void *context) { struct mysql_connection *conn = userdb_mysql_conn->conn; struct userdb_mysql_request *request; @@ -124,7 +124,8 @@ string_t *str; str = t_str_new(512); - var_expand(str, conn->set.user_query, str_escape(user), NULL); + var_expand(str, conn->set.user_query, str_escape(user), NULL, + ip_local); query = str_c(str); request = i_malloc(sizeof(struct userdb_mysql_request) + strlen(user)); diff -u -r work.patched/dovecot-0.99.10/src/auth/userdb-passwd-file.c work/dovecot-0.99.10/src/auth/userdb-passwd-file.c --- work.patched/dovecot-0.99.10/src/auth/userdb-passwd-file.c Sun May 18 07:26:28 2003 +++ work/dovecot-0.99.10/src/auth/userdb-passwd-file.c Fri Oct 24 15:28:09 2003 @@ -11,8 +11,8 @@ struct passwd_file *userdb_pwf = NULL; -static void passwd_file_lookup(const char *user, userdb_callback_t *callback, - void *context) +static void passwd_file_lookup(const char *user, const char *ip_local, + userdb_callback_t *callback, void *context) { struct user_data data; struct passwd_user *pu; diff -u -r work.patched/dovecot-0.99.10/src/auth/userdb-passwd.c work/dovecot-0.99.10/src/auth/userdb-passwd.c --- work.patched/dovecot-0.99.10/src/auth/userdb-passwd.c Sun May 18 07:26:28 2003 +++ work/dovecot-0.99.10/src/auth/userdb-passwd.c Fri Oct 24 15:28:26 2003 @@ -10,8 +10,8 @@ #include <pwd.h> -static void passwd_lookup(const char *user, userdb_callback_t *callback, - void *context) +static void passwd_lookup(const char *user, const char *ip_local, + userdb_callback_t *callback, void *context) { struct user_data data; struct passwd *pw; diff -u -r work.patched/dovecot-0.99.10/src/auth/userdb-pgsql.c work/dovecot-0.99.10/src/auth/userdb-pgsql.c --- work.patched/dovecot-0.99.10/src/auth/userdb-pgsql.c Sun May 18 07:26:28 2003 +++ work/dovecot-0.99.10/src/auth/userdb-pgsql.c Fri Oct 24 15:45:51 2003 @@ -84,8 +84,8 @@ } } -static void userdb_pgsql_lookup(const char *user, userdb_callback_t *callback, - void *context) +static void userdb_pgsql_lookup(const char *user, const char *ip_local, + userdb_callback_t *callback, void *context) { struct pgsql_connection *conn = userdb_pgsql_conn->conn; struct userdb_pgsql_request *request; @@ -93,7 +93,7 @@ string_t *str; str = t_str_new(512); - var_expand(str, conn->set.user_query, str_escape(user), NULL); + var_expand(str, conn->set.user_query, str_escape(user), NULL, ip_local); query = str_c(str); request = i_malloc(sizeof(struct userdb_pgsql_request) + strlen(user)); diff -u -r work.patched/dovecot-0.99.10/src/auth/userdb-static.c work/dovecot-0.99.10/src/auth/userdb-static.c --- work.patched/dovecot-0.99.10/src/auth/userdb-static.c Wed Feb 19 18:46:51 2003 +++ work/dovecot-0.99.10/src/auth/userdb-static.c Fri Oct 24 15:30:40 2003 @@ -16,8 +16,8 @@ static gid_t static_gid; static char *static_home_template; -static void static_lookup(const char *user, userdb_callback_t *callback, - void *context) +static void static_lookup(const char *user, const char *ip_local, + userdb_callback_t *callback, void *context) { struct user_data data; string_t *str; @@ -29,7 +29,7 @@ data.virtual_user = data.system_user = user; str = t_str_new(256); - var_expand(str, static_home_template, user, NULL); + var_expand(str, static_home_template, user, NULL, ip_local); data.home = str_c(str); callback(&data, context); diff -u -r work.patched/dovecot-0.99.10/src/auth/userdb-vpopmail.c work/dovecot-0.99.10/src/auth/userdb-vpopmail.c --- work.patched/dovecot-0.99.10/src/auth/userdb-vpopmail.c Thu Mar 20 09:46:33 2003 +++ work/dovecot-0.99.10/src/auth/userdb-vpopmail.c Fri Oct 24 15:29:24 2003 @@ -46,8 +46,8 @@ #ifdef USERDB_VPOPMAIL -static void vpopmail_lookup(const char *user, userdb_callback_t *callback, - void *context) +static void vpopmail_lookup(const char *user, const char *ip_local, + userdb_callback_t *callback, void *context) { char vpop_user[VPOPMAIL_LIMIT], vpop_domain[VPOPMAIL_LIMIT]; struct vqpasswd *vpw; diff -u -r work.patched/dovecot-0.99.10/src/auth/userdb.h work/dovecot-0.99.10/src/auth/userdb.h --- work.patched/dovecot-0.99.10/src/auth/userdb.h Fri Aug 22 13:07:53 2003 +++ work/dovecot-0.99.10/src/auth/userdb.h Fri Oct 24 15:47:11 2003 @@ -17,8 +17,8 @@ void (*init)(const char *args); void (*deinit)(void); - void (*lookup)(const char *user, userdb_callback_t *callback, - void *context); + void (*lookup)(const char *user, const char *ip_local, + userdb_callback_t *callback, void *context); }; extern struct userdb_module *userdb; diff -u -r work.patched/dovecot-0.99.10/src/imap-login/client.c work/dovecot-0.99.10/src/imap-login/client.c --- work.patched/dovecot-0.99.10/src/imap-login/client.c Wed Jun 25 20:02:04 2003 +++ work/dovecot-0.99.10/src/imap-login/client.c Fri Oct 24 14:21:46 2003 @@ -49,7 +49,7 @@ if (!verbose_proctitle || !process_per_connection) return; - addr = net_ip2addr(&client->common.ip); + addr = net_ip2addr(&client->common.ip_remote); if (addr == NULL) addr = "??"; @@ -126,7 +126,7 @@ client->common.io = NULL; } - fd_ssl = ssl_proxy_new(client->common.fd, &client->common.ip); + fd_ssl = ssl_proxy_new(client->common.fd, &client->common.ip_remote); if (fd_ssl != -1) { client->tls = TRUE; client_set_title(client); @@ -338,7 +338,8 @@ } } -struct client *client_create(int fd, struct ip_addr *ip, int ssl) +struct client *client_create(int fd, struct ip_addr *ip_remote, + struct ip_addr *ip_local, int ssl) { struct imap_client *client; @@ -357,7 +358,8 @@ client->refcount = 1; client->tls = ssl; - client->common.ip = *ip; + client->common.ip_remote = *ip_remote; + client->common.ip_local = *ip_local; client->common.fd = fd; client_open_streams(client, fd); @@ -439,7 +441,7 @@ { const char *addr; - addr = net_ip2addr(&client->common.ip); + addr = net_ip2addr(&client->common.ip_remote); if (addr == NULL) addr = "??"; diff -u -r work.patched/dovecot-0.99.10/src/imap-login/client.h work/dovecot-0.99.10/src/imap-login/client.h --- work.patched/dovecot-0.99.10/src/imap-login/client.h Tue Mar 4 19:43:04 2003 +++ work/dovecot-0.99.10/src/imap-login/client.h Fri Oct 24 14:23:02 2003 @@ -30,7 +30,8 @@ unsigned int destroyed:1; }; -struct client *client_create(int fd, struct ip_addr *ip, int ssl); +struct client *client_create(int fd, struct ip_addr *ip_remote, + struct ip_addr *ip_local, int ssl); void client_destroy(struct imap_client *client, const char *reason); void client_ref(struct imap_client *client); diff -u -r work.patched/dovecot-0.99.10/src/lib/var-expand.c work/dovecot-0.99.10/src/lib/var-expand.c --- work.patched/dovecot-0.99.10/src/lib/var-expand.c Sun Jan 26 19:27:51 2003 +++ work/dovecot-0.99.10/src/lib/var-expand.c Fri Oct 17 13:12:51 2003 @@ -5,7 +5,7 @@ #include "var-expand.h" void var_expand(string_t *dest, const char *str, - const char *user, const char *home) + const char *user, const char *home, const char *ip) { const char *var; unsigned int width; @@ -36,6 +36,9 @@ case 'd': var = strchr(user, '@'); if (var != NULL) var++; + break; + case 'i': + var = ip; break; default: str_append_c(dest, '%'); diff -u -r work.patched/dovecot-0.99.10/src/lib/var-expand.h work/dovecot-0.99.10/src/lib/var-expand.h --- work.patched/dovecot-0.99.10/src/lib/var-expand.h Sun Jan 26 19:27:51 2003 +++ work/dovecot-0.99.10/src/lib/var-expand.h Fri Oct 17 13:13:08 2003 @@ -6,8 +6,9 @@ %u user or user@domain %h home %n user - %d domain */ + %d domain + %i ip */ void var_expand(string_t *dest, const char *str, - const char *user, const char *home); + const char *user, const char *home, const char *ip); #endif diff -u -r work.patched/dovecot-0.99.10/src/login-common/auth-connection.c work/dovecot-0.99.10/src/login-common/auth-connection.c --- work.patched/dovecot-0.99.10/src/login-common/auth-connection.c Mon May 26 10:27:13 2003 +++ work/dovecot-0.99.10/src/login-common/auth-connection.c Fri Oct 24 14:47:17 2003 @@ -325,6 +325,7 @@ auth_request.protocol = protocol; auth_request.mech = request->mech; auth_request.id = request->id; + auth_request.ip_local = ((struct client *) request->context)->ip_local; if (o_stream_send(request->conn->output, &auth_request, sizeof(auth_request)) < 0) { errno = request->conn->output->stream_errno; @@ -342,6 +343,7 @@ /* send continued request to auth */ auth_request.type = AUTH_LOGIN_REQUEST_CONTINUE; auth_request.id = request->id; + auth_request.ip_local = ((struct client *) request->context)->ip_local; auth_request.data_size = data_size; if (o_stream_send(request->conn->output, &auth_request, diff -u -r work.patched/dovecot-0.99.10/src/login-common/client-common.h work/dovecot-0.99.10/src/login-common/client-common.h --- work.patched/dovecot-0.99.10/src/login-common/client-common.h Tue Mar 4 19:43:04 2003 +++ work/dovecot-0.99.10/src/login-common/client-common.h Fri Oct 24 14:23:58 2003 @@ -5,7 +5,8 @@ #include "master.h" struct client { - struct ip_addr ip; + struct ip_addr ip_remote; + struct ip_addr ip_local; int fd; struct io *io; @@ -17,7 +18,8 @@ /* ... */ }; -struct client *client_create(int fd, struct ip_addr *ip, int ssl); +struct client *client_create(int fd, struct ip_addr *ip_remote, + struct ip_addr *ip_local, int ssl); unsigned int clients_get_count(void); void clients_notify_auth_process(void); diff -u -r work.patched/dovecot-0.99.10/src/login-common/main.c work/dovecot-0.99.10/src/login-common/main.c --- work.patched/dovecot-0.99.10/src/login-common/main.c Sun Jun 22 18:09:48 2003 +++ work/dovecot-0.99.10/src/login-common/main.c Fri Oct 24 14:25:02 2003 @@ -75,28 +75,30 @@ static void login_accept(void *context __attr_unused__) { - struct ip_addr ip; + struct ip_addr ip_remote, ip_local; int fd; - fd = net_accept(LOGIN_LISTEN_FD, &ip, NULL); + fd = net_accept(LOGIN_LISTEN_FD, &ip_remote, NULL); if (fd < 0) { if (fd < -1) i_fatal("accept() failed: %m"); return; } + net_getsockname(fd, &ip_local, NULL); + if (process_per_connection) main_close_listen(); - (void)client_create(fd, &ip, FALSE); + (void)client_create(fd, &ip_remote, &ip_local, FALSE); } static void login_accept_ssl(void *context __attr_unused__) { - struct ip_addr ip; + struct ip_addr ip_remote, ip_local; int fd, fd_ssl; - fd = net_accept(LOGIN_SSL_LISTEN_FD, &ip, NULL); + fd = net_accept(LOGIN_SSL_LISTEN_FD, &ip_remote, NULL); if (fd < 0) { if (fd < -1) i_fatal("accept() failed: %m"); @@ -106,11 +108,12 @@ if (process_per_connection) main_close_listen(); - fd_ssl = ssl_proxy_new(fd, &ip); + fd_ssl = ssl_proxy_new(fd, &ip_remote); if (fd_ssl == -1) net_disconnect(fd); - else - (void)client_create(fd_ssl, &ip, TRUE); + + net_getsockname(fd_ssl, &ip_local, NULL); + (void)client_create(fd_ssl, &ip_remote, &ip_local, TRUE); } static void open_logfile(const char *name) @@ -219,7 +222,7 @@ int main(int argc __attr_unused__, char *argv[], char *envp[]) { const char *name, *group_name; - struct ip_addr ip; + struct ip_addr ip_remote, ip_local; int i, fd = -1, master_fd = -1; is_inetd = getenv("DOVECOT_MASTER") == NULL; @@ -257,7 +260,7 @@ main_init(); if (is_inetd) { - if (net_getsockname(1, &ip, NULL) < 0) { + if (net_getsockname(1, &ip_remote, NULL) < 0) { i_fatal("%s can be started only through dovecot " "master process, inetd or equilevant", argv[0]); } @@ -265,7 +268,7 @@ fd = 1; for (i = 1; i < argc; i++) { if (strcmp(argv[i], "--ssl") == 0) { - fd = ssl_proxy_new(fd, &ip); + fd = ssl_proxy_new(fd, &ip_remote); if (fd == -1) i_fatal("SSL initialization failed"); } else if (strncmp(argv[i], "--group=", 8) != 0) @@ -276,8 +279,10 @@ closing_down = TRUE; } - if (fd != -1) - (void)client_create(fd, &ip, TRUE); + if (fd != -1) { + net_getsockname(fd, &ip_local, NULL); + (void)client_create(fd, &ip_remote, &ip_local, TRUE); + } io_loop_run(ioloop); main_deinit(); diff -u -r work.patched/dovecot-0.99.10/src/login-common/master.c work/dovecot-0.99.10/src/login-common/master.c --- work.patched/dovecot-0.99.10/src/login-common/master.c Tue Mar 4 19:43:04 2003 +++ work/dovecot-0.99.10/src/login-common/master.c Fri Oct 24 14:25:57 2003 @@ -44,7 +44,7 @@ req.tag = client->fd; req.auth_pid = auth_pid; req.auth_id = auth_id; - req.ip = client->ip; + req.ip = client->ip_remote; if (fd_send(master_fd, client->fd, &req, sizeof(req)) != sizeof(req)) i_fatal("fd_send(%d) failed: %m", client->fd); diff -u -r work.patched/dovecot-0.99.10/src/master/mail-process.c work/dovecot-0.99.10/src/master/mail-process.c --- work.patched/dovecot-0.99.10/src/master/mail-process.c Fri Aug 22 18:04:27 2003 +++ work/dovecot-0.99.10/src/master/mail-process.c Fri Oct 17 13:25:54 2003 @@ -91,7 +91,7 @@ } /* expand %vars */ - var_expand(str, env, user, home); + var_expand(str, env, user, home, NULL); return str_c(str); } diff -u -r work.patched/dovecot-0.99.10/src/pop3-login/client.c work/dovecot-0.99.10/src/pop3-login/client.c --- work.patched/dovecot-0.99.10/src/pop3-login/client.c Mon May 26 10:27:13 2003 +++ work/dovecot-0.99.10/src/pop3-login/client.c Fri Oct 24 14:27:50 2003 @@ -42,7 +42,7 @@ if (!verbose_proctitle || !process_per_connection) return; - addr = net_ip2addr(&client->common.ip); + addr = net_ip2addr(&client->common.ip_remote); if (addr == NULL) addr = "??"; @@ -80,7 +80,7 @@ client->common.io = NULL; } - fd_ssl = ssl_proxy_new(client->common.fd, &client->common.ip); + fd_ssl = ssl_proxy_new(client->common.fd, &client->common.ip_remote); if (fd_ssl != -1) { client->tls = TRUE; client_set_title(client); @@ -231,7 +231,8 @@ } } -struct client *client_create(int fd, struct ip_addr *ip, int ssl) +struct client *client_create(int fd, struct ip_addr *ip_remote, + struct ip_addr *ip_local, int ssl) { struct pop3_client *client; @@ -250,7 +251,8 @@ client->refcount = 1; client->tls = ssl; - client->common.ip = *ip; + client->common.ip_remote = *ip_remote; + client->common.ip_local = *ip_local; client->common.fd = fd; client->common.io = io_add(fd, IO_READ, client_input, client); client_open_streams(client, fd); @@ -322,7 +324,7 @@ { const char *addr; - addr = net_ip2addr(&client->common.ip); + addr = net_ip2addr(&client->common.ip_remote); if (addr == NULL) addr = "??"; diff -u -r work.patched/dovecot-0.99.10/src/pop3-login/client.h work/dovecot-0.99.10/src/pop3-login/client.h --- work.patched/dovecot-0.99.10/src/pop3-login/client.h Tue Mar 4 19:43:04 2003 +++ work/dovecot-0.99.10/src/pop3-login/client.h Fri Oct 24 14:28:05 2003 @@ -24,7 +24,8 @@ unsigned int destroyed:1; }; -struct client *client_create(int fd, struct ip_addr *ip, int ssl); +struct client *client_create(int fd, struct ip_addr *ip_remote, + struct ip_addr *ip_local, int ssl); void client_destroy(struct pop3_client *client, const char *reason); void client_ref(struct pop3_client *client);
participants (1)
-
Matthew Reimer