#include <time.h>
#include <locale.h>
#include <signal.h>
-#include <unistd.h>
#include <errno.h>
-#include <termios.h>
#include <limits.h>
#if HAVE_DIRENT_H
#include <dirent.h>
struct text_object_list *text_object_list,
struct information *cur);
-#define MAXDATASIZE 1000
-#define POP3 1
-#define IMAP 2
-
-struct mail_s *parse_mail_args(char type, const char *arg)
-{
- struct mail_s *mail;
- char *tmp;
-
- mail = malloc(sizeof(struct mail_s));
- memset(mail, 0, sizeof(struct mail_s));
-
- if (sscanf(arg, "%128s %128s %128s", mail->host, mail->user, mail->pass)
- != 3) {
- if (type == POP3) {
- ERR("Scanning IMAP args failed");
- } else if (type == IMAP) {
- ERR("Scanning POP3 args failed");
- }
- }
- // see if password needs prompting
- if (mail->pass[0] == '*' && mail->pass[1] == '\0') {
- int fp = fileno(stdin);
- struct termios term;
-
- tcgetattr(fp, &term);
- term.c_lflag &= ~ECHO;
- tcsetattr(fp, TCSANOW, &term);
- printf("Enter mailbox password (%s@%s): ", mail->user, mail->host);
- scanf("%128s", mail->pass);
- printf("\n");
- term.c_lflag |= ECHO;
- tcsetattr(fp, TCSANOW, &term);
- }
- // now we check for optional args
- tmp = strstr(arg, "-r ");
- if (tmp) {
- tmp += 3;
- sscanf(tmp, "%u", &mail->retries);
- } else {
- mail->retries = 5; // 5 retries after failure
- }
- tmp = strstr(arg, "-i ");
- if (tmp) {
- tmp += 3;
- sscanf(tmp, "%f", &mail->interval);
- } else {
- mail->interval = 300; // 5 minutes
- }
- tmp = strstr(arg, "-p ");
- if (tmp) {
- tmp += 3;
- sscanf(tmp, "%lu", &mail->port);
- } else {
- if (type == POP3) {
- mail->port = 110; // default pop3 port
- } else if (type == IMAP) {
- mail->port = 143; // default imap port
- }
- }
- if (type == IMAP) {
- tmp = strstr(arg, "-f ");
- if (tmp) {
- tmp += 3;
- sscanf(tmp, "%s", mail->folder);
- } else {
- strncpy(mail->folder, "INBOX", 128); // default imap inbox
- }
- }
- tmp = strstr(arg, "-e ");
- if (tmp) {
- int len = 1024;
- tmp += 3;
-
- if (tmp[0] == '\'') {
- len = strstr(tmp + 1, "'") - tmp - 1;
- if (len > 1024) {
- len = 1024;
- }
- }
- strncpy(mail->command, tmp + 1, len);
- } else {
- mail->command[0] = '\0';
- }
- mail->p_timed_thread = NULL;
- return mail;
-}
-
-int imap_command(int sockfd, const char *command, char *response, const char *verify)
-{
- struct timeval timeout;
- fd_set fdset;
- int res, numbytes = 0;
- if (send(sockfd, command, strlen(command), 0) == -1) {
- perror("send");
- return -1;
- }
- timeout.tv_sec = 60; // 60 second timeout i guess
- timeout.tv_usec = 0;
- FD_ZERO(&fdset);
- FD_SET(sockfd, &fdset);
- res = select(sockfd + 1, &fdset, NULL, NULL, &timeout);
- if (res > 0) {
- if ((numbytes = recv(sockfd, response, MAXDATASIZE - 1, 0)) == -1) {
- perror("recv");
- return -1;
- }
- }
- DBGP2("imap_command() received: %s", response);
- response[numbytes] = '\0';
- if (strstr(response, verify) == NULL) {
- return -1;
- }
- return 0;
-}
-
-int imap_check_status(char *recvbuf, struct mail_s *mail)
-{
- char *reply;
- reply = strstr(recvbuf, " (MESSAGES ");
- if (!reply || strlen(reply) < 2) {
- return -1;
- }
- reply += 2;
- *strchr(reply, ')') = '\0';
- if (reply == NULL) {
- ERR("Error parsing IMAP response: %s", recvbuf);
- return -1;
- } else {
- timed_thread_lock(mail->p_timed_thread);
- sscanf(reply, "MESSAGES %lu UNSEEN %lu", &mail->messages,
- &mail->unseen);
- timed_thread_unlock(mail->p_timed_thread);
- }
- return 0;
-}
-
-void imap_unseen_command(struct mail_s *mail, unsigned long old_unseen, unsigned long old_messages)
-{
- if (strlen(mail->command) > 1 && (mail->unseen > old_unseen
- || (mail->messages > old_messages && mail->unseen > 0))) {
- // new mail goodie
- if (system(mail->command) == -1) {
- perror("system()");
- }
- }
-}
-
-void *imap_thread(void *arg)
-{
- int sockfd, numbytes;
- char recvbuf[MAXDATASIZE];
- char sendbuf[MAXDATASIZE];
- unsigned int fail = 0;
- unsigned long old_unseen = ULONG_MAX;
- unsigned long old_messages = ULONG_MAX;
- struct stat stat_buf;
- struct hostent he, *he_res = 0;
- int he_errno;
- char hostbuff[2048];
- struct sockaddr_in their_addr; // connector's address information
- struct mail_s *mail = (struct mail_s *)arg;
- int has_idle = 0;
- int threadfd = timed_thread_readfd(mail->p_timed_thread);
-
-#ifdef HAVE_GETHOSTBYNAME_R
- if (gethostbyname_r(mail->host, &he, hostbuff, sizeof(hostbuff), &he_res, &he_errno)) { // get the host info
- ERR("IMAP gethostbyname_r: %s", hstrerror(h_errno));
- exit(1);
- }
-#else /* HAVE_GETHOSTBYNAME_R */
- if ((he_res = gethostbyname(mail->host)) == NULL) { // get the host info
- herror("gethostbyname");
- exit(1);
- }
-#endif /* HAVE_GETHOSTBYNAME_R */
- while (fail < mail->retries) {
- struct timeval timeout;
- int res;
- fd_set fdset;
-
- if (fail > 0) {
- ERR("Trying IMAP connection again for %s@%s (try %u/%u)",
- mail->user, mail->host, fail + 1, mail->retries);
- }
- do {
- if ((sockfd = socket(PF_INET, SOCK_STREAM, 0)) == -1) {
- perror("socket");
- fail++;
- break;
- }
-
- // host byte order
- their_addr.sin_family = AF_INET;
- // short, network byte order
- their_addr.sin_port = htons(mail->port);
- their_addr.sin_addr = *((struct in_addr *) he_res->h_addr);
- // zero the rest of the struct
- memset(&(their_addr.sin_zero), '\0', 8);
-
- if (connect(sockfd, (struct sockaddr *) &their_addr,
- sizeof(struct sockaddr)) == -1) {
- perror("connect");
- fail++;
- break;
- }
-
- timeout.tv_sec = 60; // 60 second timeout i guess
- timeout.tv_usec = 0;
- FD_ZERO(&fdset);
- FD_SET(sockfd, &fdset);
- res = select(sockfd + 1, &fdset, NULL, NULL, &timeout);
- if (res > 0) {
- if ((numbytes = recv(sockfd, recvbuf, MAXDATASIZE - 1, 0)) == -1) {
- perror("recv");
- fail++;
- break;
- }
- } else {
- ERR("IMAP connection failed: timeout");
- fail++;
- break;
- }
- recvbuf[numbytes] = '\0';
- DBGP2("imap_thread() received: %s", recvbuf);
- if (strstr(recvbuf, "* OK") != recvbuf) {
- ERR("IMAP connection failed, probably not an IMAP server");
- fail++;
- break;
- }
- strncpy(sendbuf, "a1 login ", MAXDATASIZE);
- strncat(sendbuf, mail->user, MAXDATASIZE - strlen(sendbuf) - 1);
- strncat(sendbuf, " ", MAXDATASIZE - strlen(sendbuf) - 1);
- strncat(sendbuf, mail->pass, MAXDATASIZE - strlen(sendbuf) - 1);
- strncat(sendbuf, "\r\n", MAXDATASIZE - strlen(sendbuf) - 1);
- if (imap_command(sockfd, sendbuf, recvbuf, "a1 OK")) {
- fail++;
- break;
- }
- if (strstr(recvbuf, " IDLE ") != NULL) {
- has_idle = 1;
- }
-
- strncpy(sendbuf, "a2 STATUS ", MAXDATASIZE);
- strncat(sendbuf, mail->folder, MAXDATASIZE - strlen(sendbuf) - 1);
- strncat(sendbuf, " (MESSAGES UNSEEN)\r\n",
- MAXDATASIZE - strlen(sendbuf) - 1);
- if (imap_command(sockfd, sendbuf, recvbuf, "a2 OK")) {
- fail++;
- break;
- }
-
- if (imap_check_status(recvbuf, mail)) {
- fail++;
- break;
- }
- imap_unseen_command(mail, old_unseen, old_messages);
- fail = 0;
- old_unseen = mail->unseen;
- old_messages = mail->messages;
-
- if (has_idle) {
- strncpy(sendbuf, "a4 SELECT ", MAXDATASIZE);
- strncat(sendbuf, mail->folder, MAXDATASIZE - strlen(sendbuf) - 1);
- strncat(sendbuf, "\r\n", MAXDATASIZE - strlen(sendbuf) - 1);
- if (imap_command(sockfd, sendbuf, recvbuf, "a4 OK")) {
- fail++;
- break;
- }
-
- strncpy(sendbuf, "a5 IDLE\r\n", MAXDATASIZE);
- if (imap_command(sockfd, sendbuf, recvbuf, "+ idling")) {
- fail++;
- break;
- }
- recvbuf[0] = '\0';
-
- while (1) {
- /*
- * RFC 2177 says we have to re-idle every 29 minutes.
- * We'll do it every 20 minutes to be safe.
- */
- timeout.tv_sec = 1200;
- timeout.tv_usec = 0;
- FD_ZERO(&fdset);
- FD_SET(sockfd, &fdset);
- FD_SET(threadfd, &fdset);
- res = select(MAX(sockfd + 1, threadfd + 1), &fdset, NULL, NULL, NULL);
- if (timed_thread_test(mail->p_timed_thread, 1) || (res == -1 && errno == EINTR) || FD_ISSET(threadfd, &fdset)) {
- if ((fstat(sockfd, &stat_buf) == 0) && S_ISSOCK(stat_buf.st_mode)) {
- /* if a valid socket, close it */
- close(sockfd);
- }
- timed_thread_exit(mail->p_timed_thread);
- } else if (res > 0) {
- if ((numbytes = recv(sockfd, recvbuf, MAXDATASIZE - 1, 0)) == -1) {
- perror("recv idling");
- fail++;
- break;
- }
- } else {
- break;
- }
- recvbuf[numbytes] = '\0';
- DBGP2("imap_thread() received: %s", recvbuf);
- if (strlen(recvbuf) > 2) {
- unsigned long messages, recent;
- char *buf = recvbuf;
- buf = strstr(buf, "EXISTS");
- while (buf && strlen(buf) > 1 && strstr(buf + 1, "EXISTS")) {
- buf = strstr(buf + 1, "EXISTS");
- }
- if (buf) {
- // back up until we reach '*'
- while (buf >= recvbuf && buf[0] != '*') {
- buf--;
- }
- if (sscanf(buf, "* %lu EXISTS\r\n", &messages) == 1) {
- timed_thread_lock(mail->p_timed_thread);
- mail->messages = messages;
- timed_thread_unlock(mail->p_timed_thread);
- }
- }
- buf = recvbuf;
- buf = strstr(buf, "RECENT");
- while (buf && strlen(buf) > 1 && strstr(buf + 1, "RECENT")) {
- buf = strstr(buf + 1, "RECENT");
- }
- if (buf) {
- // back up until we reach '*'
- while (buf >= recvbuf && buf[0] != '*') {
- buf--;
- }
- if (sscanf(buf, "* %lu RECENT\r\n", &recent) != 1) {
- recent = 0;
- }
- }
- /*
- * check if we got a FETCH from server, recent was
- * something other than 0, or we had a timeout
- */
- buf = recvbuf;
- if (recent > 0 || (buf && strstr(buf, " FETCH ")) || timeout.tv_sec == 0) {
- // re-check messages and unseen
- if (imap_command(sockfd, "DONE\r\n", recvbuf, "a5 OK")) {
- fail++;
- break;
- }
- strncpy(sendbuf, "a2 STATUS ", MAXDATASIZE);
- strncat(sendbuf, mail->folder, MAXDATASIZE - strlen(sendbuf) - 1);
- strncat(sendbuf, " (MESSAGES UNSEEN)\r\n",
- MAXDATASIZE - strlen(sendbuf) - 1);
- if (imap_command(sockfd, sendbuf, recvbuf, "a2 OK")) {
- fail++;
- break;
- }
- if (imap_check_status(recvbuf, mail)) {
- fail++;
- break;
- }
- strncpy(sendbuf, "a5 IDLE\r\n", MAXDATASIZE);
- if (imap_command(sockfd, sendbuf, recvbuf, "+ idling")) {
- fail++;
- break;
- }
- }
- /*
- * check if we got a BYE from server
- */
- buf = recvbuf;
- if (buf && strstr(buf, "* BYE")) {
- // need to re-connect
- break;
- }
- }
- imap_unseen_command(mail, old_unseen, old_messages);
- fail = 0;
- old_unseen = mail->unseen;
- old_messages = mail->messages;
- }
- } else {
- strncpy(sendbuf, "a3 logout\r\n", MAXDATASIZE);
- if (send(sockfd, sendbuf, strlen(sendbuf), 0) == -1) {
- perror("send a3");
- fail++;
- break;
- }
- timeout.tv_sec = 60; // 60 second timeout i guess
- timeout.tv_usec = 0;
- FD_ZERO(&fdset);
- FD_SET(sockfd, &fdset);
- res = select(sockfd + 1, &fdset, NULL, NULL, &timeout);
- if (res > 0) {
- if ((numbytes = recv(sockfd, recvbuf, MAXDATASIZE - 1, 0)) == -1) {
- perror("recv a3");
- fail++;
- break;
- }
- }
- recvbuf[numbytes] = '\0';
- DBGP2("imap_thread() received: %s", recvbuf);
- if (strstr(recvbuf, "a3 OK") == NULL) {
- ERR("IMAP logout failed: %s", recvbuf);
- fail++;
- break;
- }
- }
- } while (0);
- if ((fstat(sockfd, &stat_buf) == 0) && S_ISSOCK(stat_buf.st_mode)) {
- /* if a valid socket, close it */
- close(sockfd);
- }
- if (timed_thread_test(mail->p_timed_thread, 0)) {
- timed_thread_exit(mail->p_timed_thread);
- }
- }
- mail->unseen = 0;
- mail->messages = 0;
- return 0;
-}
-
-int pop3_command(int sockfd, const char *command, char *response, const char *verify)
-{
- struct timeval timeout;
- fd_set fdset;
- int res, numbytes = 0;
- if (send(sockfd, command, strlen(command), 0) == -1) {
- perror("send");
- return -1;
- }
- timeout.tv_sec = 60; // 60 second timeout i guess
- timeout.tv_usec = 0;
- FD_ZERO(&fdset);
- FD_SET(sockfd, &fdset);
- res = select(sockfd + 1, &fdset, NULL, NULL, &timeout);
- if (res > 0) {
- if ((numbytes = recv(sockfd, response, MAXDATASIZE - 1, 0)) == -1) {
- perror("recv");
- return -1;
- }
- }
- DBGP2("pop3_command() received: %s", response);
- response[numbytes] = '\0';
- if (strstr(response, verify) == NULL) {
- return -1;
- }
- return 0;
-}
-
-void *pop3_thread(void *arg)
-{
- int sockfd, numbytes;
- char recvbuf[MAXDATASIZE];
- char sendbuf[MAXDATASIZE];
- char *reply;
- unsigned int fail = 0;
- unsigned long old_unseen = ULONG_MAX;
- struct stat stat_buf;
- struct hostent he, *he_res = 0;
- int he_errno;
- char hostbuff[2048];
- struct sockaddr_in their_addr; // connector's address information
- struct mail_s *mail = (struct mail_s *)arg;
-
-#ifdef HAVE_GETHOSTBYNAME_R
- if (gethostbyname_r(mail->host, &he, hostbuff, sizeof(hostbuff), &he_res, &he_errno)) { // get the host info
- ERR("POP3 gethostbyname_r: %s", hstrerror(h_errno));
- exit(1);
- }
-#else /* HAVE_GETHOSTBYNAME_R */
- if ((he_res = gethostbyname(mail->host)) == NULL) { // get the host info
- herror("gethostbyname");
- exit(1);
- }
-#endif /* HAVE_GETHOSTBYNAME_R */
- while (fail < mail->retries) {
- struct timeval timeout;
- int res;
- fd_set fdset;
-
- if (fail > 0) {
- ERR("Trying POP3 connection again for %s@%s (try %u/%u)",
- mail->user, mail->host, fail + 1, mail->retries);
- }
- do {
- if ((sockfd = socket(PF_INET, SOCK_STREAM, 0)) == -1) {
- perror("socket");
- fail++;
- break;
- }
-
- // host byte order
- their_addr.sin_family = AF_INET;
- // short, network byte order
- their_addr.sin_port = htons(mail->port);
- their_addr.sin_addr = *((struct in_addr *) he_res->h_addr);
- // zero the rest of the struct
- memset(&(their_addr.sin_zero), '\0', 8);
-
- if (connect(sockfd, (struct sockaddr *) &their_addr,
- sizeof(struct sockaddr)) == -1) {
- perror("connect");
- fail++;
- break;
- }
-
- timeout.tv_sec = 60; // 60 second timeout i guess
- timeout.tv_usec = 0;
- FD_ZERO(&fdset);
- FD_SET(sockfd, &fdset);
- res = select(sockfd + 1, &fdset, NULL, NULL, &timeout);
- if (res > 0) {
- if ((numbytes = recv(sockfd, recvbuf, MAXDATASIZE - 1, 0)) == -1) {
- perror("recv");
- fail++;
- break;
- }
- } else {
- ERR("POP3 connection failed: timeout\n");
- fail++;
- break;
- }
- DBGP2("pop3_thread received: %s", recvbuf);
- recvbuf[numbytes] = '\0';
- if (strstr(recvbuf, "+OK ") != recvbuf) {
- ERR("POP3 connection failed, probably not a POP3 server");
- fail++;
- break;
- }
- strncpy(sendbuf, "USER ", MAXDATASIZE);
- strncat(sendbuf, mail->user, MAXDATASIZE - strlen(sendbuf) - 1);
- strncat(sendbuf, "\r\n", MAXDATASIZE - strlen(sendbuf) - 1);
- if (pop3_command(sockfd, sendbuf, recvbuf, "+OK ")) {
- fail++;
- break;
- }
-
- strncpy(sendbuf, "PASS ", MAXDATASIZE);
- strncat(sendbuf, mail->pass, MAXDATASIZE - strlen(sendbuf) - 1);
- strncat(sendbuf, "\r\n", MAXDATASIZE - strlen(sendbuf) - 1);
- if (pop3_command(sockfd, sendbuf, recvbuf, "+OK ")) {
- ERR("POP3 server login failed: %s", recvbuf);
- fail++;
- break;
- }
-
- strncpy(sendbuf, "STAT\r\n", MAXDATASIZE);
- if (pop3_command(sockfd, sendbuf, recvbuf, "+OK ")) {
- perror("send STAT");
- fail++;
- break;
- }
-
- // now we get the data
- reply = recvbuf + 4;
- if (reply == NULL) {
- ERR("Error parsing POP3 response: %s", recvbuf);
- fail++;
- break;
- } else {
- timed_thread_lock(mail->p_timed_thread);
- sscanf(reply, "%lu %lu", &mail->unseen, &mail->used);
- timed_thread_unlock(mail->p_timed_thread);
- }
-
- strncpy(sendbuf, "QUIT\r\n", MAXDATASIZE);
- if (pop3_command(sockfd, sendbuf, recvbuf, "+OK")) {
- ERR("POP3 logout failed: %s", recvbuf);
- fail++;
- break;
- }
-
- if (strlen(mail->command) > 1 && mail->unseen > old_unseen) {
- // new mail goodie
- if (system(mail->command) == -1) {
- perror("system()");
- }
- }
- fail = 0;
- old_unseen = mail->unseen;
- } while (0);
- if ((fstat(sockfd, &stat_buf) == 0) && S_ISSOCK(stat_buf.st_mode)) {
- /* if a valid socket, close it */
- close(sockfd);
- }
- if (timed_thread_test(mail->p_timed_thread, 0)) {
- timed_thread_exit(mail->p_timed_thread);
- }
- }
- mail->unseen = 0;
- mail->used = 0;
- return 0;
-}
-
static inline void read_exec(const char *data, char *buf, const int size)
{
FILE *fp = popen(data, "r");
END OBJ_THREAD(imap_unseen, 0)
if (arg) {
// proccss
- obj->data.mail = parse_mail_args(IMAP, arg);
+ obj->data.mail = parse_mail_args(IMAP_TYPE, arg);
obj->global_mode = 0;
} else {
obj->global_mode = 1;
END OBJ_THREAD(imap_messages, 0)
if (arg) {
// proccss
- obj->data.mail = parse_mail_args(IMAP, arg);
+ obj->data.mail = parse_mail_args(IMAP_TYPE, arg);
obj->global_mode = 0;
} else {
obj->global_mode = 1;
END OBJ_THREAD(pop3_unseen, 0)
if (arg) {
// proccss
- obj->data.mail = parse_mail_args(POP3, arg);
+ obj->data.mail = parse_mail_args(POP3_TYPE, arg);
obj->global_mode = 0;
} else {
obj->global_mode = 1;
END OBJ_THREAD(pop3_used, 0)
if (arg) {
// proccss
- obj->data.mail = parse_mail_args(POP3, arg);
+ obj->data.mail = parse_mail_args(POP3_TYPE, arg);
obj->global_mode = 0;
} else {
obj->global_mode = 1;
#endif /* X11 */
CONF("imap") {
if (value) {
- info.mail = parse_mail_args(IMAP, value);
+ info.mail = parse_mail_args(IMAP_TYPE, value);
} else {
CONF_ERR;
}
}
CONF("pop3") {
if (value) {
- info.mail = parse_mail_args(POP3, value);
+ info.mail = parse_mail_args(POP3_TYPE, value);
} else {
CONF_ERR;
}
*
*/
+#include "conky.h"
+
#include <sys/stat.h>
#include <sys/time.h>
#include <dirent.h>
#include <errno.h>
-
-#include "conky.h"
+#include <termios.h>
char *current_mail_spool;
mail->last_mtime = st.st_mtime;
}
}
+
+#define MAXDATASIZE 1000
+
+struct mail_s *parse_mail_args(char type, const char *arg)
+{
+ struct mail_s *mail;
+ char *tmp;
+
+ mail = malloc(sizeof(struct mail_s));
+ memset(mail, 0, sizeof(struct mail_s));
+
+ if (sscanf(arg, "%128s %128s %128s", mail->host, mail->user, mail->pass)
+ != 3) {
+ if (type == POP3_TYPE) {
+ ERR("Scanning IMAP args failed");
+ } else if (type == IMAP_TYPE) {
+ ERR("Scanning POP3 args failed");
+ }
+ }
+ // see if password needs prompting
+ if (mail->pass[0] == '*' && mail->pass[1] == '\0') {
+ int fp = fileno(stdin);
+ struct termios term;
+
+ tcgetattr(fp, &term);
+ term.c_lflag &= ~ECHO;
+ tcsetattr(fp, TCSANOW, &term);
+ printf("Enter mailbox password (%s@%s): ", mail->user, mail->host);
+ scanf("%128s", mail->pass);
+ printf("\n");
+ term.c_lflag |= ECHO;
+ tcsetattr(fp, TCSANOW, &term);
+ }
+ // now we check for optional args
+ tmp = strstr(arg, "-r ");
+ if (tmp) {
+ tmp += 3;
+ sscanf(tmp, "%u", &mail->retries);
+ } else {
+ mail->retries = 5; // 5 retries after failure
+ }
+ tmp = strstr(arg, "-i ");
+ if (tmp) {
+ tmp += 3;
+ sscanf(tmp, "%f", &mail->interval);
+ } else {
+ mail->interval = 300; // 5 minutes
+ }
+ tmp = strstr(arg, "-p ");
+ if (tmp) {
+ tmp += 3;
+ sscanf(tmp, "%lu", &mail->port);
+ } else {
+ if (type == POP3_TYPE) {
+ mail->port = 110; // default pop3 port
+ } else if (type == IMAP_TYPE) {
+ mail->port = 143; // default imap port
+ }
+ }
+ if (type == IMAP_TYPE) {
+ tmp = strstr(arg, "-f ");
+ if (tmp) {
+ tmp += 3;
+ sscanf(tmp, "%s", mail->folder);
+ } else {
+ strncpy(mail->folder, "INBOX", 128); // default imap inbox
+ }
+ }
+ tmp = strstr(arg, "-e ");
+ if (tmp) {
+ int len = 1024;
+ tmp += 3;
+
+ if (tmp[0] == '\'') {
+ len = strstr(tmp + 1, "'") - tmp - 1;
+ if (len > 1024) {
+ len = 1024;
+ }
+ }
+ strncpy(mail->command, tmp + 1, len);
+ } else {
+ mail->command[0] = '\0';
+ }
+ mail->p_timed_thread = NULL;
+ return mail;
+}
+
+int imap_command(int sockfd, const char *command, char *response, const char *verify)
+{
+ struct timeval timeout;
+ fd_set fdset;
+ int res, numbytes = 0;
+ if (send(sockfd, command, strlen(command), 0) == -1) {
+ perror("send");
+ return -1;
+ }
+ timeout.tv_sec = 60; // 60 second timeout i guess
+ timeout.tv_usec = 0;
+ FD_ZERO(&fdset);
+ FD_SET(sockfd, &fdset);
+ res = select(sockfd + 1, &fdset, NULL, NULL, &timeout);
+ if (res > 0) {
+ if ((numbytes = recv(sockfd, response, MAXDATASIZE - 1, 0)) == -1) {
+ perror("recv");
+ return -1;
+ }
+ }
+ DBGP2("imap_command() received: %s", response);
+ response[numbytes] = '\0';
+ if (strstr(response, verify) == NULL) {
+ return -1;
+ }
+ return 0;
+}
+
+int imap_check_status(char *recvbuf, struct mail_s *mail)
+{
+ char *reply;
+ reply = strstr(recvbuf, " (MESSAGES ");
+ if (!reply || strlen(reply) < 2) {
+ return -1;
+ }
+ reply += 2;
+ *strchr(reply, ')') = '\0';
+ if (reply == NULL) {
+ ERR("Error parsing IMAP response: %s", recvbuf);
+ return -1;
+ } else {
+ timed_thread_lock(mail->p_timed_thread);
+ sscanf(reply, "MESSAGES %lu UNSEEN %lu", &mail->messages,
+ &mail->unseen);
+ timed_thread_unlock(mail->p_timed_thread);
+ }
+ return 0;
+}
+
+void imap_unseen_command(struct mail_s *mail, unsigned long old_unseen, unsigned long old_messages)
+{
+ if (strlen(mail->command) > 1 && (mail->unseen > old_unseen
+ || (mail->messages > old_messages && mail->unseen > 0))) {
+ // new mail goodie
+ if (system(mail->command) == -1) {
+ perror("system()");
+ }
+ }
+}
+
+void *imap_thread(void *arg)
+{
+ int sockfd, numbytes;
+ char recvbuf[MAXDATASIZE];
+ char sendbuf[MAXDATASIZE];
+ unsigned int fail = 0;
+ unsigned long old_unseen = ULONG_MAX;
+ unsigned long old_messages = ULONG_MAX;
+ struct stat stat_buf;
+ struct hostent he, *he_res = 0;
+ int he_errno;
+ char hostbuff[2048];
+ struct sockaddr_in their_addr; // connector's address information
+ struct mail_s *mail = (struct mail_s *)arg;
+ int has_idle = 0;
+ int threadfd = timed_thread_readfd(mail->p_timed_thread);
+
+#ifdef HAVE_GETHOSTBYNAME_R
+ if (gethostbyname_r(mail->host, &he, hostbuff, sizeof(hostbuff), &he_res, &he_errno)) { // get the host info
+ ERR("IMAP gethostbyname_r: %s", hstrerror(h_errno));
+ exit(1);
+ }
+#else /* HAVE_GETHOSTBYNAME_R */
+ if ((he_res = gethostbyname(mail->host)) == NULL) { // get the host info
+ herror("gethostbyname");
+ exit(1);
+ }
+#endif /* HAVE_GETHOSTBYNAME_R */
+ while (fail < mail->retries) {
+ struct timeval timeout;
+ int res;
+ fd_set fdset;
+
+ if (fail > 0) {
+ ERR("Trying IMAP connection again for %s@%s (try %u/%u)",
+ mail->user, mail->host, fail + 1, mail->retries);
+ }
+ do {
+ if ((sockfd = socket(PF_INET, SOCK_STREAM, 0)) == -1) {
+ perror("socket");
+ fail++;
+ break;
+ }
+
+ // host byte order
+ their_addr.sin_family = AF_INET;
+ // short, network byte order
+ their_addr.sin_port = htons(mail->port);
+ their_addr.sin_addr = *((struct in_addr *) he_res->h_addr);
+ // zero the rest of the struct
+ memset(&(their_addr.sin_zero), '\0', 8);
+
+ if (connect(sockfd, (struct sockaddr *) &their_addr,
+ sizeof(struct sockaddr)) == -1) {
+ perror("connect");
+ fail++;
+ break;
+ }
+
+ timeout.tv_sec = 60; // 60 second timeout i guess
+ timeout.tv_usec = 0;
+ FD_ZERO(&fdset);
+ FD_SET(sockfd, &fdset);
+ res = select(sockfd + 1, &fdset, NULL, NULL, &timeout);
+ if (res > 0) {
+ if ((numbytes = recv(sockfd, recvbuf, MAXDATASIZE - 1, 0)) == -1) {
+ perror("recv");
+ fail++;
+ break;
+ }
+ } else {
+ ERR("IMAP connection failed: timeout");
+ fail++;
+ break;
+ }
+ recvbuf[numbytes] = '\0';
+ DBGP2("imap_thread() received: %s", recvbuf);
+ if (strstr(recvbuf, "* OK") != recvbuf) {
+ ERR("IMAP connection failed, probably not an IMAP server");
+ fail++;
+ break;
+ }
+ strncpy(sendbuf, "a1 login ", MAXDATASIZE);
+ strncat(sendbuf, mail->user, MAXDATASIZE - strlen(sendbuf) - 1);
+ strncat(sendbuf, " ", MAXDATASIZE - strlen(sendbuf) - 1);
+ strncat(sendbuf, mail->pass, MAXDATASIZE - strlen(sendbuf) - 1);
+ strncat(sendbuf, "\r\n", MAXDATASIZE - strlen(sendbuf) - 1);
+ if (imap_command(sockfd, sendbuf, recvbuf, "a1 OK")) {
+ fail++;
+ break;
+ }
+ if (strstr(recvbuf, " IDLE ") != NULL) {
+ has_idle = 1;
+ }
+
+ strncpy(sendbuf, "a2 STATUS ", MAXDATASIZE);
+ strncat(sendbuf, mail->folder, MAXDATASIZE - strlen(sendbuf) - 1);
+ strncat(sendbuf, " (MESSAGES UNSEEN)\r\n",
+ MAXDATASIZE - strlen(sendbuf) - 1);
+ if (imap_command(sockfd, sendbuf, recvbuf, "a2 OK")) {
+ fail++;
+ break;
+ }
+
+ if (imap_check_status(recvbuf, mail)) {
+ fail++;
+ break;
+ }
+ imap_unseen_command(mail, old_unseen, old_messages);
+ fail = 0;
+ old_unseen = mail->unseen;
+ old_messages = mail->messages;
+
+ if (has_idle) {
+ strncpy(sendbuf, "a4 SELECT ", MAXDATASIZE);
+ strncat(sendbuf, mail->folder, MAXDATASIZE - strlen(sendbuf) - 1);
+ strncat(sendbuf, "\r\n", MAXDATASIZE - strlen(sendbuf) - 1);
+ if (imap_command(sockfd, sendbuf, recvbuf, "a4 OK")) {
+ fail++;
+ break;
+ }
+
+ strncpy(sendbuf, "a5 IDLE\r\n", MAXDATASIZE);
+ if (imap_command(sockfd, sendbuf, recvbuf, "+ idling")) {
+ fail++;
+ break;
+ }
+ recvbuf[0] = '\0';
+
+ while (1) {
+ /*
+ * RFC 2177 says we have to re-idle every 29 minutes.
+ * We'll do it every 20 minutes to be safe.
+ */
+ timeout.tv_sec = 1200;
+ timeout.tv_usec = 0;
+ FD_ZERO(&fdset);
+ FD_SET(sockfd, &fdset);
+ FD_SET(threadfd, &fdset);
+ res = select(MAX(sockfd + 1, threadfd + 1), &fdset, NULL, NULL, NULL);
+ if (timed_thread_test(mail->p_timed_thread, 1) || (res == -1 && errno == EINTR) || FD_ISSET(threadfd, &fdset)) {
+ if ((fstat(sockfd, &stat_buf) == 0) && S_ISSOCK(stat_buf.st_mode)) {
+ /* if a valid socket, close it */
+ close(sockfd);
+ }
+ timed_thread_exit(mail->p_timed_thread);
+ } else if (res > 0) {
+ if ((numbytes = recv(sockfd, recvbuf, MAXDATASIZE - 1, 0)) == -1) {
+ perror("recv idling");
+ fail++;
+ break;
+ }
+ } else {
+ break;
+ }
+ recvbuf[numbytes] = '\0';
+ DBGP2("imap_thread() received: %s", recvbuf);
+ if (strlen(recvbuf) > 2) {
+ unsigned long messages, recent;
+ char *buf = recvbuf;
+ buf = strstr(buf, "EXISTS");
+ while (buf && strlen(buf) > 1 && strstr(buf + 1, "EXISTS")) {
+ buf = strstr(buf + 1, "EXISTS");
+ }
+ if (buf) {
+ // back up until we reach '*'
+ while (buf >= recvbuf && buf[0] != '*') {
+ buf--;
+ }
+ if (sscanf(buf, "* %lu EXISTS\r\n", &messages) == 1) {
+ timed_thread_lock(mail->p_timed_thread);
+ mail->messages = messages;
+ timed_thread_unlock(mail->p_timed_thread);
+ }
+ }
+ buf = recvbuf;
+ buf = strstr(buf, "RECENT");
+ while (buf && strlen(buf) > 1 && strstr(buf + 1, "RECENT")) {
+ buf = strstr(buf + 1, "RECENT");
+ }
+ if (buf) {
+ // back up until we reach '*'
+ while (buf >= recvbuf && buf[0] != '*') {
+ buf--;
+ }
+ if (sscanf(buf, "* %lu RECENT\r\n", &recent) != 1) {
+ recent = 0;
+ }
+ }
+ /*
+ * check if we got a FETCH from server, recent was
+ * something other than 0, or we had a timeout
+ */
+ buf = recvbuf;
+ if (recent > 0 || (buf && strstr(buf, " FETCH ")) || timeout.tv_sec == 0) {
+ // re-check messages and unseen
+ if (imap_command(sockfd, "DONE\r\n", recvbuf, "a5 OK")) {
+ fail++;
+ break;
+ }
+ strncpy(sendbuf, "a2 STATUS ", MAXDATASIZE);
+ strncat(sendbuf, mail->folder, MAXDATASIZE - strlen(sendbuf) - 1);
+ strncat(sendbuf, " (MESSAGES UNSEEN)\r\n",
+ MAXDATASIZE - strlen(sendbuf) - 1);
+ if (imap_command(sockfd, sendbuf, recvbuf, "a2 OK")) {
+ fail++;
+ break;
+ }
+ if (imap_check_status(recvbuf, mail)) {
+ fail++;
+ break;
+ }
+ strncpy(sendbuf, "a5 IDLE\r\n", MAXDATASIZE);
+ if (imap_command(sockfd, sendbuf, recvbuf, "+ idling")) {
+ fail++;
+ break;
+ }
+ }
+ /*
+ * check if we got a BYE from server
+ */
+ buf = recvbuf;
+ if (buf && strstr(buf, "* BYE")) {
+ // need to re-connect
+ break;
+ }
+ }
+ imap_unseen_command(mail, old_unseen, old_messages);
+ fail = 0;
+ old_unseen = mail->unseen;
+ old_messages = mail->messages;
+ }
+ } else {
+ strncpy(sendbuf, "a3 logout\r\n", MAXDATASIZE);
+ if (send(sockfd, sendbuf, strlen(sendbuf), 0) == -1) {
+ perror("send a3");
+ fail++;
+ break;
+ }
+ timeout.tv_sec = 60; // 60 second timeout i guess
+ timeout.tv_usec = 0;
+ FD_ZERO(&fdset);
+ FD_SET(sockfd, &fdset);
+ res = select(sockfd + 1, &fdset, NULL, NULL, &timeout);
+ if (res > 0) {
+ if ((numbytes = recv(sockfd, recvbuf, MAXDATASIZE - 1, 0)) == -1) {
+ perror("recv a3");
+ fail++;
+ break;
+ }
+ }
+ recvbuf[numbytes] = '\0';
+ DBGP2("imap_thread() received: %s", recvbuf);
+ if (strstr(recvbuf, "a3 OK") == NULL) {
+ ERR("IMAP logout failed: %s", recvbuf);
+ fail++;
+ break;
+ }
+ }
+ } while (0);
+ if ((fstat(sockfd, &stat_buf) == 0) && S_ISSOCK(stat_buf.st_mode)) {
+ /* if a valid socket, close it */
+ close(sockfd);
+ }
+ if (timed_thread_test(mail->p_timed_thread, 0)) {
+ timed_thread_exit(mail->p_timed_thread);
+ }
+ }
+ mail->unseen = 0;
+ mail->messages = 0;
+ return 0;
+}
+
+int pop3_command(int sockfd, const char *command, char *response, const char *verify)
+{
+ struct timeval timeout;
+ fd_set fdset;
+ int res, numbytes = 0;
+ if (send(sockfd, command, strlen(command), 0) == -1) {
+ perror("send");
+ return -1;
+ }
+ timeout.tv_sec = 60; // 60 second timeout i guess
+ timeout.tv_usec = 0;
+ FD_ZERO(&fdset);
+ FD_SET(sockfd, &fdset);
+ res = select(sockfd + 1, &fdset, NULL, NULL, &timeout);
+ if (res > 0) {
+ if ((numbytes = recv(sockfd, response, MAXDATASIZE - 1, 0)) == -1) {
+ perror("recv");
+ return -1;
+ }
+ }
+ DBGP2("pop3_command() received: %s", response);
+ response[numbytes] = '\0';
+ if (strstr(response, verify) == NULL) {
+ return -1;
+ }
+ return 0;
+}
+
+void *pop3_thread(void *arg)
+{
+ int sockfd, numbytes;
+ char recvbuf[MAXDATASIZE];
+ char sendbuf[MAXDATASIZE];
+ char *reply;
+ unsigned int fail = 0;
+ unsigned long old_unseen = ULONG_MAX;
+ struct stat stat_buf;
+ struct hostent he, *he_res = 0;
+ int he_errno;
+ char hostbuff[2048];
+ struct sockaddr_in their_addr; // connector's address information
+ struct mail_s *mail = (struct mail_s *)arg;
+
+#ifdef HAVE_GETHOSTBYNAME_R
+ if (gethostbyname_r(mail->host, &he, hostbuff, sizeof(hostbuff), &he_res, &he_errno)) { // get the host info
+ ERR("POP3 gethostbyname_r: %s", hstrerror(h_errno));
+ exit(1);
+ }
+#else /* HAVE_GETHOSTBYNAME_R */
+ if ((he_res = gethostbyname(mail->host)) == NULL) { // get the host info
+ herror("gethostbyname");
+ exit(1);
+ }
+#endif /* HAVE_GETHOSTBYNAME_R */
+ while (fail < mail->retries) {
+ struct timeval timeout;
+ int res;
+ fd_set fdset;
+
+ if (fail > 0) {
+ ERR("Trying POP3 connection again for %s@%s (try %u/%u)",
+ mail->user, mail->host, fail + 1, mail->retries);
+ }
+ do {
+ if ((sockfd = socket(PF_INET, SOCK_STREAM, 0)) == -1) {
+ perror("socket");
+ fail++;
+ break;
+ }
+
+ // host byte order
+ their_addr.sin_family = AF_INET;
+ // short, network byte order
+ their_addr.sin_port = htons(mail->port);
+ their_addr.sin_addr = *((struct in_addr *) he_res->h_addr);
+ // zero the rest of the struct
+ memset(&(their_addr.sin_zero), '\0', 8);
+
+ if (connect(sockfd, (struct sockaddr *) &their_addr,
+ sizeof(struct sockaddr)) == -1) {
+ perror("connect");
+ fail++;
+ break;
+ }
+
+ timeout.tv_sec = 60; // 60 second timeout i guess
+ timeout.tv_usec = 0;
+ FD_ZERO(&fdset);
+ FD_SET(sockfd, &fdset);
+ res = select(sockfd + 1, &fdset, NULL, NULL, &timeout);
+ if (res > 0) {
+ if ((numbytes = recv(sockfd, recvbuf, MAXDATASIZE - 1, 0)) == -1) {
+ perror("recv");
+ fail++;
+ break;
+ }
+ } else {
+ ERR("POP3 connection failed: timeout\n");
+ fail++;
+ break;
+ }
+ DBGP2("pop3_thread received: %s", recvbuf);
+ recvbuf[numbytes] = '\0';
+ if (strstr(recvbuf, "+OK ") != recvbuf) {
+ ERR("POP3 connection failed, probably not a POP3 server");
+ fail++;
+ break;
+ }
+ strncpy(sendbuf, "USER ", MAXDATASIZE);
+ strncat(sendbuf, mail->user, MAXDATASIZE - strlen(sendbuf) - 1);
+ strncat(sendbuf, "\r\n", MAXDATASIZE - strlen(sendbuf) - 1);
+ if (pop3_command(sockfd, sendbuf, recvbuf, "+OK ")) {
+ fail++;
+ break;
+ }
+
+ strncpy(sendbuf, "PASS ", MAXDATASIZE);
+ strncat(sendbuf, mail->pass, MAXDATASIZE - strlen(sendbuf) - 1);
+ strncat(sendbuf, "\r\n", MAXDATASIZE - strlen(sendbuf) - 1);
+ if (pop3_command(sockfd, sendbuf, recvbuf, "+OK ")) {
+ ERR("POP3 server login failed: %s", recvbuf);
+ fail++;
+ break;
+ }
+
+ strncpy(sendbuf, "STAT\r\n", MAXDATASIZE);
+ if (pop3_command(sockfd, sendbuf, recvbuf, "+OK ")) {
+ perror("send STAT");
+ fail++;
+ break;
+ }
+
+ // now we get the data
+ reply = recvbuf + 4;
+ if (reply == NULL) {
+ ERR("Error parsing POP3 response: %s", recvbuf);
+ fail++;
+ break;
+ } else {
+ timed_thread_lock(mail->p_timed_thread);
+ sscanf(reply, "%lu %lu", &mail->unseen, &mail->used);
+ timed_thread_unlock(mail->p_timed_thread);
+ }
+
+ strncpy(sendbuf, "QUIT\r\n", MAXDATASIZE);
+ if (pop3_command(sockfd, sendbuf, recvbuf, "+OK")) {
+ ERR("POP3 logout failed: %s", recvbuf);
+ fail++;
+ break;
+ }
+
+ if (strlen(mail->command) > 1 && mail->unseen > old_unseen) {
+ // new mail goodie
+ if (system(mail->command) == -1) {
+ perror("system()");
+ }
+ }
+ fail = 0;
+ old_unseen = mail->unseen;
+ } while (0);
+ if ((fstat(sockfd, &stat_buf) == 0) && S_ISSOCK(stat_buf.st_mode)) {
+ /* if a valid socket, close it */
+ close(sockfd);
+ }
+ if (timed_thread_test(mail->p_timed_thread, 0)) {
+ timed_thread_exit(mail->p_timed_thread);
+ }
+ }
+ mail->unseen = 0;
+ mail->used = 0;
+ return 0;
+}
+