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