/*
	tellmail s3_list

*/

#include <stdlib.h>
#include <stdio.h>
#include <string.h>
#include "lib.h"
#include "rstring.h"
#include "mime.h"
#include "md5.h"
#include "xml.h"
#include "lprintf.h"
#ifndef FALSE
#define FALSE 0
#endif
#ifndef TRUE
#define TRUE (!FALSE)
#endif
#include "tcplib.h"
#include "ld_sha.h"
#include "s3.h"
#define DMID 11
#include "debug.h"
int s3_verb(char *verb, char *host, char *url, char *key, char *secret, char *product_token, char *user_token, int (*cb)(void *param, char *bf, int sz), void *param, char *response, char *fname, int from, int len);

static int (*imsg)(const char *, ...);
void s3_init(int (*fn)(const char *, ...)) 
{
	imsg = fn;
}

/*

Authorization = "AWS" + " " + AWSAccessKeyId + ":" + Signature;

Signature = Base64( HMAC-SHA1( UTF-8-Encoding-Of( YourSecretAccessKeyID, StringToSign ) ) );

StringToSign = HTTP-Verb + "\n" +
	Content-MD5 + "\n" +
	Content-Type + "\n" +
	Date + "\n" +
	CanonicalizedAmzHeaders +
	CanonicalizedResource;

CanonicalizedResource = [ "/" + Bucket ] +
	<HTTP-Request-URI, from the protocol name up to the query string> +
	[ sub-resource, if present. For example "?acl", "?location", "?logging", or "?torrent"];

CanonicalizedAmzHeaders = <described below>


*/
int s3_sign(Pair **phdrs, char *key, char *secret, char *host)
{
	// Build string to sign.
	Pair *p;
	Pair *hdrs = *phdrs;
	char url[BFSZ];
	char bf[BFSZ];
	char *s;
	char digest[BFSZ];
	int dlen;
	char sigtext[BFSZ];
	char head[BFSZ];
	char path[BFSZ];
	char tosign[BFSZ];
	char verb[BFSZ];
	char canres[BFSZ];
	char canamz[BFSZ];
	Wordlist *w,*wlist=NULL;
	strcpy(verb,"");

	imsg("s3: Signing headers key (%s) secret (%s) host (%s)\n",key,secret,host);
	// get the http verb...
	for (p=hdrs;p!=NULL;p=p->next) {
//		printf("Headers (%s) (%s)\n",p->var,p->val);
		if (strchr(p->var,':')==NULL) {
			ncpy(verb,p->var,BFSZ-1);
			ncpy(url,p->val,BFSZ-1);
			s = strchr(url,' '); if (s!=NULL) *s = 0;
			break;
		}
	}

	ncpy(tosign,verb,BFSZ-1); ncat(tosign,"\n",BFSZ-1);
	ncat(tosign,pair_find(hdrs,"Content-MD5:"),BFSZ-1); ncat(tosign,"\n",BFSZ-1);
	ncat(tosign,pair_find(hdrs,"Content-Type:"),BFSZ-1); ncat(tosign,"\n",BFSZ-1);
	ncat(tosign,pair_find(hdrs,"Date:"),BFSZ-1); ncat(tosign,"\n",BFSZ-1);
//	printf("tosign (%s)\n",tosign);
//	printf("Date header (%s)\n",pair_find(hdrs,"Date:"));
	// Build canres...
	strcpy(canres,"");
	// Append bucket name if bucket specified.
	lpcat(canres,BFSZ-1,"/%s",host);
	s = strstr(canres,".s3.amazonaws.com"); if (s!=NULL) *s = 0;
	if (s==NULL) strcpy(canres,"");

	ncpy(path,url,BFSZ-1);
	s = strchr(path,'?');  if (s!=NULL) *s = 0;
	ncat(canres,path,BFSZ-1);

//	printf(" tosign (%s)\n",tosign);
//	printf(" canres (%s)\n",canres);
	// Build canamz..
	for (p=hdrs;p!=NULL;p=p->next) {
		if (strncmpnc(p->var,"x-amz-",6)==0) {
			ncpy(head,p->var,BFSZ-1); strlwr(head);
			lprintf(bf,BFSZ-1,"%s%s\n",head,p->val); // headers have colon already... I think...
			word_add(&wlist,bf);
		}
	}
	word_sort(&wlist);
	strcpy(canamz,"");
	for (w=wlist;w!=NULL;w=w->next) {
		ncat(canamz,w->word,BFSZ-1);
	}
//	printf(" canamz (%s)\n",canamz);
	word_free(&wlist);
	ncat(tosign,canamz,BFSZ-1);
	ncat(tosign,canres,BFSZ-1);
//printf("Tosign: (%s)\n",tosign);

//	hmac_sha_test();
	hmac_sha(tosign,lib_strlen(tosign),secret,lib_strlen(secret),digest,&dlen);
	mime_encode_base64_bad(digest,dlen+1,sigtext,BFSZ);
	pair_printf(phdrs,"Authorization:","AWS %s:%s",key,sigtext);

	return TRUE;
}

