/* This file is part of libmail.
(c) 2009 - Dimitris Mandalidis <mandas@users.sourceforge.net>
libmail is free software: you can redistribute it and/or modify
it under the terms of the GNU General Public License as published by
the Free Software Foundation, either version 3 of the License, or
(at your option) any later version.
libmail is distributed in the hope that it will be useful,
but WITHOUT ANY WARRANTY; without even the implied warranty of
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
GNU General Public License for more details.
You should have received a copy of the GNU General Public License
along with libmail. If not, see <http://www.gnu.org/licenses/>.
*/
#include <stdio.h>
#include <string.h>
#include <stdlib.h>
#include <unistd.h>
#include <sys/socket.h>
#include <netinet/in.h>
#include <arpa/inet.h>
#include <sys/types.h>
#include <netdb.h>
#include <sasl/sasl.h>
#include <sasl/saslutil.h>
#include <libmail/libmail.h>
#include <libmail/commons.h>
#include <libmail/pop3.h>
#include <libmail/imap4.h>
#include <libmail/md5sum.h>
#include <libmail/network.h>
#include <libmail/sasl.h>
#include <libmail/error.h>
static char *get_timestamp(char *dst, char *src) {
char *ptr1, *ptr2;
size_t n_chars;
ptr1 = strchr(src, '<');
ptr2 = strchr(src, '>');
if (ptr1 == NULL || ptr2 == NULL)
return NULL;
n_chars = strlen(ptr1)-strlen(ptr2)+1;
strncpy(dst, ptr1, n_chars);
dst[n_chars] = '\0';
return dst;
}
static int pop3_command_success(char *buffer) {
if (*buffer == '+')
return LIBMAIL_SUCCESS;
else
return LIBMAIL_SRVERROR;
}
int pop3_connect(pop3_server_t *server) {
int sfd;
if ((sfd = connect_to_server(server->hostname, server->port, server->ai_family)) < 0)
return sfd;
server->sfd = sfd;
if (send_command(NULL, 0, POP3_MAX_RESPONSE, server->greeting, NULL, server->sfd) != 0)
return LIBMAIL_SYSERROR;
if (pop3_command_success(server->greeting) != 0)
return LIBMAIL_SRVERROR;
return LIBMAIL_SUCCESS;
}
int pop3_disconnect(pop3_server_t *server) {
char buffer[POP3_MAX_RESPONSE+1];
if (send_command("QUIT\r\n", 6, POP3_MAX_RESPONSE, buffer, NULL, server->sfd) != 0)
return LIBMAIL_SYSERROR;
if (pop3_command_success(buffer) != 0)
return LIBMAIL_SRVERROR;
if (close(server->sfd) != 0)
return LIBMAIL_SYSERROR;
return LIBMAIL_SUCCESS;
}
static int pop3_get_sasl_mechs(char *mechlist, int sfd) {
char buffer[POP3_MAX_RESPONSE+1];
char *ptr;
if (send_command("CAPA\r\n", 6, POP3_MAX_RESPONSE, buffer, NULL, sfd) != LIBMAIL_SUCCESS)
return LIBMAIL_SYSERROR;
if (pop3_command_success(buffer) != LIBMAIL_SUCCESS)
return LIBMAIL_SRVERROR;
ptr = strstr(buffer, "SASL");
if (ptr == NULL)
return LIBMAIL_UNSUPPORTED_SASL;
strcpy(mechlist, ptr+5);
ptr = strchr(mechlist, '\r');
if (ptr != NULL)
*ptr = '\0';
return LIBMAIL_SUCCESS;
}
static sasl_pop3_authenticate(pop3_server_t *server) {
char snd_buffer[POP3_MAX_RESPONSE+1];
char rcv_buffer[POP3_MAX_RESPONSE+1];
sasl_conn_t *pconn;
int result;
const char out[POP3_MAX_RESPONSE+1];
const char *mechusing;
unsigned outlen;
char mechlist[POP3_MAX_RESPONSE+1];
char *ptr;
sasl_callback_t callbacks[] = {
{ SASL_CB_USER, &get_user, server->auth.username },
{ SASL_CB_PASS, &get_secret, server->auth.password },
{ SASL_CB_AUTHNAME, &get_user, server->auth.username },
{ SASL_CB_CANON_USER, &get_canon_user, NULL },
{ SASL_CB_LOG, &sasl_my_log, NULL },
{ SASL_CB_LIST_END, NULL, NULL }
};
if ((result = initialize_sasl(server->hostname, server->port, server->sfd, "pop", &pconn, callbacks)) != LIBMAIL_SUCCESS)
return result;
if ((result = pop3_get_sasl_mechs(mechlist, server->sfd)) != LIBMAIL_SUCCESS)
return result;
if (start_sasl(callbacks, mechlist, pconn, out, &outlen, &mechusing) != LIBMAIL_SUCCESS)
return LIBMAIL_SASL_ERROR;
snprintf(snd_buffer, POP3_MAX_RESPONSE, "AUTH %s\r\n", mechusing);
if (send_command(snd_buffer, strlen(snd_buffer), POP3_MAX_RESPONSE, rcv_buffer, NULL, server->sfd) != 0)
return LIBMAIL_SYSERROR;
if (pop3_command_success(rcv_buffer) != LIBMAIL_SUCCESS)
return LIBMAIL_SRVERROR;
do {
if (outlen != 0) { // Send initial data from client_start
snprintf(snd_buffer, POP3_MAX_COMMAND, "%s\r\n", out);
if (send_command(snd_buffer,outlen+2, POP3_MAX_RESPONSE, rcv_buffer, NULL, server->sfd) != 0)
return LIBMAIL_SYSERROR;
if (pop3_command_success(rcv_buffer) != LIBMAIL_SUCCESS)
return LIBMAIL_SRVERROR;
}
ptr = rcv_buffer + 2;
ptr = strrchr(ptr, '\r');
*ptr = '\0';
ptr = rcv_buffer + 2;
} while ((result = next_sasl_step(callbacks, pconn, ptr, strlen(ptr), out, &outlen)) == LIBMAIL_CONTINUE);
if (outlen != 0) {
snprintf(snd_buffer, POP3_MAX_COMMAND, "%s\r\n", out);
if (send_command(snd_buffer,outlen+2, POP3_MAX_RESPONSE, rcv_buffer, NULL, server->sfd) != 0)
return LIBMAIL_SYSERROR;
}
end_sasl(&pconn, callbacks);
return result;
}
int pop3_authenticate(pop3_server_t *server) {
char snd_buffer[POP3_MAX_RESPONSE+1];
char rcv_buffer[POP3_MAX_RESPONSE+1];
unsigned char digest[33];
char timestamp[POP3_MAX_RESPONSE+1];
char buffer[POP3_MAX_RESPONSE+1];
int n_print, result;
switch (server->auth.auth_type) {
case AUTH_POP3_USERPASS:
n_print = snprintf(snd_buffer, POP3_MAX_RESPONSE, "USER %s\r\n", server->auth.username);
if (send_command(snd_buffer, n_print, POP3_MAX_RESPONSE, rcv_buffer, NULL, server->sfd) != 0)
return LIBMAIL_SYSERROR;
if (pop3_command_success(rcv_buffer) != 0)
return LIBMAIL_SRVERROR;
n_print = snprintf(snd_buffer, POP3_MAX_RESPONSE, "PASS %s\r\n", server->auth.password);
if (send_command(snd_buffer, n_print, POP3_MAX_RESPONSE, rcv_buffer, NULL, server->sfd) != 0)
return LIBMAIL_SYSERROR;
if (pop3_command_success(rcv_buffer) != 0)
return LIBMAIL_SRVERROR;
break;
case AUTH_POP3_APOP:
if (get_timestamp(timestamp, server->greeting) == NULL)
return LIBMAIL_POP3_UNSUPPORTED_APOP;
snprintf(buffer, POP3_MAX_RESPONSE, "%s%s", timestamp, server->auth.password);
n_print = snprintf(snd_buffer, POP3_MAX_RESPONSE, "APOP %s %s\r\n", server->auth.username, md5_digest(digest, buffer));
if (send_command(snd_buffer, n_print, POP3_MAX_RESPONSE, rcv_buffer, NULL, server->sfd) != 0)
return LIBMAIL_SYSERROR;
if (pop3_command_success(rcv_buffer) != 0)
return LIBMAIL_SRVERROR;
break;
case AUTH_POP3_SASL:
if ((result = sasl_pop3_authenticate(server)) != LIBMAIL_SUCCESS)
return result;
break;
default:
break;
}
return LIBMAIL_SUCCESS;
}
int pop3_check(pop3_server_t *server, int *n_new_messages, int *n_messages) {
char buffer[POP3_MAX_RESPONSE+1];
if (send_command("STAT\r\n", 6, POP3_MAX_RESPONSE, buffer, NULL, server->sfd) != 0)
return LIBMAIL_SYSERROR;
if (pop3_command_success(buffer) != 0)
return LIBMAIL_SRVERROR;
sscanf(buffer, "+OK %d", n_messages);
// TODO next line should be replaced with the real check of unread messages (use of UIDL)
*n_new_messages = *n_messages;
//
return LIBMAIL_SUCCESS;
}