Move imap/pop3 stuff into mail.c
[monky] / src / mail.c
1 /* Conky, a system monitor, based on torsmo
2  *
3  * Any original torsmo code is licensed under the BSD license
4  *
5  * All code written since the fork of torsmo is licensed under the GPL
6  *
7  * Please see COPYING for details
8  *
9  * Copyright (c) 2004, Hannu Saransaari and Lauri Hakkarainen
10  * Copyright (c) 2005-2008 Brenden Matthews, Philip Kovacs, et. al.
11  *      (see AUTHORS)
12  * All rights reserved.
13  *
14  * This program is free software: you can redistribute it and/or modify
15  * it under the terms of the GNU General Public License as published by
16  * the Free Software Foundation, either version 3 of the License, or
17  * (at your option) any later version.
18  *
19  * This program is distributed in the hope that it will be useful,
20  * but WITHOUT ANY WARRANTY; without even the implied warranty of
21  * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
22  * GNU General Public License for more details.
23  * You should have received a copy of the GNU General Public License
24  * along with this program.  If not, see <http://www.gnu.org/licenses/>.
25  *
26  */
27
28 #include "conky.h"
29
30 #include <sys/stat.h>
31 #include <sys/time.h>
32
33 #include <dirent.h>
34 #include <errno.h>
35 #include <termios.h>
36
37 char *current_mail_spool;
38
39 void update_mail_count(struct local_mail_s *mail)
40 {
41         struct stat st;
42
43         if (mail == NULL) {
44                 return;
45         }
46
47         /* TODO: use that fine file modification notify on Linux 2.4 */
48
49         /* don't check mail so often (9.5s is minimum interval) */
50         if (current_update_time - mail->last_update < 9.5) {
51                 return;
52         } else {
53                 mail->last_update = current_update_time;
54         }
55
56         if (stat(mail->box, &st)) {
57                 static int rep;
58
59                 if (!rep) {
60                         ERR("can't stat %s: %s", mail->box, strerror(errno));
61                         rep = 1;
62                 }
63                 return;
64         }
65 #if HAVE_DIRENT_H
66         /* maildir format */
67         if (S_ISDIR(st.st_mode)) {
68                 DIR *dir;
69                 char *dirname;
70                 struct dirent *dirent;
71
72                 mail->mail_count = mail->new_mail_count = 0;
73                 dirname = (char *) malloc(sizeof(char) * (strlen(mail->box) + 5));
74                 if (!dirname) {
75                         ERR("malloc");
76                         return;
77                 }
78                 strcpy(dirname, mail->box);
79                 strcat(dirname, "/");
80                 /* checking the cur subdirectory */
81                 strcat(dirname, "cur");
82
83                 dir = opendir(dirname);
84                 if (!dir) {
85                         ERR("cannot open directory");
86                         free(dirname);
87                         return;
88                 }
89                 dirent = readdir(dir);
90                 while (dirent) {
91                         /* . and .. are skipped */
92                         if (dirent->d_name[0] != '.') {
93                                 mail->mail_count++;
94                         }
95                         dirent = readdir(dir);
96                 }
97                 closedir(dir);
98
99                 dirname[strlen(dirname) - 3] = '\0';
100                 strcat(dirname, "new");
101
102                 dir = opendir(dirname);
103                 if (!dir) {
104                         ERR("cannot open directory");
105                         free(dirname);
106                         return;
107                 }
108                 dirent = readdir(dir);
109                 while (dirent) {
110                         /* . and .. are skipped */
111                         if (dirent->d_name[0] != '.') {
112                                 mail->new_mail_count++;
113                                 mail->mail_count++;
114                         }
115                         dirent = readdir(dir);
116                 }
117                 closedir(dir);
118
119                 free(dirname);
120                 return;
121         }
122 #endif
123         /* mbox format */
124         if (st.st_mtime != mail->last_mtime) {
125                 /* yippee, modification time has changed, let's read mail count! */
126                 static int rep;
127                 FILE *fp;
128                 int reading_status = 0;
129
130                 /* could lock here but I don't think it's really worth it because
131                  * this isn't going to write mail spool */
132
133                 mail->new_mail_count = mail->mail_count = 0;
134
135                 fp = open_file(mail->box, &rep);
136                 if (!fp) {
137                         return;
138                 }
139
140                 /* NOTE: adds mail as new if there isn't Status-field at all */
141
142                 while (!feof(fp)) {
143                         char buf[128];
144
145                         if (fgets(buf, 128, fp) == NULL) {
146                                 break;
147                         }
148
149                         if (strncmp(buf, "From ", 5) == 0) {
150                                 /* ignore MAILER-DAEMON */
151                                 if (strncmp(buf + 5, "MAILER-DAEMON ", 14) != 0) {
152                                         mail->mail_count++;
153
154                                         if (reading_status) {
155                                                 mail->new_mail_count++;
156                                         } else {
157                                                 reading_status = 1;
158                                         }
159                                 }
160                         } else {
161                                 if (reading_status
162                                                 && strncmp(buf, "X-Mozilla-Status:", 17) == 0) {
163                                         /* check that mail isn't already read */
164                                         if (strchr(buf + 21, '0')) {
165                                                 mail->new_mail_count++;
166                                         }
167
168                                         reading_status = 0;
169                                         continue;
170                                 }
171                                 if (reading_status && strncmp(buf, "Status:", 7) == 0) {
172                                         /* check that mail isn't already read */
173                                         if (strchr(buf + 7, 'R') == NULL) {
174                                                 mail->new_mail_count++;
175                                         }
176
177                                         reading_status = 0;
178                                         continue;
179                                 }
180                         }
181
182                         /* skip until \n */
183                         while (strchr(buf, '\n') == NULL && !feof(fp)) {
184                                 fgets(buf, 128, fp);
185                         }
186                 }
187
188                 fclose(fp);
189
190                 if (reading_status) {
191                         mail->new_mail_count++;
192                 }
193
194                 mail->last_mtime = st.st_mtime;
195         }
196 }
197
198 #define MAXDATASIZE 1000
199
200 struct mail_s *parse_mail_args(char type, const char *arg)
201 {
202         struct mail_s *mail;
203         char *tmp;
204
205         mail = malloc(sizeof(struct mail_s));
206         memset(mail, 0, sizeof(struct mail_s));
207
208         if (sscanf(arg, "%128s %128s %128s", mail->host, mail->user, mail->pass)
209                         != 3) {
210                 if (type == POP3_TYPE) {
211                         ERR("Scanning IMAP args failed");
212                 } else if (type == IMAP_TYPE) {
213                         ERR("Scanning POP3 args failed");
214                 }
215         }
216         // see if password needs prompting
217         if (mail->pass[0] == '*' && mail->pass[1] == '\0') {
218                 int fp = fileno(stdin);
219                 struct termios term;
220
221                 tcgetattr(fp, &term);
222                 term.c_lflag &= ~ECHO;
223                 tcsetattr(fp, TCSANOW, &term);
224                 printf("Enter mailbox password (%s@%s): ", mail->user, mail->host);
225                 scanf("%128s", mail->pass);
226                 printf("\n");
227                 term.c_lflag |= ECHO;
228                 tcsetattr(fp, TCSANOW, &term);
229         }
230         // now we check for optional args
231         tmp = strstr(arg, "-r ");
232         if (tmp) {
233                 tmp += 3;
234                 sscanf(tmp, "%u", &mail->retries);
235         } else {
236                 mail->retries = 5;      // 5 retries after failure
237         }
238         tmp = strstr(arg, "-i ");
239         if (tmp) {
240                 tmp += 3;
241                 sscanf(tmp, "%f", &mail->interval);
242         } else {
243                 mail->interval = 300;   // 5 minutes
244         }
245         tmp = strstr(arg, "-p ");
246         if (tmp) {
247                 tmp += 3;
248                 sscanf(tmp, "%lu", &mail->port);
249         } else {
250                 if (type == POP3_TYPE) {
251                         mail->port = 110;       // default pop3 port
252                 } else if (type == IMAP_TYPE) {
253                         mail->port = 143;       // default imap port
254                 }
255         }
256         if (type == IMAP_TYPE) {
257                 tmp = strstr(arg, "-f ");
258                 if (tmp) {
259                         tmp += 3;
260                         sscanf(tmp, "%s", mail->folder);
261                 } else {
262                         strncpy(mail->folder, "INBOX", 128);    // default imap inbox
263                 }
264         }
265         tmp = strstr(arg, "-e ");
266         if (tmp) {
267                 int len = 1024;
268                 tmp += 3;
269
270                 if (tmp[0] == '\'') {
271                         len = strstr(tmp + 1, "'") - tmp - 1;
272                         if (len > 1024) {
273                                 len = 1024;
274                         }
275                 }
276                 strncpy(mail->command, tmp + 1, len);
277         } else {
278                 mail->command[0] = '\0';
279         }
280         mail->p_timed_thread = NULL;
281         return mail;
282 }
283
284 int imap_command(int sockfd, const char *command, char *response, const char *verify)
285 {
286         struct timeval timeout;
287         fd_set fdset;
288         int res, numbytes = 0;
289         if (send(sockfd, command, strlen(command), 0) == -1) {
290                 perror("send");
291                 return -1;
292         }
293         timeout.tv_sec = 60;    // 60 second timeout i guess
294         timeout.tv_usec = 0;
295         FD_ZERO(&fdset);
296         FD_SET(sockfd, &fdset);
297         res = select(sockfd + 1, &fdset, NULL, NULL, &timeout);
298         if (res > 0) {
299                 if ((numbytes = recv(sockfd, response, MAXDATASIZE - 1, 0)) == -1) {
300                         perror("recv");
301                         return -1;
302                 }
303         }
304         DBGP2("imap_command() received: %s", response);
305         response[numbytes] = '\0';
306         if (strstr(response, verify) == NULL) {
307                 return -1;
308         }
309         return 0;
310 }
311
312 int imap_check_status(char *recvbuf, struct mail_s *mail)
313 {
314         char *reply;
315         reply = strstr(recvbuf, " (MESSAGES ");
316         if (!reply || strlen(reply) < 2) {
317                 return -1;
318         }
319         reply += 2;
320         *strchr(reply, ')') = '\0';
321         if (reply == NULL) {
322                 ERR("Error parsing IMAP response: %s", recvbuf);
323                 return -1;
324         } else {
325                 timed_thread_lock(mail->p_timed_thread);
326                 sscanf(reply, "MESSAGES %lu UNSEEN %lu", &mail->messages,
327                                 &mail->unseen);
328                 timed_thread_unlock(mail->p_timed_thread);
329         }
330         return 0;
331 }
332
333 void imap_unseen_command(struct mail_s *mail, unsigned long old_unseen, unsigned long old_messages)
334 {
335         if (strlen(mail->command) > 1 && (mail->unseen > old_unseen
336                                 || (mail->messages > old_messages && mail->unseen > 0))) {
337                 // new mail goodie
338                 if (system(mail->command) == -1) {
339                         perror("system()");
340                 }
341         }
342 }
343
344 void *imap_thread(void *arg)
345 {
346         int sockfd, numbytes;
347         char recvbuf[MAXDATASIZE];
348         char sendbuf[MAXDATASIZE];
349         unsigned int fail = 0;
350         unsigned long old_unseen = ULONG_MAX;
351         unsigned long old_messages = ULONG_MAX;
352         struct stat stat_buf;
353         struct hostent he, *he_res = 0;
354         int he_errno;
355         char hostbuff[2048];
356         struct sockaddr_in their_addr;  // connector's address information
357         struct mail_s *mail = (struct mail_s *)arg;
358         int has_idle = 0;
359         int threadfd = timed_thread_readfd(mail->p_timed_thread);
360
361 #ifdef HAVE_GETHOSTBYNAME_R
362         if (gethostbyname_r(mail->host, &he, hostbuff, sizeof(hostbuff), &he_res, &he_errno)) { // get the host info
363                 ERR("IMAP gethostbyname_r: %s", hstrerror(h_errno));
364                 exit(1);
365         }
366 #else /* HAVE_GETHOSTBYNAME_R */
367         if ((he_res = gethostbyname(mail->host)) == NULL) {     // get the host info
368                 herror("gethostbyname");
369                 exit(1);
370         }
371 #endif /* HAVE_GETHOSTBYNAME_R */
372         while (fail < mail->retries) {
373                 struct timeval timeout;
374                 int res;
375                 fd_set fdset;
376
377                 if (fail > 0) {
378                         ERR("Trying IMAP connection again for %s@%s (try %u/%u)",
379                                         mail->user, mail->host, fail + 1, mail->retries);
380                 }
381                 do {
382                         if ((sockfd = socket(PF_INET, SOCK_STREAM, 0)) == -1) {
383                                 perror("socket");
384                                 fail++;
385                                 break;
386                         }
387
388                         // host byte order
389                         their_addr.sin_family = AF_INET;
390                         // short, network byte order
391                         their_addr.sin_port = htons(mail->port);
392                         their_addr.sin_addr = *((struct in_addr *) he_res->h_addr);
393                         // zero the rest of the struct
394                         memset(&(their_addr.sin_zero), '\0', 8);
395
396                         if (connect(sockfd, (struct sockaddr *) &their_addr,
397                                                 sizeof(struct sockaddr)) == -1) {
398                                 perror("connect");
399                                 fail++;
400                                 break;
401                         }
402
403                         timeout.tv_sec = 60;    // 60 second timeout i guess
404                         timeout.tv_usec = 0;
405                         FD_ZERO(&fdset);
406                         FD_SET(sockfd, &fdset);
407                         res = select(sockfd + 1, &fdset, NULL, NULL, &timeout);
408                         if (res > 0) {
409                                 if ((numbytes = recv(sockfd, recvbuf, MAXDATASIZE - 1, 0)) == -1) {
410                                         perror("recv");
411                                         fail++;
412                                         break;
413                                 }
414                         } else {
415                                 ERR("IMAP connection failed: timeout");
416                                 fail++;
417                                 break;
418                         }
419                         recvbuf[numbytes] = '\0';
420                         DBGP2("imap_thread() received: %s", recvbuf);
421                         if (strstr(recvbuf, "* OK") != recvbuf) {
422                                 ERR("IMAP connection failed, probably not an IMAP server");
423                                 fail++;
424                                 break;
425                         }
426                         strncpy(sendbuf, "a1 login ", MAXDATASIZE);
427                         strncat(sendbuf, mail->user, MAXDATASIZE - strlen(sendbuf) - 1);
428                         strncat(sendbuf, " ", MAXDATASIZE - strlen(sendbuf) - 1);
429                         strncat(sendbuf, mail->pass, MAXDATASIZE - strlen(sendbuf) - 1);
430                         strncat(sendbuf, "\r\n", MAXDATASIZE - strlen(sendbuf) - 1);
431                         if (imap_command(sockfd, sendbuf, recvbuf, "a1 OK")) {
432                                 fail++;
433                                 break;
434                         }
435                         if (strstr(recvbuf, " IDLE ") != NULL) {
436                                 has_idle = 1;
437                         }
438
439                         strncpy(sendbuf, "a2 STATUS ", MAXDATASIZE);
440                         strncat(sendbuf, mail->folder, MAXDATASIZE - strlen(sendbuf) - 1);
441                         strncat(sendbuf, " (MESSAGES UNSEEN)\r\n",
442                                         MAXDATASIZE - strlen(sendbuf) - 1);
443                         if (imap_command(sockfd, sendbuf, recvbuf, "a2 OK")) {
444                                 fail++;
445                                 break;
446                         }
447
448                         if (imap_check_status(recvbuf, mail)) {
449                                 fail++;
450                                 break;
451                         }
452                         imap_unseen_command(mail, old_unseen, old_messages);
453                         fail = 0;
454                         old_unseen = mail->unseen;
455                         old_messages = mail->messages;
456
457                         if (has_idle) {
458                                 strncpy(sendbuf, "a4 SELECT ", MAXDATASIZE);
459                                 strncat(sendbuf, mail->folder, MAXDATASIZE - strlen(sendbuf) - 1);
460                                 strncat(sendbuf, "\r\n", MAXDATASIZE - strlen(sendbuf) - 1);
461                                 if (imap_command(sockfd, sendbuf, recvbuf, "a4 OK")) {
462                                         fail++;
463                                         break;
464                                 }
465
466                                 strncpy(sendbuf, "a5 IDLE\r\n", MAXDATASIZE);
467                                 if (imap_command(sockfd, sendbuf, recvbuf, "+ idling")) {
468                                         fail++;
469                                         break;
470                                 }
471                                 recvbuf[0] = '\0';
472
473                                 while (1) {
474                                         /*
475                                          * RFC 2177 says we have to re-idle every 29 minutes.
476                                          * We'll do it every 20 minutes to be safe.
477                                          */
478                                         timeout.tv_sec = 1200;
479                                         timeout.tv_usec = 0;
480                                         FD_ZERO(&fdset);
481                                         FD_SET(sockfd, &fdset);
482                                         FD_SET(threadfd, &fdset);
483                                         res = select(MAX(sockfd + 1, threadfd + 1), &fdset, NULL, NULL, NULL);
484                                         if (timed_thread_test(mail->p_timed_thread, 1) || (res == -1 && errno == EINTR) || FD_ISSET(threadfd, &fdset)) {
485                                                 if ((fstat(sockfd, &stat_buf) == 0) && S_ISSOCK(stat_buf.st_mode)) {
486                                                         /* if a valid socket, close it */
487                                                         close(sockfd);
488                                                 }
489                                                 timed_thread_exit(mail->p_timed_thread);
490                                         } else if (res > 0) {
491                                                 if ((numbytes = recv(sockfd, recvbuf, MAXDATASIZE - 1, 0)) == -1) {
492                                                         perror("recv idling");
493                                                         fail++;
494                                                         break;
495                                                 }
496                                         } else {
497                                                 break;
498                                         }
499                                         recvbuf[numbytes] = '\0';
500                                         DBGP2("imap_thread() received: %s", recvbuf);
501                                         if (strlen(recvbuf) > 2) {
502                                                 unsigned long messages, recent;
503                                                 char *buf = recvbuf;
504                                                 buf = strstr(buf, "EXISTS");
505                                                 while (buf && strlen(buf) > 1 && strstr(buf + 1, "EXISTS")) {
506                                                         buf = strstr(buf + 1, "EXISTS");
507                                                 }
508                                                 if (buf) {
509                                                         // back up until we reach '*'
510                                                         while (buf >= recvbuf && buf[0] != '*') {
511                                                                 buf--;
512                                                         }
513                                                         if (sscanf(buf, "* %lu EXISTS\r\n", &messages) == 1) {
514                                                                 timed_thread_lock(mail->p_timed_thread);
515                                                                 mail->messages = messages;
516                                                                 timed_thread_unlock(mail->p_timed_thread);
517                                                         }
518                                                 }
519                                                 buf = recvbuf;
520                                                 buf = strstr(buf, "RECENT");
521                                                 while (buf && strlen(buf) > 1 && strstr(buf + 1, "RECENT")) {
522                                                         buf = strstr(buf + 1, "RECENT");
523                                                 }
524                                                 if (buf) {
525                                                         // back up until we reach '*'
526                                                         while (buf >= recvbuf && buf[0] != '*') {
527                                                                 buf--;
528                                                         }
529                                                         if (sscanf(buf, "* %lu RECENT\r\n", &recent) != 1) {
530                                                                 recent = 0;
531                                                         }
532                                                 }
533                                                 /*
534                                                  * check if we got a FETCH from server, recent was
535                                                  * something other than 0, or we had a timeout
536                                                  */
537                                                 buf = recvbuf;
538                                                 if (recent > 0 || (buf && strstr(buf, " FETCH ")) || timeout.tv_sec == 0) {
539                                                         // re-check messages and unseen
540                                                         if (imap_command(sockfd, "DONE\r\n", recvbuf, "a5 OK")) {
541                                                                 fail++;
542                                                                 break;
543                                                         }
544                                                         strncpy(sendbuf, "a2 STATUS ", MAXDATASIZE);
545                                                         strncat(sendbuf, mail->folder, MAXDATASIZE - strlen(sendbuf) - 1);
546                                                         strncat(sendbuf, " (MESSAGES UNSEEN)\r\n",
547                                                                         MAXDATASIZE - strlen(sendbuf) - 1);
548                                                         if (imap_command(sockfd, sendbuf, recvbuf, "a2 OK")) {
549                                                                 fail++;
550                                                                 break;
551                                                         }
552                                                         if (imap_check_status(recvbuf, mail)) {
553                                                                 fail++;
554                                                                 break;
555                                                         }
556                                                         strncpy(sendbuf, "a5 IDLE\r\n", MAXDATASIZE);
557                                                         if (imap_command(sockfd, sendbuf, recvbuf, "+ idling")) {
558                                                                 fail++;
559                                                                 break;
560                                                         }
561                                                 }
562                                                 /*
563                                                  * check if we got a BYE from server
564                                                  */
565                                                 buf = recvbuf;
566                                                 if (buf && strstr(buf, "* BYE")) {
567                                                         // need to re-connect
568                                                         break;
569                                                 }
570                                         }
571                                         imap_unseen_command(mail, old_unseen, old_messages);
572                                         fail = 0;
573                                         old_unseen = mail->unseen;
574                                         old_messages = mail->messages;
575                                 }
576                         } else {
577                                 strncpy(sendbuf, "a3 logout\r\n", MAXDATASIZE);
578                                 if (send(sockfd, sendbuf, strlen(sendbuf), 0) == -1) {
579                                         perror("send a3");
580                                         fail++;
581                                         break;
582                                 }
583                                 timeout.tv_sec = 60;    // 60 second timeout i guess
584                                 timeout.tv_usec = 0;
585                                 FD_ZERO(&fdset);
586                                 FD_SET(sockfd, &fdset);
587                                 res = select(sockfd + 1, &fdset, NULL, NULL, &timeout);
588                                 if (res > 0) {
589                                         if ((numbytes = recv(sockfd, recvbuf, MAXDATASIZE - 1, 0)) == -1) {
590                                                 perror("recv a3");
591                                                 fail++;
592                                                 break;
593                                         }
594                                 }
595                                 recvbuf[numbytes] = '\0';
596                                 DBGP2("imap_thread() received: %s", recvbuf);
597                                 if (strstr(recvbuf, "a3 OK") == NULL) {
598                                         ERR("IMAP logout failed: %s", recvbuf);
599                                         fail++;
600                                         break;
601                                 }
602                         }
603                 } while (0);
604                 if ((fstat(sockfd, &stat_buf) == 0) && S_ISSOCK(stat_buf.st_mode)) {
605                         /* if a valid socket, close it */
606                         close(sockfd);
607                 }
608                 if (timed_thread_test(mail->p_timed_thread, 0)) {
609                         timed_thread_exit(mail->p_timed_thread);
610                 }
611         }
612         mail->unseen = 0;
613         mail->messages = 0;
614         return 0;
615 }
616
617 int pop3_command(int sockfd, const char *command, char *response, const char *verify)
618 {
619         struct timeval timeout;
620         fd_set fdset;
621         int res, numbytes = 0;
622         if (send(sockfd, command, strlen(command), 0) == -1) {
623                 perror("send");
624                 return -1;
625         }
626         timeout.tv_sec = 60;    // 60 second timeout i guess
627         timeout.tv_usec = 0;
628         FD_ZERO(&fdset);
629         FD_SET(sockfd, &fdset);
630         res = select(sockfd + 1, &fdset, NULL, NULL, &timeout);
631         if (res > 0) {
632                 if ((numbytes = recv(sockfd, response, MAXDATASIZE - 1, 0)) == -1) {
633                         perror("recv");
634                         return -1;
635                 }
636         }
637         DBGP2("pop3_command() received: %s", response);
638         response[numbytes] = '\0';
639         if (strstr(response, verify) == NULL) {
640                 return -1;
641         }
642         return 0;
643 }
644
645 void *pop3_thread(void *arg)
646 {
647         int sockfd, numbytes;
648         char recvbuf[MAXDATASIZE];
649         char sendbuf[MAXDATASIZE];
650         char *reply;
651         unsigned int fail = 0;
652         unsigned long old_unseen = ULONG_MAX;
653         struct stat stat_buf;
654         struct hostent he, *he_res = 0;
655         int he_errno;
656         char hostbuff[2048];
657         struct sockaddr_in their_addr;  // connector's address information
658         struct mail_s *mail = (struct mail_s *)arg;
659
660 #ifdef HAVE_GETHOSTBYNAME_R
661         if (gethostbyname_r(mail->host, &he, hostbuff, sizeof(hostbuff), &he_res, &he_errno)) { // get the host info
662                 ERR("POP3 gethostbyname_r: %s", hstrerror(h_errno));
663                 exit(1);
664         }
665 #else /* HAVE_GETHOSTBYNAME_R */
666         if ((he_res = gethostbyname(mail->host)) == NULL) {     // get the host info
667                 herror("gethostbyname");
668                 exit(1);
669         }
670 #endif /* HAVE_GETHOSTBYNAME_R */
671         while (fail < mail->retries) {
672                 struct timeval timeout;
673                 int res;
674                 fd_set fdset;
675
676                 if (fail > 0) {
677                         ERR("Trying POP3 connection again for %s@%s (try %u/%u)",
678                                         mail->user, mail->host, fail + 1, mail->retries);
679                 }
680                 do {
681                         if ((sockfd = socket(PF_INET, SOCK_STREAM, 0)) == -1) {
682                                 perror("socket");
683                                 fail++;
684                                 break;
685                         }
686
687                         // host byte order
688                         their_addr.sin_family = AF_INET;
689                         // short, network byte order
690                         their_addr.sin_port = htons(mail->port);
691                         their_addr.sin_addr = *((struct in_addr *) he_res->h_addr);
692                         // zero the rest of the struct
693                         memset(&(their_addr.sin_zero), '\0', 8);
694
695                         if (connect(sockfd, (struct sockaddr *) &their_addr,
696                                                 sizeof(struct sockaddr)) == -1) {
697                                 perror("connect");
698                                 fail++;
699                                 break;
700                         }
701
702                         timeout.tv_sec = 60;    // 60 second timeout i guess
703                         timeout.tv_usec = 0;
704                         FD_ZERO(&fdset);
705                         FD_SET(sockfd, &fdset);
706                         res = select(sockfd + 1, &fdset, NULL, NULL, &timeout);
707                         if (res > 0) {
708                                 if ((numbytes = recv(sockfd, recvbuf, MAXDATASIZE - 1, 0)) == -1) {
709                                         perror("recv");
710                                         fail++;
711                                         break;
712                                 }
713                         } else {
714                                 ERR("POP3 connection failed: timeout\n");
715                                 fail++;
716                                 break;
717                         }
718                         DBGP2("pop3_thread received: %s", recvbuf);
719                         recvbuf[numbytes] = '\0';
720                         if (strstr(recvbuf, "+OK ") != recvbuf) {
721                                 ERR("POP3 connection failed, probably not a POP3 server");
722                                 fail++;
723                                 break;
724                         }
725                         strncpy(sendbuf, "USER ", MAXDATASIZE);
726                         strncat(sendbuf, mail->user, MAXDATASIZE - strlen(sendbuf) - 1);
727                         strncat(sendbuf, "\r\n", MAXDATASIZE - strlen(sendbuf) - 1);
728                         if (pop3_command(sockfd, sendbuf, recvbuf, "+OK ")) {
729                                 fail++;
730                                 break;
731                         }
732
733                         strncpy(sendbuf, "PASS ", MAXDATASIZE);
734                         strncat(sendbuf, mail->pass, MAXDATASIZE - strlen(sendbuf) - 1);
735                         strncat(sendbuf, "\r\n", MAXDATASIZE - strlen(sendbuf) - 1);
736                         if (pop3_command(sockfd, sendbuf, recvbuf, "+OK ")) {
737                                 ERR("POP3 server login failed: %s", recvbuf);
738                                 fail++;
739                                 break;
740                         }
741
742                         strncpy(sendbuf, "STAT\r\n", MAXDATASIZE);
743                         if (pop3_command(sockfd, sendbuf, recvbuf, "+OK ")) {
744                                 perror("send STAT");
745                                 fail++;
746                                 break;
747                         }
748
749                         // now we get the data
750                         reply = recvbuf + 4;
751                         if (reply == NULL) {
752                                 ERR("Error parsing POP3 response: %s", recvbuf);
753                                 fail++;
754                                 break;
755                         } else {
756                                 timed_thread_lock(mail->p_timed_thread);
757                                 sscanf(reply, "%lu %lu", &mail->unseen, &mail->used);
758                                 timed_thread_unlock(mail->p_timed_thread);
759                         }
760                         
761                         strncpy(sendbuf, "QUIT\r\n", MAXDATASIZE);
762                         if (pop3_command(sockfd, sendbuf, recvbuf, "+OK")) {
763                                 ERR("POP3 logout failed: %s", recvbuf);
764                                 fail++;
765                                 break;
766                         }
767                         
768                         if (strlen(mail->command) > 1 && mail->unseen > old_unseen) {
769                                 // new mail goodie
770                                 if (system(mail->command) == -1) {
771                                         perror("system()");
772                                 }
773                         }
774                         fail = 0;
775                         old_unseen = mail->unseen;
776                 } while (0);
777                 if ((fstat(sockfd, &stat_buf) == 0) && S_ISSOCK(stat_buf.st_mode)) {
778                         /* if a valid socket, close it */
779                         close(sockfd);
780                 }
781                 if (timed_thread_test(mail->p_timed_thread, 0)) {
782                         timed_thread_exit(mail->p_timed_thread);
783                 }
784         }
785         mail->unseen = 0;
786         mail->used = 0;
787         return 0;
788 }
789