int s3_verb(char *verb, char *host, char *url, char *key, char *secret, char *product_token, char *user_token, int (*cb)(void *param, char *bf, int sz), void *param, char *response, char *fname, int from, int len)
{
	char bf[BFSZ];
	char location[BFSZ];
	char *s;
	Chan *c=NULL;
	int code;
	int ishead=TRUE;
	int count;
	int expect=FALSE;
	int got=0;
	int done_redirect=FALSE;
	int worked=FALSE;
	int failed=FALSE;
	int isssl=FALSE;
	int chunked=FALSE;
	int downloadsize=0;
	FILE *f=NULL;
	char tmp3[BFSZ];
	int tout=100;
	Pair *hdrs=NULL;
	Pair *p;
	int gotresponse;
	if (imsg==NULL) imsg = printf;

//printf("S3 VERB %s %s %s %s %s\n",verb,key,secret,product_token,user_token);
	strcpy(response,"");
	strcpy(location,"");
	pair_printf(&hdrs,verb,"%s HTTP/1.1",url);
	pair_printf(&hdrs,"Host:","%s",host);
	if (len>0) {
		pair_printf(&hdrs,"Range:","bytes=%d-%d",from,from+len-1);
	}
	pair_printf(&hdrs,"Date:","%s",lib_web_date(tmp3));
	if (fname && (strcmp(verb,"PUT")==0) ) {
		f = fopen(fname,"rb");
		imsg("Opened (%s) to send %p\n",fname,f);
	}

	
	if (user_token) {
		char both[BFSZ];
		lprintf(both,BFSZ-1,"%s,%s",user_token,product_token);
		pair_printf(&hdrs,"x-amz-security-token:",both);
	}
	if (f) {
		pair_printf(&hdrs,"Content-Length:","%d",lib_file_osize(f));
//		pair_printf(&hdrs,"Expect:","100-continue"); // we want to get a status...
	}

	s3_sign(&hdrs,key,secret,host);
	pair_reverse(&hdrs);

	tcp_new(&c);
	tcp_nodelay(c,TRUE);
	while(TRUE) {
		imsg("http: Open (%s)\n",host);
		if (!tcp_open(c,host,80,NULL)) break;
		if(isssl){
			if (!tcp_ssl_init(c,FALSE,NULL,response)) {
				imsg("http: ssl init failed %s\n",response);
				break;
			}
		}
		for (p=hdrs;p!=NULL;p=p->next) {
			tcp_printf(c,"%s %s\r\n",p->var,p->val);
			imsg("--> %s %s\n",p->var,p->val);
		}
		if (!tcp_printf(c,"\r\n")) break;
		imsg("-->\n");
		worked = TRUE;
		break;
	}
	pair_free(&hdrs);
	if (!worked) {
		tcp_error(c,response);
		lpcat(response,BFSZ-1," Host (%s)",host);
		imsg("http: xml not sent %s\n",response);
		tcp_free(&c);
		return FALSE;
	}

	if (f!=NULL) {
		int sent=0;
		int code=500;
		if (expect) {
			tcp_readline(c,bf,BFSZ-1);
			ncpy(response,bf,BFSZ-1);
			imsg("Response-100: %s\n",response);
			s = strchr(bf,' '); 
			if (s!=NULL) {
				code = atoi(s+1);
				lprintf(response,BFSZ-1,"s3ret1: %s",s+1);
			}
			if (code!=100) { // don't try and send the file...
				fclose(f);
				failed = TRUE;
				goto response;
			}
		}
		imsg("Sending data now\n");
		for (;!feof(f);) {
			int r;
			r = (int)fread(bf,1,BFSZ-1,f);
			if (r<=0) break;
			tcp_write(c,bf,r);
			sent += r;
		}
		fclose(f);
		imsg("Sent %d  bytese of data\n",sent);
	}
response:
	gotresponse=FALSE;
	for (;ishead && tcp_readline(c,bf,BFSZ-1);) {
		imsg("s3: read: %d %s\n",lib_strlen(bf),bf);
		if (strncmp(bf,"HTTP/",5)==0) {
			gotresponse=TRUE;
			ncpy(response,bf,BFSZ-1);
			imsg("Response: %s\n",response);
			s = strchr(bf,' '); 
			if (s!=NULL) {
				code = atoi(s+1);
				lprintf(response,BFSZ-1,"s3ret2: %s",s+1);
			}
			if (code>400) { // still read the response as it holds the error message...
				//tcp_free(&c);
				//return FALSE;
			}
		}
		if (strncmp(bf, "Location: ",10)==0)  {ncpy(location,bf+10,BFSZ-1);}
		if (strncmp(bf, "Content-Length: ",16)==0)  downloadsize = atoi( bf+16 );
		else if (strncmp(bf, "Transfer-Encoding: chunked",26)==0)  chunked = TRUE;
		
		if (gotresponse) if (lib_strlen(bf)==0) ishead=0;
	}
	imsg("End of headers? %d \n",ishead);
	if (code==302 || code==301) if (!done_redirect)  {
		imsg("Redirect Location: (%s)\n",location);
		done_redirect = TRUE; 
		// no idea how a redirect could nicely work so need to see another one...
		
	}
//	if (code>=300) {
//		tcp_free(&c);
//		return FALSE;
//	}
	if (chunked) { // added by chrisp, alternate http protocol used by some http servers.
		int chunk;
		imsg("Trying to read in 'chunked' mode... \n");
		for (;TRUE;) {
			if (!tcp_readline(c,bf,BFSZ-1)) break;
			chunk = hex_to_ulong(bf);
			imsg("tcp: Reading chunk of %d bytes (%s)\n",chunk,bf);
			if (chunk==0) break; 
			for (;chunk>0;) {
				int w = chunk; 
				if (w>BFSZ-2) w = BFSZ - 2;
				count = tcp_read_timed(c,bf,w, tout*1000);
				if (count<=0) break;
				bf[count] = 0;
				cb(param,bf,count);
				got += count;
				chunk -= count; 
			}
			count = tcp_read_timed(c,bf,2, tout*1000); // read trailing crlf 
		}
	} else {
		for (;downloadsize>0;) {
			count = tcp_read_timed(c,bf,BFSZ-2, tout * 1000);

			if (count<=0) {
				lpcat(response,BFSZ-1," - channel closed %s",tcp_error(c,tmp3));
				break;
			}
			got += count;
			if (count>0) {
				bf[count] = 0;
				cb(param,bf,count);
				downloadsize -= count;
			}
		}
	}
	tcp_close_nice(c);
	tcp_free(&c);
	imsg("Finished %s\n",response);
	if (code>=300 || code<200) {
		return FALSE;
	}
	if (failed) return FALSE;
	return TRUE;

}
int s3_list_buckets(char *key,char *secret, char *product_token, char *user_token, int (*cb)(void *param, char *bf, int sz), void *param, char *response)
{
	printf("s3_list_buckets: key (%s) secret (%s)\n",key,secret);
	return s3_verb("GET","s3.amazonaws.com","/",key,secret,product_token,user_token,cb,param,response,NULL,0,0);	
}



