mirror of
https://bitbucket.org/jsuto/piler.git
synced 2024-11-08 12:32:00 +01:00
364 lines
8.7 KiB
C
364 lines
8.7 KiB
C
/*
|
|
* imap.c, SJ
|
|
*/
|
|
|
|
#include <stdio.h>
|
|
#include <stdlib.h>
|
|
#include <string.h>
|
|
#include <strings.h>
|
|
#include <sys/types.h>
|
|
#include <sys/stat.h>
|
|
#include <sys/socket.h>
|
|
#include <netinet/in.h>
|
|
#include <arpa/inet.h>
|
|
#include <netdb.h>
|
|
#include <sys/time.h>
|
|
#include <openssl/ssl.h>
|
|
#include <openssl/err.h>
|
|
#include <fcntl.h>
|
|
#include <ctype.h>
|
|
#include <syslog.h>
|
|
#include <unistd.h>
|
|
#include <limits.h>
|
|
#include <piler.h>
|
|
|
|
|
|
#define CHK_NULL(x, errmsg) if ((x)==NULL) { printf("error: %s\n", errmsg); return ERR; }
|
|
#define CHK_SSL(err, msg) if ((err)==-1) { printf("ssl error: %s\n", msg); return ERR; }
|
|
|
|
|
|
unsigned long resolve_host(char *host){
|
|
struct hostent *h;
|
|
struct in_addr addr;
|
|
|
|
if(!host) return 0;
|
|
|
|
if((addr.s_addr = inet_addr(host)) == -1){
|
|
if((h = gethostbyname(host)) == NULL){
|
|
return 0;
|
|
}
|
|
else return *(unsigned long*)h->h_addr;
|
|
}
|
|
else return addr.s_addr;
|
|
}
|
|
|
|
|
|
int get_message_length_from_imap_answer(char *s, int *_1st_line_bytes){
|
|
char *p, *q;
|
|
int len=0;
|
|
|
|
|
|
p = strstr(s, "\r\n");
|
|
if(!p){
|
|
printf("invalid reply: %s", s);
|
|
return len;
|
|
}
|
|
|
|
*p = '\0';
|
|
|
|
//*_1st_line_bytes = strlen(s)+2;
|
|
*_1st_line_bytes = p-s+2;
|
|
|
|
if(*(p-1) == '}') *(p-1) = '\0';
|
|
|
|
|
|
q = strchr(s, '{');
|
|
if(q){
|
|
q++;
|
|
len = atoi(q);
|
|
}
|
|
|
|
*(p-1) = '}';
|
|
*p = '\r';
|
|
|
|
return len;
|
|
}
|
|
|
|
|
|
int is_last_complete_packet(char *s, int len, char *tagok, char *tagbad, int *pos){
|
|
char *p;
|
|
|
|
*pos = 0;
|
|
|
|
if(*(s+len-2) == '\r' && *(s+len-1) == '\n'){
|
|
if((p = strstr(s, tagok))){
|
|
*pos = p - s;
|
|
if(*pos > 3) *pos -= 2;
|
|
return 1;
|
|
}
|
|
if(strstr(s, tagbad)) return 1;
|
|
}
|
|
|
|
return 0;
|
|
}
|
|
|
|
|
|
int process_imap_folder(int sd, int *seq, char *folder, struct session_data *sdata, struct __data *data, int use_ssl, struct __config *cfg){
|
|
int rc=ERR, i, n, pos, endpos, messages=0, len, readlen, fd, lastpos, nreads, processed_messages=0;
|
|
char *p, tag[SMALLBUFSIZE], tagok[SMALLBUFSIZE], tagbad[SMALLBUFSIZE], buf[MAXBUFSIZE], filename[SMALLBUFSIZE];
|
|
char aggrbuf[3*MAXBUFSIZE];
|
|
|
|
snprintf(tag, sizeof(tag)-1, "A%d", *seq); snprintf(tagok, sizeof(tagok)-1, "\r\nA%d OK", (*seq)++);
|
|
snprintf(buf, sizeof(buf)-1, "%s SELECT \"%s\"\r\n", tag, folder);
|
|
|
|
n = write1(sd, buf, use_ssl, data->ssl);
|
|
n = recvtimeoutssl(sd, buf, sizeof(buf), 10, use_ssl, data->ssl);
|
|
|
|
if(!strstr(buf, tagok)){
|
|
trimBuffer(buf);
|
|
printf("error: %s\n", buf);
|
|
return rc;
|
|
}
|
|
|
|
p = strstr(buf, " EXISTS");
|
|
if(p){
|
|
*p = '\0';
|
|
p = strrchr(buf, '\n');
|
|
if(p){
|
|
while(!isdigit(*p)){ p++; }
|
|
messages = atoi(p);
|
|
}
|
|
}
|
|
|
|
printf("found %d messages\n", messages);
|
|
|
|
if(messages <= 0) return rc;
|
|
|
|
for(i=1; i<=messages; i++){
|
|
processed_messages++;
|
|
printf("processed: %7d\r", processed_messages); fflush(stdout);
|
|
|
|
snprintf(tag, sizeof(tag)-1, "A%d", *seq);
|
|
snprintf(tagok, sizeof(tagok)-1, "\r\nA%d OK", (*seq)++);
|
|
snprintf(tagbad, sizeof(tagbad)-1, "\r\n%s BAD", tag);
|
|
|
|
snprintf(buf, sizeof(buf)-1, "%s FETCH %d (BODY.PEEK[])\r\n", tag, i);
|
|
|
|
snprintf(filename, sizeof(filename)-1, "%s-%d.txt", folder, i);
|
|
unlink(filename);
|
|
|
|
fd = open(filename, O_CREAT|O_EXCL|O_RDWR|O_TRUNC, S_IRUSR|S_IWUSR);
|
|
if(fd == -1){
|
|
printf("cannot open: %s\n", filename);
|
|
return rc;
|
|
}
|
|
|
|
|
|
n = write1(sd, buf, use_ssl, data->ssl);
|
|
|
|
readlen = 0;
|
|
pos = 0;
|
|
len = 0;
|
|
nreads = 0;
|
|
endpos = 0;
|
|
|
|
memset(aggrbuf, 0, sizeof(aggrbuf));
|
|
lastpos = 0;
|
|
|
|
|
|
while((n = recvtimeoutssl(sd, buf, sizeof(buf), 15, use_ssl, data->ssl)) > 0){
|
|
nreads++;
|
|
readlen += n;
|
|
|
|
if(nreads == 1){
|
|
len = get_message_length_from_imap_answer(buf, &pos);
|
|
|
|
if(len < 10){
|
|
printf("%d: too short message! %s\n", i, buf);
|
|
break;
|
|
}
|
|
}
|
|
|
|
if(lastpos + 1 + n < sizeof(aggrbuf)){
|
|
|
|
if(nreads == 1){
|
|
memcpy(aggrbuf+lastpos, buf+pos, n-pos);
|
|
lastpos += n-pos;
|
|
}
|
|
else {
|
|
memcpy(aggrbuf+lastpos, buf, n);
|
|
lastpos += n;
|
|
}
|
|
}
|
|
else {
|
|
write(fd, aggrbuf, sizeof(buf));
|
|
|
|
memmove(aggrbuf, aggrbuf+sizeof(buf), lastpos-sizeof(buf));
|
|
lastpos -= sizeof(buf);
|
|
|
|
memcpy(aggrbuf+lastpos, buf, n);
|
|
lastpos += n;
|
|
}
|
|
|
|
if(is_last_complete_packet(aggrbuf, lastpos, tagok, tagbad, &endpos) == 1){
|
|
write(fd, aggrbuf, lastpos-(lastpos-endpos));
|
|
break;
|
|
}
|
|
|
|
}
|
|
|
|
close(fd);
|
|
|
|
rc = import_message(filename, sdata, data, cfg);
|
|
|
|
unlink(filename);
|
|
}
|
|
|
|
printf("\n");
|
|
|
|
return OK;
|
|
}
|
|
|
|
|
|
int connect_to_imap_server(int sd, int *seq, char *imapserver, char *username, char *password, int port, struct __data *data, int use_ssl){
|
|
int n, pos=0;
|
|
char tag[SMALLBUFSIZE], tagok[SMALLBUFSIZE], buf[MAXBUFSIZE];
|
|
char auth[2*SMALLBUFSIZE];
|
|
unsigned char tmp[SMALLBUFSIZE];
|
|
unsigned long host=0;
|
|
struct sockaddr_in remote_addr;
|
|
X509* server_cert;
|
|
SSL_METHOD *meth;
|
|
char *str;
|
|
|
|
|
|
host = resolve_host(imapserver);
|
|
|
|
remote_addr.sin_family = AF_INET;
|
|
remote_addr.sin_port = htons(port);
|
|
remote_addr.sin_addr.s_addr = host;
|
|
bzero(&(remote_addr.sin_zero),8);
|
|
|
|
if(connect(sd, (struct sockaddr *)&remote_addr, sizeof(struct sockaddr)) == -1){
|
|
printf("connect()\n");
|
|
return ERR;
|
|
}
|
|
|
|
|
|
if(use_ssl == 1){
|
|
|
|
SSLeay_add_ssl_algorithms();
|
|
meth = SSLv3_client_method();
|
|
SSL_load_error_strings();
|
|
|
|
data->ctx = SSL_CTX_new(meth);
|
|
CHK_NULL(data->ctx, "internal SSL error");
|
|
|
|
data->ssl = SSL_new(data->ctx);
|
|
CHK_NULL(data->ssl, "internal ssl error");
|
|
|
|
SSL_set_fd(data->ssl, sd);
|
|
n = SSL_connect(data->ssl);
|
|
CHK_SSL(n, "internal ssl error");
|
|
|
|
//printf("Cipher: %s\n", SSL_get_cipher(data->ssl));
|
|
|
|
server_cert = SSL_get_peer_certificate(data->ssl);
|
|
CHK_NULL(server_cert, "server cert error");
|
|
|
|
//if(verbose){
|
|
str = X509_NAME_oneline(X509_get_subject_name(server_cert), 0, 0);
|
|
CHK_NULL(str, "error in server cert");
|
|
printf("server cert:\n\t subject: %s\n", str);
|
|
OPENSSL_free(str);
|
|
|
|
str = X509_NAME_oneline(X509_get_issuer_name(server_cert), 0, 0);
|
|
CHK_NULL(str, "error in server cert");
|
|
printf("\t issuer: %s\n\n", str);
|
|
OPENSSL_free(str);
|
|
//}
|
|
|
|
X509_free(server_cert);
|
|
}
|
|
|
|
|
|
n = recvtimeoutssl(sd, buf, sizeof(buf), 10, use_ssl, data->ssl);
|
|
|
|
|
|
/*
|
|
* create auth buffer: username + NUL character + username + NUL character + password
|
|
*/
|
|
|
|
memset(tmp, 0, sizeof(tmp));
|
|
pos = 0;
|
|
|
|
memcpy(tmp+pos, username, strlen(username));
|
|
pos = strlen(username) + 1;
|
|
memcpy(tmp+pos, username, strlen(username));
|
|
pos += strlen(username) + 1;
|
|
memcpy(tmp+pos, password, strlen(password));
|
|
pos += strlen(password);
|
|
|
|
|
|
base64_encode(&tmp[0], pos, &auth[0], sizeof(auth));
|
|
|
|
snprintf(tag, sizeof(tag)-1, "A%d", *seq); snprintf(tagok, sizeof(tagok)-1, "A%d OK", (*seq)++);
|
|
snprintf(buf, sizeof(buf)-1, "%s AUTHENTICATE PLAIN %s\r\n", tag, auth);
|
|
|
|
write1(sd, buf, use_ssl, data->ssl);
|
|
n = recvtimeoutssl(sd, buf, sizeof(buf), 10, use_ssl, data->ssl);
|
|
|
|
if(strncmp(buf, tagok, strlen(tagok))){
|
|
printf("login failed, server reponse: %s\n", buf);
|
|
return ERR;
|
|
}
|
|
|
|
return OK;
|
|
}
|
|
|
|
|
|
void close_connection(int sd, struct __data *data, int use_ssl){
|
|
close(sd);
|
|
|
|
if(use_ssl == 1){
|
|
SSL_shutdown(data->ssl);
|
|
SSL_free(data->ssl);
|
|
SSL_CTX_free(data->ctx);
|
|
}
|
|
}
|
|
|
|
|
|
int list_folders(int sd, int *seq, char *folders, int foldersize, int use_ssl, struct __data *data){
|
|
int n;
|
|
char *p, *q, tag[SMALLBUFSIZE], tagok[SMALLBUFSIZE], buf[MAXBUFSIZE], puf[MAXBUFSIZE];
|
|
|
|
snprintf(tag, sizeof(tag)-1, "A%d", *seq); snprintf(tagok, sizeof(tagok)-1, "A%d OK", (*seq)++);
|
|
//snprintf(buf, sizeof(buf)-1, "%s LIST \"\" %%\r\n", tag);
|
|
snprintf(buf, sizeof(buf)-1, "%s LIST \"\" \"*\"\r\n", tag);
|
|
|
|
write1(sd, buf, use_ssl, data->ssl);
|
|
|
|
n = recvtimeoutssl(sd, buf, sizeof(buf), 10, use_ssl, data->ssl);
|
|
|
|
p = &buf[0];
|
|
do {
|
|
memset(puf, 0, sizeof(puf));
|
|
p = split(p, '\n', puf, sizeof(puf)-1);
|
|
trimBuffer(puf);
|
|
|
|
if(strncmp(puf, "* LIST ", 7) == 0){
|
|
q = strstr(puf, "\".\"");
|
|
if(q){
|
|
q += 3;
|
|
|
|
if(*q == ' ') q++;
|
|
if(*q == '"') q++;
|
|
|
|
if(q[strlen(q)-1] == '"') q[strlen(q)-1] = '\0';
|
|
|
|
strncat(folders, "\n", foldersize-1);
|
|
strncat(folders, q, foldersize-1);
|
|
|
|
}
|
|
}
|
|
else {
|
|
if(strncmp(puf, tagok, strlen(tagok)) == 0) {}
|
|
}
|
|
|
|
} while(p);
|
|
|
|
return 0;
|
|
}
|
|
|
|
|