/*
* WMMail - Window Maker Mail
*
* Copyright (c) 1996, 1997, 1998 Per Liden
* Copyright (c) 1997, 1998 Bryan Chan
*
* This program 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 2 of the License, or
* (at your option) any later version.
*
* This program 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 this program; if not, write to the Free Software
* Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
*
* pop3.c: functions to handle POP3 mailboxes
*
* $Id: pop3.c,v 1.2 2000/07/03 08:49:24 bryan.chan Exp $
*
*/
#include <stdio.h>
#include <netdb.h>
#include <sys/types.h>
#include <sys/socket.h>
#include <netinet/in.h>
#include <time.h>
#include <proplist.h>
#include "wmmail.h"
#include "wmutil.h"
#include "net.h"
#ifdef POP3_SUPPORT
/*
* Supported key(s) in the Options dictionary:
*
* Hostname
* Port (optional)
* Username
* Password
*
* See mbox.c for information on states and transitions.
*
* For a local mailbox, errors encountered during an update usually
* cause the program to give up monitoring the mailbox ("ignored").
*
* For remote mailboxes, network errors are ignored. Query will be
* re-tried at the next update. Other types of errors (such as invalid
* options) still cause the program to give up.
*
*/
/* internal data structure */
typedef struct {
char *folder_name;
int socket;
} Connection;
/* internal functions */
static Connection *init_connection(Mailbox *);
static int count_mail(Connection *, int *, int *);
static void close_connection(Connection *);
#define MAX_STRING_SIZE 1024
#define GET_ENTRY(x,y,z) { proplist_t sub_key = PLMakeString(z); \
y = PLGetDictionaryEntry(x, sub_key); \
PLRelease(sub_key); }
int POP3_check(Mailbox *mailbox, int *beep, int *redraw, int *run)
{
Connection *connection;
int prev_status,
prev_new_mail_count;
connection = init_connection(mailbox);
mailbox->last_update = time(NULL);
if (connection == NULL)
return False;
else if (connection == (Connection *) -1)
return True;
else
{
prev_status = mailbox->status;
prev_new_mail_count = mailbox->new_mail_count;
if (count_mail(connection,
&mailbox->total_mail_count,
&mailbox->new_mail_count))
{
close_connection(connection);
if (mailbox->total_mail_count == 0)
{
/* there is no mail in the mailbox */
mailbox->status = NO_MAIL;
}
else if (mailbox->new_mail_count == 0)
{
/* there are no new mails */
mailbox->status = OLD_MAIL;
}
else if (mailbox->new_mail_count > prev_new_mail_count)
{
/* new mails have arrived! */
if (mailbox->status == NEW_MAIL && always_new_mail_exec)
*run |= True;
else if (mailbox->status == NEW_MAIL)
*run |= False;
else
*run |= True;
*beep = True;
mailbox->status = NEW_MAIL;
}
/* else no change */
}
else
{
croak("cannot count messages in mailbox \"%s\"; ignored", mailbox->name);
close_connection(connection);
return False;
}
*redraw |= (prev_status != mailbox->status);
return True;
}
}
/* creates a connection to the POP3 server and logs in
*
* returns: -1 if recoverable error encountered
* NULL if unrecoverable error encountered
* pointer to Connection if successfully initiated
*/
static Connection *init_connection(Mailbox *mailbox)
{
Connection *connection;
struct sockaddr_in remote_addr;
struct hostent *host;
char buf[MAX_STRING_SIZE];
int sockfd,
port_no = -1;
proplist_t hostname,
port,
username,
password,
folder;
GET_ENTRY(mailbox->options, hostname, "Hostname");
GET_ENTRY(mailbox->options, port, "Port");
GET_ENTRY(mailbox->options, username, "Username");
GET_ENTRY(mailbox->options, password, "Password");
if (!username || !password || !hostname)
{
croak("mailbox \"%s\" missing one of the options \"Username\", "
"\"Password\", and \"Hostname\"; ignored", mailbox->name);
return NULL;
}
else if ( !PLIsString(hostname) ||
!PLIsString(username) ||
!PLIsString(password) ||
(port && (!PLIsString(port) ||
sscanf(PLGetString(port), "%i", &port_no) != 1)) )
{
croak("mailbox \"%s\" has invalid options; ignored", mailbox->name);
return (Connection *) NULL;
}
if (( host = gethostbyname(PLGetString(hostname)) ) == NULL)
{
croak("gethostbyname() failed for mailbox \"%s\"; continuing",
mailbox->name);
return (Connection *) -1;
}
if (( sockfd = socket(AF_INET, SOCK_STREAM, 0) ) == -1)
{
croak("socket() failed for mailbox\"%s\"; continuing", mailbox->name);
return (Connection *) -1;
}
remote_addr.sin_family = AF_INET;
remote_addr.sin_port = htons(port_no == -1 ? 110 : port_no);
remote_addr.sin_addr = *((struct in_addr *) host->h_addr);
bzero(&remote_addr.sin_zero, 8);
if (connect(sockfd, (struct sockaddr *) &remote_addr,
sizeof(struct sockaddr)) == -1)
{
croak("connect() failed for mailbox\"%s\"; continuing", mailbox->name);
close(sockfd);
return (Connection *) -1;
}
/* are we welcome? */
socket_read(sockfd, buf, MAX_STRING_SIZE);
if (strncasecmp(buf, "+OK ", 4))
{
/* give up if not; we don't want to force open a door that won't */
croak("no service from remote server for mailbox \"%s\"; ignored",
mailbox->name);
close(sockfd);
return (Connection *) NULL;
}
sprintf(buf, "USER %s\n", PLGetString(username));
socket_write(sockfd, buf);
socket_read(sockfd, buf, MAX_STRING_SIZE);
if (strncasecmp(buf, "+OK ", 4))
{
croak("Bad username specified for mailbox \"mailbox\"; ignored",
mailbox->name);
close(sockfd);
return (Connection *) NULL;
}
sprintf(buf, "PASS %s\n", PLGetString(password));
socket_write(sockfd, buf);
socket_read(sockfd, buf, MAX_STRING_SIZE);
if (strncasecmp(buf, "+OK ", 4))
{
/* if we can't login, then don't try any more */
croak("login failed for mailbox \"%s\"; ignored", mailbox->name);
close(sockfd);
return NULL;
}
connection = wmalloc(sizeof(Connection));
connection->socket = sockfd;
return connection;
}
/* FIXME: socket errors are transparent! */
static int count_mail(connection, total_mail_count, new_mail_count)
Connection *connection;
int *total_mail_count,
*new_mail_count;
{
int i;
char buf[MAX_STRING_SIZE + 1];
char cmd[MAX_STRING_SIZE + 1];
buf[MAX_STRING_SIZE] = 0;
cmd[MAX_STRING_SIZE] = 0;
sprintf(cmd, "STAT\n");
if (socket_write(connection->socket, cmd) &&
socket_read(connection->socket, buf, MAX_STRING_SIZE)) {
if (!strncasecmp(buf, "+OK ", 4)) {
sscanf(buf + 4, "%d", total_mail_count);
}
}
for (i = 0, *new_mail_count = 0; i < *total_mail_count; i++) {
sprintf(cmd, "TOP %d 0\n", i + 1);
if (socket_write(connection->socket, cmd)) {
int j = 1;
while (socket_read(connection->socket, buf, MAX_STRING_SIZE) &&
strncmp(buf, ".", 1)) {
if (!strncmp(buf, "Status:", 7) && index(buf + 7, 'R'))
j = 0;
}
(*new_mail_count) += j;
}
}
return True;
}
static void close_connection(Connection *connection)
{
char buf[MAX_STRING_SIZE];
sprintf(buf, "QUIT\n");
socket_write(connection->socket, buf);
socket_read(connection->socket, buf, MAX_STRING_SIZE);
close(connection->socket);
wfree(connection);
}
#endif
syntax highlighted by Code2HTML, v. 0.9.1