/*******************************************************************************
*
*  xtc.c -  Extract Transfer Content from reassembled TCP stream files
*             using 'Transfer-Encoding: chunked' 
*
*  Copyright (C) 2010 - Christian North
*
*******************************************************************************/

#include <stdio.h>
#include <stdlib.h>
#include <string.h>

#define version  "0.1b"

/******************************************************************************/
void usage()
{
	printf("XTC v0.1b - Extract Transfer Content from ");
	printf("reassembled TCP stream files\n");
	printf("Usage: xtc <stream_file>\n");
	exit(0);
}


/******************************************************************************/
int chunked_encoding(FILE *stream)
{
	const char *encoding="Transfer-Encoding";
	char *chunked="chunked";
	long offset=0;
	size_t len=1024;
	char data[len];
	int bytes=0;
	int c=0;

	while(!feof(stream)){
		bytes=fread(data,len,1,stream);
		if(bytes==0) break;
		for(c=0;c<len;c++){
			if(data[c] == 0x0D){
				if(data[c+1] == 0x0A){
					data[c]='\0';	
					offset+=strlen(data)+2;
					break;
				}
			}
		}
		if(strstr(data,encoding)) {
			if(strstr(data,chunked)){
				rewind(stream);
				return 1;
			}
			return 0;
		}
		fseek(stream,offset,SEEK_SET);
	}

	return 0;
}


/******************************************************************************/
FILE *disposition(char *data)
{
	const char *disposition = "Content-Disposition";
	const char *filename = "filename=";
	FILE *fp;

	if(strstr(data,disposition)) {
		filename=strtok(data,"=");
		printf("Extracting Transfer Content: ");
		printf("%s\n",data+strlen(filename)+1);
		fp=fopen(data+strlen(filename)+1,"a");
		return fp;
	}

	return NULL;
}


/******************************************************************************/
int extract(FILE *stream, FILE *content, long offset)
{
	int bytes=0;
	size_t chunk=0;
	size_t len=1024;
	char data[len];
	int c=0;
	char hex[8];
	char *fragment;

	fseek(stream,offset,SEEK_SET);

	// get first chunk size
	while(!feof(stream)){
		bytes=fread(data,1,len,stream);
		if(bytes==0) break;
		for(c=0;c<bytes;c++){
			if(data[c]   == 0x0D && data[c+1] == 0x0A &&
			   data[c+2] == 0x0D && data[c+3] == 0x0A ){
				offset+=4; // skip leading CRLFCRLF
				snprintf(hex,8,"%c%c%c%c", 
						data[c+4],data[c+5],
						data[c+6],data[c+7]);
				chunk=strtoul(hex,NULL,16);
				printf("chunk offset: %6d - ",offset);
				printf("size %d bytes\n",chunk);
				offset+=6;  // skip chunk-size and CRLF
				break;
			}
			offset++;
		}
		if(chunk) break;
	}

	fseek(stream,offset,SEEK_SET);
	len=8; // CRLF chunk-size CRLF

	// extract remaining chunks...
	while(!feof(stream)){
		fragment = malloc(chunk);
		bytes=fread(fragment,chunk,1,stream);
		if(bytes==0) return;
		fwrite(fragment,chunk,1,content);
		free(fragment);
		offset=ftell(stream);
		bytes=fread(data,len,1,stream);
		if(bytes==0) break;
		snprintf(hex,8,"%c%c%c%c", data[2],data[3], data[4],data[5]);
		chunk=strtoul(hex,NULL,16);
		printf("chunk offset: %6d - size %d bytes\n",offset+2,chunk);
	}

	return 0;
}

/******************************************************************************/
int main(int argc, char **argv)
{
	const char *type= "Type:";
	char *streamfile=NULL;
	FILE *stream=NULL;
	FILE *content=NULL;
	size_t len=1024;
	char data[len];
	long offset=0;
	int c=0;
	int bytes=0;

	if(argc != 2) usage();

	streamfile = argv[1];
	stream = fopen(streamfile,"r");

	if(!chunked_encoding(stream)){
		printf("Transfer-Encoding: chunked - not found in ");
		printf("\"%s\"\n", streamfile);	
		exit(0);
	}

	while(!feof(stream)){
		bytes=fread(data,len,1,stream);
		if(bytes==0) break;
		for(c=0;c<len;c++){
			if(data[c] == 0x0D){
				if(data[c+1] == 0x0A){
					data[c]='\0';	
					offset++;
					break;
				}
			}
			offset++;
		}
		content = disposition(data);
		if(content != NULL) break;
		fseek(stream,offset,SEEK_SET);
	}

	offset--;
	extract(stream,content,offset);

	fclose(stream);

	return 0;
}
/******************************************************************************/

