/* * Copyright (C) 1996-8 Michael R. Elkins * * 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. */ /* * Rather crude POP3 support. */ #include "../config.h" #include "mutt.h" #include "mailbox.h" #include "mx.h" #ifdef BALSA_USE_THREADS #include "thread_msgs.h" #endif #include #include #include #include #include #include #include static int getLine (int fd, char *s, int len) { char ch; int bytes = 0; while (read (fd, &ch, 1) > 0) { *s++ = ch; bytes++; if (ch == '\n') { *s = 0; return (bytes); } /* make sure not to overwrite the buffer */ if (bytes == len - 1) { *s = 0; return bytes; } } *s = 0; return (-1); } #ifndef LIBMUTT static int getPass (void) { if (!PopPass) { char tmp[SHORT_STRING]; tmp[0] = '\0'; if (mutt_get_password ("POP Password: ", tmp, sizeof (tmp)) != 0 || *tmp == '\0') return 0; PopPass = safe_strdup (tmp); } return 1; } #endif void mutt_fetchPopMail (void) { struct sockaddr_in sin; #if SIZEOF_LONG == 4 long n; #else int n; #endif struct hostent *he; char buffer[2048]; char msgbuf[SHORT_STRING]; #ifdef BALSA_USE_THREADS MailThreadMessage *threadmsg; char threadbuf[160]; #endif char uid[80], last_uid[80]; int s, i, last = 0, tmp, total, msgs, bytes, err = 0; int num_bytes, tot_bytes; int first_msg; CONTEXT ctx; MESSAGE *msg = NULL; if (!PopHost) { mutt_error ("POP host is not defined."); return; } if (!PopUser) { mutt_error ("No POP username is defined."); return; } #ifndef LIBMUTT if (!getPass ()) return; #endif s = socket (AF_INET, SOCK_STREAM, IPPROTO_IP); memset ((char *) &sin, 0, sizeof(sin)); sin.sin_family = AF_INET; sin.sin_port = htons (PopPort); if ((n = inet_addr (NONULL(PopHost))) == -1) { /* Must be a DNS name */ if ((he = gethostbyname (NONULL(PopHost))) == NULL) { mutt_error ("Could not find address for host %s.", PopHost); return; } memcpy ((void *)&sin.sin_addr, *(he->h_addr_list), he->h_length); } else memcpy ((void *)&sin.sin_addr, (void *)&n, sizeof(n)); mutt_message ("Connecting to %s", inet_ntoa (sin.sin_addr)); if (connect (s, (struct sockaddr *) &sin, sizeof (struct sockaddr_in)) == -1) { mutt_perror ("connect"); return; } if (getLine (s, buffer, sizeof (buffer)) == -1) goto fail; if (mutt_strncmp (buffer, "+OK", 3) != 0) { mutt_remove_trailing_ws (buffer); mutt_error (buffer); goto finish; } snprintf (buffer, sizeof(buffer), "user %s\r\n", PopUser); write (s, buffer, mutt_strlen (buffer)); if (getLine (s, buffer, sizeof (buffer)) == -1) goto fail; if (mutt_strncmp (buffer, "+OK", 3) != 0) { mutt_remove_trailing_ws (buffer); mutt_error (buffer); goto finish; } snprintf (buffer, sizeof(buffer), "pass %s\r\n", NONULL(PopPass)); write (s, buffer, mutt_strlen (buffer)); if (getLine (s, buffer, sizeof (buffer)) == -1) goto fail; if (mutt_strncmp (buffer, "+OK", 3) != 0) { if(PopPass) memset(PopPass, 0, mutt_strlen(PopPass)); safe_free((void **) &PopPass); /* void the given password */ mutt_remove_trailing_ws (buffer); mutt_error (buffer[0] ? buffer : "Server closed connection!"); goto finish; } /* find out how many messages are in the mailbox. */ write (s, "stat\r\n", 6); if (getLine (s, buffer, sizeof (buffer)) == -1) goto fail; if (mutt_strncmp (buffer, "+OK", 3) != 0) { mutt_remove_trailing_ws (buffer); mutt_error (buffer); goto finish; } sscanf (buffer, "+OK %d %d", &msgs, &bytes); if (msgs == 0) { mutt_message ("No new mail in POP mailbox."); goto finish; } if (mx_open_mailbox (NONULL(Spoolfile), M_APPEND, &ctx) == NULL) goto finish; first_msg = 1; /* * If Messages are left on the server, be sure that we don't retrieve * them a second time. Do this by checking Unique IDs backward from the * last message, until a previously downloaded message is found. */ if ( !option (OPTPOPDELETE) ) { for( i = msgs; i > 0 ; i--) { snprintf( buffer, sizeof(buffer), "uidl %d\r\n", i); write( s, buffer, strlen(buffer) ); getLine (s, buffer, sizeof (buffer)); if (strncmp (buffer, "+OK", 3) != 0) { mutt_remove_trailing_ws( buffer); mutt_error (buffer); goto finish; /* I'm following the convention of libmutt, not my coding style */ } sscanf( buffer, "+OK %d %s", &tmp, uid ); if( i == msgs ) { strcpy( last_uid, uid ); // save uid of the last message for exit if( PopUID[0] == 0 ) break; /* no previous UID set */ } if( *PopUID && strcmp( uid, PopUID ) == 0 ) { /* * this message seen, so start w/ next in queue * * This will be larger than 'msgs' if no new messages * * forcing the for loop below to skip retrieving messages * */ first_msg = i + 1; break; } } } total = msgs - first_msg + 1; /* will be used for display later */ /* Check for the total amount of bytes mail to be received */ tot_bytes=0; for (i = first_msg ; i <= msgs ; i++) { snprintf(buffer, sizeof(buffer), "list %d\r\n", i); write (s, buffer, strlen(buffer)); if ( getLine (s, buffer, sizeof(buffer)) == -1) { mx_fastclose_mailbox (&ctx); goto fail; } if (sscanf (buffer,"+OK %d %d",&i,&num_bytes) != 2) { tot_bytes=-1; break; } tot_bytes += num_bytes; } num_bytes=0; for (i = first_msg ; i <= msgs ; i++) { snprintf (buffer, sizeof(buffer), "retr %d\r\n", i); write (s, buffer, strlen (buffer)); #ifdef BALSA_USE_THREADS sprintf( threadbuf, "Retrieving Message %d of %d", i - first_msg + 1, total ); MSGMAILTHREAD( threadmsg, MSGMAILTHREAD_MSGINFO, threadbuf,0,0 ); #endif if (getLine (s, buffer, sizeof (buffer)) == -1) { mx_fastclose_mailbox (&ctx); goto fail; } if (mutt_strncmp (buffer, "+OK", 3) != 0) { mutt_remove_trailing_ws (buffer); mutt_error (buffer); break; } if ((msg = mx_open_new_message (&ctx, NULL, M_ADD_FROM)) == NULL) { err = 1; break; } /* Now read the actual message. */ FOREVER { char *p; int chunk; if ((chunk = getLine (s, buffer, sizeof (buffer))) == -1) { mutt_error ("Error reading message!"); err = 1; break; } #ifdef BALSA_USE_THREADS sprintf( threadbuf,"Received %d bytes of %d",num_bytes,tot_bytes); MSGMAILTHREAD(threadmsg, MSGMAILTHREAD_PROGRESS, threadbuf, num_bytes,tot_bytes); #endif /* check to see if we got a full line */ if (buffer[chunk-2] == '\r' && buffer[chunk-1] == '\n') { if (mutt_strcmp(".\r\n", buffer) == 0) { /* end of message */ break; } num_bytes += chunk; /* change CRLF to just LF */ buffer[chunk-2] = '\n'; buffer[chunk-1] = 0; chunk--; /* see if the line was byte-stuffed */ if (buffer[0] == '.') { p = buffer + 1; chunk--; } else p = buffer; } else p = buffer; fwrite (p, 1, chunk, msg->fp); } if (mx_commit_message (msg, &ctx) != 0) { mutt_error ("Error while writing mailbox!"); err = 1; } mx_close_message (&msg); if (err) break; if (option (OPTPOPDELETE)) { /* delete the message on the server */ snprintf (buffer, sizeof(buffer), "dele %d\r\n", i); write (s, buffer, mutt_strlen (buffer)); /* eat the server response */ getLine (s, buffer, sizeof (buffer)); if (mutt_strncmp (buffer, "+OK", 3) != 0) { err = 1; mutt_remove_trailing_ws (buffer); mutt_error (buffer); break; } } mutt_message ("%s [%d messages read]", msgbuf, i); } if (msg) { mx_commit_message (msg, &ctx); mx_close_message (&msg); } mx_close_mailbox (&ctx); if (err) { /* make sure no messages get deleted */ write (s, "rset\r\n", 6); getLine (s, buffer, sizeof (buffer)); /* snarf the response */ } finish: /* exit gracefully */ write (s, "quit\r\n", 6); getLine (s, buffer, sizeof (buffer)); /* snarf the response */ close (s); strcpy( PopUID, last_uid ); return; /* not reached */ fail: mutt_error ("Server closed connection!"); close (s); }