/*

PUT /ObjectName HTTP/1.1
Host: BucketName.s3.amazonaws.com
Date: date
Authorization: signatureValue
Content-Length: 



  */

int s3_cb(void *a, char *bf, int len)
{
	char tmp[BFSZ];
	ncpy(tmp,bf,BFSZ-1);
	if (len<BFSZ) tmp[len] = 0;
	imsg("s3 callback %d (%s)\n",len,tmp);
	return TRUE;
}
int s3_cb_file(void *a, char *bf, int len)
{
	FILE *f;
	f = (FILE *) a;
	fwrite(bf,1,len,f);
	return TRUE;
}
int s3_put_file(char *key,char *secret, char *product_token, char *user_token, char *bucket, char *fname, char *remotename, char *reason)
{
	char host[BFSZ];
	if (*remotename!='/') {
		strcpy(reason,"remote file name must begin with slash");
		return FALSE;
	}	
	lprintf(host,BFSZ-1,"%s.s3.amazonaws.com",bucket);

	return s3_verb("PUT",host,remotename,key,secret,product_token,user_token,s3_cb,NULL,reason,fname,0,0);	
}
int s3_delete_file(char *key,char *secret, char *product_token, char *user_token, char *bucket, char *remotename, char *reason)
{
	char host[BFSZ];
	char fixname[BFSZ];
	if (*remotename!='/') {
		lprintf(fixname,BFSZ-1,"/%s",remotename);
		remotename = fixname;
	}	
	lprintf(host,BFSZ-1,"%s.s3.amazonaws.com",bucket);

	return s3_verb("DELETE",host,remotename,key,secret,product_token,user_token,s3_cb,NULL,reason,NULL,0,0);	
}
int s3_get_file(char *key,char *secret, char *product_token, char *user_token,  char *bucket, char *fname, char *remotename, int from, int len, char *reason)
{
	char host[BFSZ];
	FILE *f;
	int r;
	lprintf(host,BFSZ-1,"%s.s3.amazonaws.com",bucket);
	f = fopen(fname,"wb");
	if (f==NULL) {
		strcpy(reason,"Unable to write to file");
		return FALSE;
	}
	r = s3_verb("GET",host,remotename,key,secret,product_token,user_token,s3_cb_file,f,reason,fname,from,len);	
	fclose(f);
	return r;
}
// int s3_cb_file(void *a, char *bf, int len)
int s3_get_file_cb(char *key,char *secret, char *product_token, char *user_token, char *bucket, char *remotename, int from, int len, char *reason, int (*cb)(void *a, char *bf, int len), void *param)
{
	char host[BFSZ];
	int r;
	lprintf(host,BFSZ-1,"%s.s3.amazonaws.com",bucket);
	r = s3_verb("GET",host,remotename,key,secret,product_token,user_token,cb,param,reason,NULL,from,len);	
	return r;
}

