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