diff --git a/src/archive.c b/src/archive.c index 9984c26f..237ae756 100644 --- a/src/archive.c +++ b/src/archive.c @@ -18,6 +18,7 @@ #include #include #include +#include #include @@ -130,34 +131,89 @@ int inf(unsigned char *in, int len, int mode, char **buffer, FILE *dest){ } +unsigned char *extract_file_from_zip(char *zipfile, char *filename, zip_uint64_t *size){ + int i=0, errorp; + unsigned char *p=NULL; + struct zip *z; + struct zip_stat sb; + struct zip_file *zf; + + z = zip_open(zipfile, ZIP_RDONLY, &errorp); + if(!z){ + syslog(LOG_INFO, "%s: error: corrupt zip file=%s, error code=%d", zipfile, filename, errorp); + return NULL; + } + + while(zip_stat_index(z, i, 0, &sb) == 0){ + if(sb.size > 0 && sb.comp_size > 0 && strncmp(sb.name, filename, strlen(filename)) == 0){ + *size = sb.comp_size; + zf = zip_fopen_index(z, i, 0); + if(zf){ + p = malloc(sb.comp_size); + if(p){ + if(zip_fread(zf, p, sb.comp_size) == -1){ + syslog(LOG_PRIORITY, "zip_fread(): Error reading %s from %s", filename, zipfile); + free(p); + p = NULL; + } + } + zip_fclose(zf); + } + else syslog(LOG_PRIORITY, "cannot extract '%s' from '%s'", filename, zipfile); + } + + i++; + } + + zip_close(z); + + return p; +} + + int retrieve_file_from_archive(char *filename, int mode, char **buffer, FILE *dest, struct config *cfg){ int rc=0, n, olen, tlen, len, fd=-1; - unsigned char *s=NULL, *addr=NULL, inbuf[REALLYBIGBUFSIZE]; + char *relfilename; + unsigned char *s=NULL, *addr=NULL, *zipbuf=NULL, inbuf[REALLYBIGBUFSIZE]; struct stat st; + zip_uint64_t zipped_size=0; #if OPENSSL_VERSION_NUMBER < 0x10100000L EVP_CIPHER_CTX ctx; #else EVP_CIPHER_CTX *ctx=NULL; #endif + syslog(LOG_PRIORITY, "retrieveing %s", filename); if(filename == NULL) return 1; + relfilename = strchr(filename, ' '); + if(relfilename){ + // We'll read the given file from a zip file + // filename is decomposed to and + *relfilename = '\0'; + relfilename++; - fd = open(filename, O_RDONLY); - if(fd == -1){ - syslog(LOG_PRIORITY, "%s: cannot open()", filename); - return 1; + zipbuf = extract_file_from_zip(filename, relfilename, &zipped_size); + len = zipped_size+EVP_MAX_BLOCK_LENGTH; } + else { + // We have a distinct .m file or an attachment to read from + fd = open(filename, O_RDONLY); + if(fd == -1){ + syslog(LOG_PRIORITY, "%s: cannot open()", filename); + return 1; + } + if(fstat(fd, &st)){ + syslog(LOG_PRIORITY, "%s: cannot fstat()", filename); + close(fd); + return 1; + } - if(fstat(fd, &st)){ - syslog(LOG_PRIORITY, "%s: cannot fstat()", filename); - close(fd); - return 1; + len = st.st_size+EVP_MAX_BLOCK_LENGTH; } - if(cfg->encrypt_messages == 1){ #if OPENSSL_VERSION_NUMBER < 0x10100000L EVP_CIPHER_CTX_init(&ctx); @@ -170,8 +226,6 @@ int retrieve_file_from_archive(char *filename, int mode, char **buffer, FILE *de EVP_DecryptInit_ex(ctx, EVP_bf_cbc(), NULL, cfg->key, cfg->iv); #endif - len = st.st_size+EVP_MAX_BLOCK_LENGTH; - s = malloc(len); if(!s){ @@ -181,27 +235,35 @@ int retrieve_file_from_archive(char *filename, int mode, char **buffer, FILE *de tlen = 0; - while((n = read(fd, inbuf, sizeof(inbuf)))){ + if(!relfilename){ + while((n = read(fd, inbuf, sizeof(inbuf)))){ - #if OPENSSL_VERSION_NUMBER < 0x10100000L - if(!EVP_DecryptUpdate(&ctx, s+tlen, &olen, inbuf, n)){ - #else - if(!EVP_DecryptUpdate(ctx, s+tlen, &olen, inbuf, n)){ - #endif - syslog(LOG_PRIORITY, "%s: EVP_DecryptUpdate()", filename); + #if OPENSSL_VERSION_NUMBER < 0x10100000L + if(!EVP_DecryptUpdate(&ctx, s+tlen, &olen, inbuf, n)){ + #else + if(!EVP_DecryptUpdate(ctx, s+tlen, &olen, inbuf, n)){ + #endif + syslog(LOG_PRIORITY, "%s: EVP_DecryptUpdate()", filename); + goto CLEANUP; + } + + tlen += olen; + } + } else { + if(!EVP_DecryptUpdate(ctx, s+tlen, &olen, zipbuf, zipped_size)){ + printf("error in EVP_DecryptUpdate\n"); goto CLEANUP; } - tlen += olen; } - #if OPENSSL_VERSION_NUMBER < 0x10100000L if(EVP_DecryptFinal(&ctx, s + tlen, &olen) != 1){ #else if(EVP_DecryptFinal(ctx, s + tlen, &olen) != 1){ #endif syslog(LOG_PRIORITY, "%s: EVP_DecryptFinal()", filename); + printf("error in EVP_DecryptFinal\n"); goto CLEANUP; } @@ -222,6 +284,7 @@ int retrieve_file_from_archive(char *filename, int mode, char **buffer, FILE *de CLEANUP: if(fd != -1) close(fd); if(s) free(s); + if(zipbuf) free(zipbuf); if(cfg->encrypt_messages == 1) #if OPENSSL_VERSION_NUMBER < 0x10100000L EVP_CIPHER_CTX_cleanup(&ctx); @@ -233,13 +296,57 @@ CLEANUP: } +void assemble_filename(char *filename, int len, char *s, struct config *cfg){ + char zipfilename[SMALLBUFSIZE]; + struct stat st; + + /* + * If the zip file exists, then fix the filename to be ' ' + */ + snprintf(zipfilename, sizeof(zipfilename)-1, "%s/%02x/%c%c%c.zip", cfg->queuedir, cfg->server_id, s[8], s[9], s[10]); + + if(stat(zipfilename, &st)){ + snprintf(filename, len-1, "%s/%02x/%c%c%c/%c%c/%c%c/%s.m", + cfg->queuedir, cfg->server_id, s[8], s[9], s[10], s[RND_STR_LEN-4], s[RND_STR_LEN-3], s[RND_STR_LEN-2], s[RND_STR_LEN-1], s); + #ifdef HAVE_SUPPORT_FOR_COMPAT_STORAGE_LAYOUT + if(stat(filename, &st)){ + snprintf(filename, len-1, "%s/%02x/%c%c/%c%c/%c%c/%s.m", + cfg->queuedir, cfg->server_id, s[RND_STR_LEN-6], s[RND_STR_LEN-5], s[RND_STR_LEN-4], s[RND_STR_LEN-3], s[RND_STR_LEN-2], s[RND_STR_LEN-1], s); + } + #endif + } else { + snprintf(filename, len-1, "%s %c%c%c/%c%c/%c%c/%s.m", + zipfilename, s[8], s[9], s[10], s[RND_STR_LEN-4], s[RND_STR_LEN-3], s[RND_STR_LEN-2], s[RND_STR_LEN-1], s); + } +} + + +void assemble_attachment_filename(char *filename, int len, char *s, int attachment_id, struct config *cfg){ + char zipfilename[SMALLBUFSIZE]; + struct stat st; + + snprintf(zipfilename, sizeof(zipfilename)-1, "%s/%02x/%c%c%c.zip", cfg->queuedir, cfg->server_id, s[8], s[9], s[10]); + + if(stat(zipfilename, &st)){ + snprintf(filename, len-1, "%s/%02x/%c%c%c/%c%c/%c%c/%s.a%d", cfg->queuedir, cfg->server_id, s[8], s[9], s[10], s[RND_STR_LEN-4], s[RND_STR_LEN-3], s[RND_STR_LEN-2], s[RND_STR_LEN-1], s, attachment_id); + + #ifdef HAVE_SUPPORT_FOR_COMPAT_STORAGE_LAYOUT + if(stat(filename, &st)){ + snprintf(filename, len-1, "%s/%02x/%c%c/%c%c/%c%c/%s.a%d", + cfg->queuedir, cfg->server_id, s[RND_STR_LEN-6], s[RND_STR_LEN-5], s[RND_STR_LEN-4], s[RND_STR_LEN-3], s[RND_STR_LEN-2], s[RND_STR_LEN-1], s, attachment_id); + } + #endif + } else { + snprintf(filename, len-1, "%s %c%c%c/%c%c/%c%c/%s.a%d", + zipfilename, s[8], s[9], s[10], s[RND_STR_LEN-4], s[RND_STR_LEN-3], s[RND_STR_LEN-2], s[RND_STR_LEN-1], s, attachment_id); + } +} + + int retrieve_email_from_archive(struct session_data *sdata, FILE *dest, struct config *cfg){ int attachments; char *buffer=NULL, *saved_buffer, *p, filename[SMALLBUFSIZE]; struct ptr_array ptr_arr[MAX_ATTACHMENTS]; -#ifdef HAVE_SUPPORT_FOR_COMPAT_STORAGE_LAYOUT - struct stat st; -#endif if(strlen(sdata->ttmpfile) != RND_STR_LEN){ printf("invalid piler-id: %s\n", sdata->ttmpfile); @@ -253,12 +360,8 @@ int retrieve_email_from_archive(struct session_data *sdata, FILE *dest, struct c return 1; } - snprintf(filename, sizeof(filename)-1, "%s/%02x/%c%c%c/%c%c/%c%c/%s.m", cfg->queuedir, cfg->server_id, *(sdata->ttmpfile+8), *(sdata->ttmpfile+9), *(sdata->ttmpfile+10), *(sdata->ttmpfile+RND_STR_LEN-4), *(sdata->ttmpfile+RND_STR_LEN-3), *(sdata->ttmpfile+RND_STR_LEN-2), *(sdata->ttmpfile+RND_STR_LEN-1), sdata->ttmpfile); -#ifdef HAVE_SUPPORT_FOR_COMPAT_STORAGE_LAYOUT - if(stat(filename, &st)){ - snprintf(filename, sizeof(filename)-1, "%s/%02x/%c%c/%c%c/%c%c/%s.m", cfg->queuedir, cfg->server_id, *(sdata->ttmpfile+RND_STR_LEN-6), *(sdata->ttmpfile+RND_STR_LEN-5), *(sdata->ttmpfile+RND_STR_LEN-4), *(sdata->ttmpfile+RND_STR_LEN-3), *(sdata->ttmpfile+RND_STR_LEN-2), *(sdata->ttmpfile+RND_STR_LEN-1), sdata->ttmpfile); - } -#endif + + assemble_filename(filename, sizeof(filename), sdata->ttmpfile, cfg); if(attachments == 0){ retrieve_file_from_archive(filename, WRITE_TO_STDOUT, &buffer, dest, cfg); @@ -280,14 +383,7 @@ int retrieve_email_from_archive(struct session_data *sdata, FILE *dest, struct c buffer = p + strlen(pointer); if(strlen(ptr_arr[i].piler_id) == RND_STR_LEN){ - snprintf(filename, sizeof(filename)-1, "%s/%02x/%c%c%c/%c%c/%c%c/%s.a%d", cfg->queuedir, cfg->server_id, ptr_arr[i].piler_id[8], ptr_arr[i].piler_id[9], ptr_arr[i].piler_id[10], ptr_arr[i].piler_id[RND_STR_LEN-4], ptr_arr[i].piler_id[RND_STR_LEN-3], ptr_arr[i].piler_id[RND_STR_LEN-2], ptr_arr[i].piler_id[RND_STR_LEN-1], ptr_arr[i].piler_id, ptr_arr[i].attachment_id); - - #ifdef HAVE_SUPPORT_FOR_COMPAT_STORAGE_LAYOUT - if(stat(filename, &st)){ - snprintf(filename, sizeof(filename)-1, "%s/%02x/%c%c/%c%c/%c%c/%s.a%d", cfg->queuedir, cfg->server_id, ptr_arr[i].piler_id[RND_STR_LEN-6], ptr_arr[i].piler_id[RND_STR_LEN-5], ptr_arr[i].piler_id[RND_STR_LEN-4], ptr_arr[i].piler_id[RND_STR_LEN-3], ptr_arr[i].piler_id[RND_STR_LEN-2], ptr_arr[i].piler_id[RND_STR_LEN-1], ptr_arr[i].piler_id, ptr_arr[i].attachment_id); - } - #endif - + assemble_attachment_filename(filename, sizeof(filename), ptr_arr[i].piler_id, ptr_arr[i].attachment_id, cfg); retrieve_file_from_archive(filename, WRITE_TO_STDOUT, NULL, dest, cfg); } }