int s3_create_bucket(char *key,char *secret, char *product_token, char *user_token,  char *bucket, char *reason, char *response)
{
	char host[BFSZ];
	int r;
	FILE *f;
	char *remotename = "/";
	strcpy(response,"");
	lprintf(host,BFSZ-1,"%s.s3.amazonaws.com",bucket);
	f = fopen("s3.response","wb");
	r = s3_verb("PUT",host,remotename,key,secret,product_token,user_token,s3_cb_file,f,reason,NULL,0,0);	
	if (f) fclose(f);
	if (!r) {
		ncpy_file(response,"s3.response",BFSZ-1);
	}
	return r;
	//return s3_verb("PUT",host,remotename,key,secret,product_token,user_token,s3_cb_file,NULL,reason,NULL,0,0);	
}
int s3_list_path(char *key,char *secret, char *product_token, char *user_token, char *bucket, char *fname, char *prefix, char *reason, char *marker, int listall, int max)
{
	char host[BFSZ];
	FILE *f;
	char remotename[BFSZ];
	int r;
	int len;
	lprintf(host,BFSZ-1,"%s.s3.amazonaws.com",bucket);
	f = fopen(fname,"wb");
	if (f==NULL) {
		strcpy(reason,"Unable to write to file");
		return FALSE;
	}
	// note, we need a slash on the 'end' but not on the 'start' of the prefix...
	if (*prefix=='/') prefix++;
	len = lib_strlen(prefix);
	if (len) if (prefix[len-1]!='/') {
		prefix[len++] = '/';
		prefix[len] = 0;
	}
	lprintf(remotename,BFSZ-1,"/?prefix=%s&delimiter=/",prefix);
	if (listall) 	lprintf(remotename,BFSZ-1,"/?prefix=%s",prefix);
	if (marker!=NULL) lpcat(remotename,BFSZ-1,"&marker=%s",marker);
	if (max>0) lpcat(remotename,BFSZ-1,"&max-keys=%d",max);

	//lprintf(remotename,BFSZ-1,"/",prefix);
	r = s3_verb("GET",host,remotename,key,secret,product_token,user_token,s3_cb_file,f,reason,fname,0,0);	
	fclose(f);
	return r;
}
int s3_list_buckets_file(char *key,char *secret, char *product_token, char *user_token, char *fname, char *reason)
{
	char host[BFSZ];
	FILE *f;
	int r;
	lprintf(host,BFSZ-1,"s3.amazonaws.com");
	f = fopen(fname,"wb");
	if (f==NULL) {
		strcpy(reason,"Unable to write to file");
		return FALSE;
	}
	r = s3_verb("GET",host,"/",key,secret,product_token,user_token,s3_cb_file,f,reason,fname,0,0);	
	fclose(f);
	return r;
}

