/* * imap.c, SJ */ #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #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; }