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