int s3_activate_product(char *userkey, char *product_token, char *proxy, char *reason, char *usertoken, char *accesskey, char *secretkey) 
{
	int is_modified;
	Xml *xml,*x;
	char url[BFSZ];
	strcpy(usertoken,"");
	strcpy(accesskey,"");
	strcpy(secretkey,"");
	lprintf(url,BFSZ-1,"https://ls.amazonaws.com/?Action=ActivateDesktopProduct&ActivationKey=%s&ProductToken=%s&Version=2008-04-28",userkey,product_token);
	if (!tcp_download_file(url,proxy,"activate_xml.tmp",0,&is_modified,reason,TRUE)) {
		return FALSE;
	}
	xml = xml_read_file("activate_xml.tmp");	
	x = xml_find_child(xml,"ActivateDesktopProductResult","");
	if (x) {
		Xml *y;
		y = xml_find_child(x,"UserToken","");
		if (y!=NULL) ncpy(usertoken,y->text,BFSZ-1); else printf("Not found usertoken");
		y = xml_find_child(x,"AWSAccessKeyId","");
		if (y!=NULL) ncpy(accesskey,y->text,BFSZ-1);
		y = xml_find_child(x,"SecretAccessKey","");
		if (y!=NULL) ncpy(secretkey,y->text,BFSZ-1);
	} else xml_print(xml,0,NULL);
	xml_free(xml);
	// DELETE ACTIVATE_XML.TMP
	return TRUE;
}
/*
https://ls.amazonaws.com/
?Action=ActivateDesktopProduct
&ActivationKey=KEYFROMUSER
&ProductToken={ProductToken}FROMPRODUCT
&Version=2008-04-28

<ActivateDesktopProductResponse>
   <ActivateDesktopProductResult>
      <UserToken>
         {UserToken}AAAHVXNlclRrbgfOpSykBAXO7g/zG....[long encoded token]...
      </UserToken>
      <AWSAccessKeyId>
         YSJ4L29BRHP0V8J4A25
      </AWSAccessKeyId>
      <SecretAccessKey>
         kUdrlXJtn55OI/L3EHMGD/aVmHEXAMPLEKEY
      </SecretAccessKey>
   </ActivateDesktopProductResult>
   <ResponseMetadata>
      <RequestId>
         cb919c0a-9bce-4afe-9b48-9bdf2412bb67
      </RequestId>
   </ResponseMetadata>
</ActivateDesktopProductResponse>

*/
