#ifdef __BORLANDC__
#pragma warn -8058
#pragma warn -8066
#pragma warn -8060
#pragma warn -8080
#endif
#include <stdio.h>
#include <ctype.h>
#include <stdlib.h>
#include <stdarg.h>
#include <string.h>
#include <errno.h>
#include <sys/types.h>
#include <sys/stat.h>
#include "lib.h"
#define DMID 20
#include "debug.h"
#include "miconv.h"
int mime_encode_base64_bad(char *xin, int inlen, char *xout, int outlen);
#ifdef TESTING
#define DMID 5
#include "dmalloc.h"
#endif

#include "rstring.h"
#include "mime.h"

#ifndef FALSE
#define FALSE  0
#endif
#ifndef TRUE
#define TRUE (!FALSE)
#endif

extern int imsg(const char * arg_list, ...);

char *mime_fgets(char *bf, int bfsz, Mime *m, void *f);
char mime_hexbyte(char *s);

#ifdef TESTING
int main(int argc, char *argv[])
{
	FILE *f;
	char raw[BFSZ];
	char bin[BFSZ];
	Mime *m=NULL;
	int k=TRUE;
	int len;

	d_malloc_init(printf);
	mime_new(&m);
	f = fopen(argv[1],"rb");
	strcpy(m->part,"1");
	for (;k;) {
		k = mime_line(m,f,raw,bin,&len,BFSZ-1);
		switch (k) {
		case MIME_END: break;
		case MIME_HEADER:
			printf("Header: %s %s",m->part,raw);
			break;
		case MIME_BODY:
			printf("Body: %s %s",m->part,raw);
			break;
		case MIME_BOUNDARY:
			printf("Boundary: %s %s",m->part,raw);
			break;
		}
	}
	fclose(f);
	mime_free(&m);
	d_check();
	return 0;
}

#endif
int mime_new_real(Mime **m, char *file, int line)
{
	*m = d_callocid(DMID,1,sizeof(Mime),file,line); // TRACK MIME_NEW PROPERLY...
	(*m)->inhead = TRUE;
	(*m)->partid[0] = 1;
	(*m)->partlvl = 1;
	strcpy((*m)->part,"");		// QQQBUG ""
	return TRUE;
}
// Better non rfc compliant decoding, Currently only used by surgeweb
void mime_robust_decode(Mime *m)    
{
    m->rdecode = rstring_new2(BFSZ*2);
}
void mime_robust_decode_reset(Mime *m)
{
	if(!m->rdecode) return;
	// Hack for malformed mime messages to make sure robust mime decode
	// does not bleed across mime parts 
	m->rdecode->length = 0;
}
int mime_save_file(FILE *f)
{
	FILE *fout;
	char bf[BFSZ];
	fout = fopen("mime.crashing","wb");
	if (fout==NULL) return FALSE;
	fseek(f,0,SEEK_SET);
	for (;!feof(f);) {
		if (fgets(bf,BFSZ-1,f)==NULL) break;
		fprintf(fout,"%s",bf);
	}
	fclose(fout);
	return FALSE;
}

int (*emsg_fn)(const char * arg_list, ...);


void mime_set_emsg(int (*vemsg)(const char * arg_list, ...))
{
	emsg_fn=vemsg;
}


char *mime_fgets9(char *bf, int bfsz, Mime *m, void *f);
char *mime_fgets(char *bf, int bfsz, Mime *m, void *f)
{
	char *r = mime_fgets9(bf, bfsz, m, f);
	if (m->unstuff) {
		if (*bf=='.') {
			safe_memmove(bf,bf+1,lib_strlen(bf),bf,BFSZ-1);
		}
	}
	return r;

}
int mime_set_unstuff(Mime *m, int x)
{
	m->unstuff = x;
	return TRUE;
}

char *mime_fgets9(char *bf, int bfsz, Mime *m, void *f)
{
	char *s = m->mem_at;
	char *start=s;
//	char *sold;
	int len;
	char *end = m->mem + m->mem_len;
	bfsz--;
	if (m->mem==NULL) {
		if (m->read) return m->read(bf,bfsz,f);
		return fgets(bf,bfsz,f);
	}
	if (s==NULL) {
		if (emsg_fn) emsg_fn("mime failing as mem_at was null\n");
		mime_save_file(f);
		lib_abort();
	}
	strcpy(bf,"");
//	sold = s;
	for (;s<end;s++) {
		if (s && *s=='\n') {
			len = (int)(s - start + 1);
			if (bfsz>len) bfsz = len;
			ncpy(bf,start,bfsz);
			m->mem_at += len;
			return bf;
		}
	}

	if (start>=end) return NULL;
	len = (int) (end-start + 1);

/* not sure about this... 
	if (len>30) { // If no 'newlines' then try 'cr' instead...
		for (s=sold;s<end;s++) {
			if (*s=='\r') {
				len = s - start + 1;
				if (bfsz>len) bfsz = len;
				ncpy(bf,start,bfsz);
				m->mem_at += len;
				return bf;
			}
		}
	}
	len = end-start + 1;
*/
	if (bfsz>len) bfsz = len;
	ncpy(bf,start,bfsz);
	m->mem_at += len;
	return bf;
}

int mime_setfn(Mime *m, char * (*read)(char *, int, void *))
{
	if (!m) return FALSE;
	m->read = read;
	return TRUE;
}
int mime_setmem(Mime *m, char *mem, int len)
{
	m->mem = mem;
	m->mem_len = len;
	m->mem_at = mem;
	return TRUE;
}
int mime_free(Mime **m)
{
	Bound *b,*bnext;
	for (b=(*m)->blist;b!=NULL;b=bnext) {
		bnext = b->next;
		free(b);
	}
	(*m)->blist = NULL;
    if((*m)->rdecode){
        rstring_free(&(*m)->rdecode);
        (*m)->rdecode = NULL;
    }
	free(*m);
	*m = NULL;
	return TRUE;
}
/*

	Mime-Version: 1.0
	Content-Type: text/html
	Content-Transfer-Encoding: quoted-printable
Complex:
Content-Type: multipart/mixed;
 boundary="------------TCADZWoAA=00"


Part counting:

  1	aaa
  2	aaa
  2.1	 bbb
  2.2	 bbb
  2.2.1    ccc
  2.2.2    ccc
  2.3	 bbb
  3	aaa


  
12 07:58:52.12:Info:2936: content type (multipart/related;

	boundary=Apple-Mail-10-520981223;

	type="text/html"

)

*/
int mime_addbound(Mime *m, char *hdr)
{
	Bound *b;
	char *s;
	int isquote=FALSE;
	char bf[BFSZ];
	char bound[BFSZ];
	ncpy(bf,hdr,BFSZ-1);

	s = strstrnc(hdr,"boundary");
	if (s==NULL) return FALSE;
	s += lib_strlen("boundary");
	if (*s==' ') s++;
	if (*s!='=') return FALSE;
	s++;
	for (;*s!=0;s++) if (*s!=' ') break;
	for (;*s!=0;s++) if (*s!='\t') break;
	
	if (*s=='"') {isquote=TRUE; s++;}

	ncpy(bound,s,BFSZ-1);
	s = strchr(bound,'\t');	if (s!=NULL) *s = 0;
	s = strchr(bound,';');	if (s!=NULL) *s = 0;
	s = strchr(bound,'\n');	if (s!=NULL) *s = 0;
	s = strchr(bound,'\r');	if (s!=NULL) *s = 0;
	if (isquote) {
		s = strchr(bound,'"'); 
		if (s!=NULL) *s = 0;
	}

//  MW: message I received has spaces in boundary and RFC confirms spaces are actually allowed in MIME boundaries but not at end
//	s = strchr(bound,' ');	if (s!=NULL) *s = 0;
	s=bound+lib_strlen(bound)-1;
	while (*s==' ' && s>bound) {*s = 0; s--;}

	b = calloc(1,sizeof(Bound));
	ncpy(b->bound,bound,BFSZ-1);
//imsg("add boundary (%s)\n",bound);
	b->level = ++m->level;
	b->n = 0;
	b->next = m->blist;
	m->blist = b;

	return TRUE;
}

int mime_isbound(Mime *m, char *bf, char *part, int *pisend)
{
	Bound *b;
	char tmp[BFSZ];
	int isend = FALSE;
	int i;
	char lastchar;
	*pisend = FALSE;
	for (b=m->blist; b!=NULL; b=b->next) {
		int found=FALSE;
		if (strncmp(bf,"--",2)!=0) continue;		
		if (strncmp(bf+2,b->bound,lib_strlen(b->bound))==0) {
			char *pend  = (bf+2+lib_strlen(b->bound));
			lastchar = *(bf+2+lib_strlen(b->bound));
			if (m->needcrlf) {
				if (strncmp(pend,"\r\n",2)==0) found = TRUE;
				if (strncmp(pend,"--\r\n",4)==0) {found = TRUE; isend = TRUE;} 
			} else {
				if (lastchar==0 || lastchar == '\r' || lastchar == '\n' || lastchar==' '|| lastchar=='-') found=TRUE;
			}
			if (found) {
				if (strncmp(bf+2+lib_strlen(b->bound),"--",2)==0) {
						isend = TRUE;
				}
				b->n++;
				if (b!=m->cur[m->partlvl]) {
					if (m->partlvl<(MIME_PARTMAX-1)) {
						m->partlvl++;
						m->cur[m->partlvl] = b;
						m->partid[m->partlvl-1] ++;
					} else {
	//					log_startstop("mime: message did indeed exceed partlvl\n");
					}
				} else {
					m->partid[m->partlvl-1] ++;
				}
				if (isend) if (m->partlvl>0) m->partlvl--;
				*pisend = isend;

				strcpy(part,"");
				if(m->partlvl==1){
					ncat(part,"1",100);
				} else{
					for (i=1; i<m->partlvl;i++) {
						if (i>80) continue;
						sprintf(tmp,"%d",m->partid[i]);
						if (i>1) ncat(part,".",100);
						ncat(part,tmp,100);
					}
				}
				ncpy(m->lastbound,b->bound,MIME_BFSZ-1);
				return TRUE;
			}
		}
	}
	return FALSE;

}

int mime_charset(char *content, char *res, int bfsz)
{
	char *p = strstrnc(content,"charset=");
	if (!p) return FALSE;
	p += 8;
	if (*p == '\"') {p++; lib_copy_upto_any(&p,"\";\r\n",res,bfsz);}
	else lib_copy_upto_any(&p,";\r\n",res,bfsz);
	return TRUE;
}

/*
Content-Type: image/jpeg;
        name="BusScene.jpg"
Content-Transfer-Encoding: base64
Content-Disposition: attachment;
        filename="BusScene.jpg"
*/
int mime_set_encoding(Mime *m, char *tmp)
{
	if (!m || !tmp) return FALSE;
	if (strstrnc(tmp,"quoted-printable")!=NULL) m->encoding = MIME_QUOTED;
	if (strstrnc(tmp,"7bit")!=NULL) m->encoding = MIME_NONE;
	if (strstrnc(tmp,"base64")!=NULL) m->encoding = MIME_BASE64;
	return TRUE;
}
// Still seems a hack to do rfc 2231 decoding here as could potentially affect other headers?? but remains the
//   least impact change so has least change of bad side effects:
//Content-Type: application/pdf;
//	name*0*=us-ascii''Rue%20Cases%20Negres%20a%00%13%20La%00%19A%28C%29ducation%20et%20les%20deux%20portes%20A%20%20la%20l;
//	name*1*=ibertA%28C%29.pdf
//Content-Disposition: attachment;
//	filename*0*=utf-8''Rue%20Cases%20Negres%20%C3%A2%C2%80%C2%93%20L%C3%A2%C2%80%C2%99%C3%83%C2%A9ducation%20et%20les%20deux%20portes%20;
//	filename*1*=%C3%83%C2%A0%20la%20libert%C3%83%C2%A9.pdf
int mime_collapse_multi(Mime *m, char *xrest, char *bf)
{
	char tmp[BFSZ], tmp2[BFSZ], *s, *p, *q, rest[BFSZ], *upto=NULL;
	int count=0;
	if(strstrnc(xrest,"name*=")) {
		ncpy(bf,xrest,BFSZ-1);
		return TRUE;
	}

	// OK lets try full scale collapse
	ncpy(rest,xrest,BFSZ-1);
	ncpy(bf,"",BFSZ-1);
	s = mystrtok(rest,";",&upto);
	// for each possible line
	for (;s!=NULL;s=mystrtok(NULL,";",&upto),count++) {
		lib_trim_spaces(s,tmp);
		p=strstrnc(tmp,"name*");
		if(!p) break;

		// First iteration make sure the output string is correctly formatted
		if(count==0){
			ncpy(bf,rest,BFSZ-1);
			q=strstrnc(bf,"name*");
			if(!q) break;
			q+=5;
			*q='='; q++; *q=0;
		}

		// Now concat each of the continuation strings
		p=strchr(p,'=');
		if(!p || !*(p+1)) break;
		lib_trim_spaces(p+1,tmp2);
		lib_unquote(tmp2, tmp);
		//if(*(p-1)=='*'){}	
		ncat(bf,tmp,BFSZ-1);
	}
	return TRUE;
}




// Start to deal with *= encoding as per rfc 2231. To do properly requires significant changes 
// to use of mime sam, so just accept utf8 one liners here which should cover most situations for now
// eg: Content-Disposition: attachment; filename*="UTF-8''Nejnebezpecnejs%C3%AD vn.pps";
// STEP2 (July 2013): Also deal with multiline and other charsets somehow... see mime_collapse_multi
int mime_get_fname_ex(Mime *m, char *xrest)
{
	iconv_t ic = (iconv_t)-1;
	char *s,*p,tmp[BFSZ],bf[BFSZ], rest[BFSZ], charset[BFSZ], *r;
	size_t n,left;

	mime_collapse_multi(m, xrest, rest);
	if(strncmpnc(rest,"name*=",6)==0){
		s=rest+6;
	}else if(strncmpnc(rest,"filename*=",10)==0){
		s=rest+10;
	}else{
		// nope not extended syntax??
		return FALSE;
	}
	if(!*s) return FALSE;
	
	// Just in case charset / iconv detection fails, setup the filename buffer
	// 11/3/2022 lib_decode_percent_clean changed to lib_decode_percent to get surgeweb unmutilated (hopefully) filenames 
	lib_decode_percent(bf,s,BFSZ);
	ncpy(m->fname,bf,200);

	// OK lets see if we can make charset detection reliable
	ncpy(charset,s,BFSZ-1);
	p=strchr(charset,'\'');
	if(p==NULL || *p==0) return TRUE;	// valid exit condition

	*p=0;

	// Strip rest of charset definition
	s=strchr(s,'\'');
	if(!s || !*(s+1)) return FALSE;
	s++;
	s=strchr(s,'\'');
	if(!s || !*(s+1)) return FALSE;
	s++;

	if(strcmpnc(charset,"utf-8")==0){
		// no charset to change
		ncpy(tmp,s,BFSZ-1);
	} else {
		// Need to do iconv translation...
#ifndef NOICONV
		iconv_t ic = (iconv_t)-1;
		left = BFSZ;
		n=lib_strlen(s);
		r = tmp;
		ic = iconv_open("UTF-8",charset);
		if (ic == (iconv_t)-1) {
			imsg("iconv: %s (1)\n",strerror(errno));
			goto err;
		}
		if (iconv(ic,&s,&n,&r,&left) == (size_t)-1) {
			imsg("iconv: %s (2)\n",strerror(errno));
			goto err;
		}
		*r=0;
		iconv_close(ic);
		ic=NULL;
#else
		ncpy(tmp,s,BFSZ-1);
#endif
	}

	// 11/3/2022 lib_decode_percent_clean changed to lib_decode_percent to get surgeweb unmutilated (hopefully) filenames 
	lib_decode_percent(bf,tmp,BFSZ);

	ncpy(m->fname,bf,200);
	return TRUE;
err:
	if (ic != (iconv_t)-1) iconv_close(ic);
	return FALSE;
}

int mime_get_fname(Mime *m, char *xrest)
{
	char rest[BFSZ];
	char *s;
	char bf2[BFSZ];
	int found=FALSE;
	ncpy(rest,xrest,BFSZ-1);
	mime_clean_line_breaks(rest);
	//strlwr(rest); RMH: removed, name could be encoded, eg name="=?iso-8859-8-i?B?7vLp6e8g+uv56ejp7SAwODEuanBn?="
	s = strstrnc(rest,"name=");
	if(s){
		s+=5; found=TRUE;
	} else {
		s = strstrnc(rest,"name =");
		if(s){
			s+=6; found=TRUE;
		}
	}

	if (!found){
		// Marijn: must also deal with *= encoding as per rfc 2231
		s = strstrnc(rest,"name*");
		if(s){ 
			return mime_get_fname_ex(m,s);
		}
		return FALSE;
	}

	while(sisspace(*s)) s++;
	if (*s == '"') {
		s++;
		ncpy(m->fname,s,200);
		s = strchr(m->fname,'"');
		if (s!=NULL) *s = 0;
	} else {	// chrisp changed this ffunction so it trims spaces off the end (but not in the middle)...
		ncpy(m->fname,s,200);
		s=m->fname;
		for (;*s!=0;s++) {
			if (*s==';') break;
			if (*s=='\r') break;
			if (*s=='\n') break;
		}
		if (*s!=0) *s=0;
		lib_trim_spaces_end(m->fname);
	}

	mime_header_decode(m->fname,bf2,BFSZ-1);
	ncpy(m->fname,bf2,BFSZ-1);


	return TRUE;
}
void next_line(Mime *m, void *f)
{
	if (mime_fgets(m->nextline,BFSZ-1,m,f)==NULL) return;
	m->gotline = TRUE;
}
int mime_line(Mime *m, void *f, char *raw, char *bin, int *len, int bfsz)
{
	char bf[BFSZ];
	char tmp[BFSZ],*s;

	raw[0] = 0;
	bin[0] = 0;
	*len = 0;

	if (m->isend) return MIME_END;
	if (!m->inhead) if (m->one_level) m->done_level = TRUE;
	if (m->islost) {
		ncpy(raw,m->lost,bfsz-1);
		ncpy(m->lost,"",10);
		m->islost = FALSE;
		return MIME_HEADER;
	}
	
	if (m->gotline) {
		ncpy(bf,m->nextline,BFSZ-1);
		m->gotline = FALSE;
	} else {
		if (mime_fgets(bf,BFSZ-1,m,f)==NULL) {
			return MIME_END;
		}
	}
	ncpy(raw,bf,bfsz-1);
//imsg("mime: INHEAD=%d %s\n",m->inhead,raw);
	if (m->inhead) {
		if ( (strcmp(bf,"\r\n")==0) || (strcmp(bf,"\n")==0) || (strcmp(bf,"")==0)) {
			m->inhead = FALSE;
			if (*len<0) *len = 0;  if (*len>=bfsz) *len = bfsz-1;
			if (!m->blist && !*m->part) strcpy(m->part,"1");	// QQQBUG 
			mime_robust_decode_reset(m);
			return MIME_HEADER_END;
		}
		if (m->loose_header) if ( (strcmp(bf," \r\n")==0) || (strcmp(bf," \n")==0) || (strcmp(bf," ")==0)) {
			m->inhead = FALSE;
			if (*len<0) *len = 0;  if (*len>=bfsz) *len = bfsz-1;
			if (!m->blist && !*m->part) strcpy(m->part,"1");	// QQQBUG
			mime_robust_decode_reset(m);
			return MIME_HEADER_END;
		}
		if (m->append_lines) {
			mime_header(m,f,bf,tmp);
			ncpy(bf,tmp,BFSZ-1);
			ncpy(raw,bf,bfsz-1);
		}
		if (strncmpnc(bf,"Content-Type:",13)==0) {
			s=bf+13; if(*s==' ') s++;
			if (m->append_lines) ncpy(m->content,s,BFSZ-1);
			else mime_header(m,f,s,m->content);
			m->ismime = TRUE;
//imsg("content type (%s)\n",m->content);
			if (m->one_level && m->done_level) {
				; // we don't add boundaries once we've done the main header of this section...
			} else {
				mime_addbound(m,m->content);	
			}
			mime_get_fname(m,m->content);
			if (*len<0) *len = 0;  if (*len>=bfsz) *len = bfsz-1;
			return MIME_CONTENT;
		} else if (strncmpnc(bf,"Content-Transfer-Encoding:",26)==0) {
			s=bf+26; if(*s==' ') s++;
			if (m->append_lines) ncpy(tmp,s,BFSZ-1);
			else mime_header(m,f,s,tmp);
			m->ismime = TRUE;
			strlwr(tmp);
			mime_set_encoding(m,tmp);
//printf("Encoding (%s) %d\n",encoding,m->encoding);
			mime_get_fname(m,tmp);
			//Content-ID: <part1.07000408.09030308@star>
		} else if (strncmpnc(bf,"Content-ID: ",11)==0) {
			s=bf+11; if(*s==' ') s++;
			lib_fix_email(s,m->content_id);
		} else if (strncmpnc(bf,"Content-Disposition:",20)==0) {
			s=bf+20; if(*s==' ') s++;
			if (m->append_lines) ncpy(tmp,s,BFSZ-1);
			else mime_header(m,f,s,tmp);
			mime_get_fname(m,tmp);
		}
		if (*len<0) *len = 0;  if (*len>=bfsz) *len = bfsz-1;
		mime_robust_decode_reset(m);

		return MIME_HEADER;
	} else {
        if (m->rdecode){
            // Allow rdecode (in function mime_tobin) to flush its buffer
            if (bf[0]=='-' && m->rdecode->length>0 && m->rdecode->length<m->rlen_prev){
                m->gotline = true;
                ncpy(m->nextline,bf,MIME_BFSZ-1);
                bf[0] = 0;
                raw[0] = 0;
            }
            m->rlen_prev = m->rdecode->length;
        }
        
		if (bf[0]=='-') {
			int isend = FALSE;

			if (mime_isbound(m,bf,m->part,&isend) || (m->resync && bf[1]=='-')) {
				m->bound_end = isend;
				/* Found next part */
				if (!isend) m->inhead = TRUE;
				m->encoding = MIME_NONE;
				strcpy(m->content,"");
				strcpy(m->content_id,"");
				strcpy(m->fname,"");
				if (*len<0) *len = 0;  if (*len>=bfsz) *len = bfsz-1;
				return MIME_BOUNDARY;
			}
		}
        
		mime_tobin(m,raw,bin,len,bfsz); 
		
        
		// MW 16/8/2015: PROBABLY REMOVE SOMETIME: Superceeded by more capable rdecode (in mime_tobin) but  for
        // now leave in to avoid "change in behaviour" if rdecode not enabled (primarily surgeweb)
        if(!m->rdecode && m->encoding==MIME_BASE64 && *len==0 && lib_strlen(raw)>800){
			// yuck, non RFC compliant, LONG base64 broken across lines? lets try fixing
			next_line(m,f);

			if(m->gotline && ((m->nextline[0]==' ') || (m->nextline[0]=='\t')) && lib_strlen(m->nextline)<800) {
				ncpy(tmp,raw+800,BFSZ-1);
				lib_strip_crlf(tmp);
				ncat(tmp,m->nextline+1,BFSZ-1);
				ncpy(m->nextline,tmp,BFSZ-1);
				raw[800]='\0';
//imsg("mime: NEXTLINE %s\n",m->nextline);
				mime_tobin(m,raw,bin,len,bfsz);
			}
		}
        
		if (*len<0) *len = 0;  if (*len>=bfsz) *len = bfsz-1;
		if (*len>0) bin[*len] = 0;
		return MIME_BODY;
	}
	// Code can't get here I think. 
	*len = 0;
	return MIME_END;
}
int mime_header(Mime *m, void *f, char *bf, char *hdr)
{
	char *line;
	int length=0;
	ncpy(hdr,bf,BFSZ-1);
	for (;TRUE;) {
		if (mime_fgets(m->nextline,BFSZ-1,m,f)==NULL) break;
//imsg("mime:2  %s\n",m->nextline);
//		if (fgets(m->nextline,BFSZ-1,f)==NULL) break;
		m->gotline = TRUE;
		line = m->nextline;
		if ( (strcmp(line,"\r\n")==0) || (strcmp(line,"\n")==0) || (strcmp(line,"")==0)) {
			return TRUE;
		}
		if ((line[0]==' ') || (line[0]=='\t')) {
			if(length+lib_strlen(line)>BFSZ-20) return TRUE;
			ncat(hdr,line,BFSZ-1);
			if (m->return_all) {
				if (!m->islost) strcpy(m->lost,"");
				ncat(m->lost,line,MIME_BFSZ-1);
				m->islost = TRUE;
			}
			m->gotline = FALSE;
			length+=lib_strlen(line);
//			MW: Check removed 25-2-09 (and rest of function modified)
//			if (max<4) continue;
			continue;
		}
		return TRUE;
	}
	m->isend = TRUE;
	return TRUE;
}
int mime_tobin(Mime *m, char *raw, char *bin, int *len,int bfsz)
{
    char *t;
    *len = 0;

	switch (m->encoding) {
	case MIME_NONE:
		ncpy(bin,raw,bfsz-1);
		*len = lib_strlen(bin);
        /*if (bin[0]=='.') {	 
                 safe_memmove(bin,bin+1,*len,bin,bfsz-1);  // chrisp added code to undotstuff.	  'bin' shouldn't have dot stuffed data in it...
                 (*len) -= 1;	 
        }*/
		break;
	case MIME_BASE64:
            
        // Apply a more robust mime decoding scheme that will deal with malformed mime encoded data
        // ie short and not split on mime boundaries or longer than BFSZ as found in real mail
        // Only used by surgeweb for now
        if (m->rdecode) {
            char xx[BFSZ];
            int prcc_len = 160;    // Process in small chunck (99% of the time will be 80 char as per RFC
			int have_length, use_length;
            lib_trim_spaces2(raw, xx);
            lib_strip_crlf_atend(xx);
            lib_trim_end(xx);
            rstring_cat(m->rdecode, xx);
            have_length = m->rdecode->length;
            use_length = (have_length / 4) * 4;
            if (use_length>prcc_len) use_length = prcc_len;
            ncpy2(raw, m->rdecode->data, use_length, BFSZ-1);
            rstring_consume(m->rdecode, use_length);
        }
            
		*len = mime_decode_base64(raw,bin,bfsz-1);
		break;
	case MIME_QUOTED:
		t = raw;
		//if (*t=='.') t++; //undotstuff.
		*len = mime_decode_quoted(t,bin,bfsz-1);		
//if (t!=raw) printf("Decoded (%s)\n",bin);
		break;
	}
	return TRUE;
}
static char	vec[] =
	"ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789+/=";
int mime_decode_base64(char *buf, char *out, int bfsz)
{
	int	i, num, len, j;
	int nout=0;
	long	d, val;
	char	nw[4], *p, *c;

	len = lib_strlen(buf)-1;
	if (len<0) return 0;
	if (buf[len] == '\n') len--;
	if (len<0) return 0;
	if (buf[len] == '\r') len--;
	if (len<0) return 0;
	if (buf[len] == '\n') len--;
	if (len<0) return 0;
	for (i = 0; i < len; i += 4)	{
		val = 0;
		num = 3;
		c = buf+i;
		if (c[2] == '=')
			num = 1;
		else if (c[3] == '=')
			num = 2;
		for (j = 0; j <= num; j++){
			if (!(p = strchr(vec, c[j]))) {
				return 0;
			}
			d = (int) (p-vec);
			d <<= (3-j)*6;
			val += d;
		}
		for (j = 2; j >= 0; j--){
			nw[j] = val & 255;
			val >>= 8;
		}
		if (nout+num>=bfsz) break; // limit output
		if (num>0) memcpy(out+nout,nw,num);
		nout += num;
	}
	return nout;
}
int mime_encode_quoted(char *in, int inlen, char *out, int outlen)
{
	int prcc;
	return mime_encode_quoted_real(in, inlen, out, outlen, &prcc);
}
int mime_encode_quoted_real(char *in, int inlen, char *out, int outlen, int *prcc)
{

	char *start=out;
	int i;
	/* returns actual length */
	for (i=0; i<inlen; i++) {
		if (sisprint((unsigned char)in[i]) && in[i] != '=') {
			*out++ = in[i];
		} else {
			*out++ = '=';
			sprintf(out,"%02x",(unsigned char) in[i]);
			out+=2;
			(*prcc)++;
		}
		if (out-start>(outlen-10)) break;
	}
	start[out-start] = 0;
	return (int) (out-start);
}
#define HEXCHARS "0123456789ABCDEFabcdef"
int mime_decode_quoted(char *s, char *xout, int bfsz)
{
	int i;
	char *out=xout;
	for (i=0; i<bfsz-2 && *s!=0;i++,s++) {
		if (out-xout>bfsz-2) break; // Stop it in time! 
		if (*s<0) {
			*out++ = *s++;
			*out++ = *s;
		} else if (*s=='=') {
			if (strncmp(s,"=\r\n",3)==0) { /* SOFT crlf, remove */
				s += 2;
				continue;
			}
			if (s[1]==0) break;
			if (s[2]==0) break;
			if (strchr(HEXCHARS,s[1])==NULL || strchr(HEXCHARS,s[2])==NULL){
				*out++ = *s;
				continue;
			}	
			*out++ = mime_hexbyte(s+1);
			s+=2;
		} else *out++ = *s;
	}

	*out = 0;
	return (int) (out-xout);
}
char mime_hexbyte(char *s)
{
		char bf[3];
        int x;
        strncpy(bf,s,2);
        bf[2] = 0;
        sscanf(bf,"%x",&x);
        return (char) x;
}
static char alphabet[] = "ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz"
                  "0123456789+/";
#ifdef TESTINGmime
int hex_encode(FILE *fin, FILE *fout);
main()
{
	FILE *fin,*fout;
	fin = fopen("in.txt","rb");
	fout = fopen("out.txt","w");
	hex_encode(fin,fout);
	return 0;
}
#endif

int mime_encrypt_string(char *in, char *out)
{
	strcpy(out,"$%$%");
	mime_encode_base64_bad(in,lib_strlen(in)+2,out+4,BFSZ-9);
//	printf("Mime encrypted (%s) to (%s)\n",in,out);
	return true;
}
int mime_decrypt_string(char *in, char *out)
{
	ncpy(out,in,BFSZ-1);
//	printf("Decrypt (%s)\n",in);
	if (strncmp(in,"$%$%",4)!=0) return true; // not encrypted... 
	if (lib_strlen(in+4)==0) {
		strcpy(out,"");
		return true;
	}
	mime_decode_base64(in+4,out,BFSZ-1);
//	printf("Decrypted string (%s)\n",out);
	return true;
}


int mime_encode_base64_bad(char *xin, int inlen, char *xout, int outlen)
/*
Warning - inlen must be one more than the length of the input.
e.g. mime_encode_base64_bad(txt,lib_strlen(txt)+1,bf,999)
*/

{
   int  index, shift, save_shift=0;
   int quit = 0;
   unsigned char blivit;
   unsigned long accum, value;
   char buf[BFSZ*2];


      shift = 0;
      accum = 0;
      index = 0;
      while ( (inlen>0) || (shift != 0) )  {
         if ( (inlen!=0) && ( quit == 0 ) ) {
		blivit = *xin++; inlen--;
		if ( inlen<=0 ) {
		       quit = 1;
		       save_shift = shift;
		       blivit = 0;
		}
         } else {
            quit = 1;
            save_shift = shift;
            blivit = 0;
         }

         if ( (quit == 0) || (shift != 0) )
         {
            value = (unsigned long)blivit;
            accum <<= 8;
            shift += 8;
            accum |= value;
         } /* ENDIF */

         while ( shift >= 6 )
         {
            shift -= 6;
            value = (accum >> shift) & 0x3Fl;
            blivit = alphabet[value];
			if (index>BFSZ*1.7) break;
            buf[index++] = blivit;
            if ( quit != 0 )  shift = 0;
         }
      }

      if ( save_shift == 2 ) {
         buf[index++] = '=';
         buf[index++] = '=';
      } else if ( save_shift == 4 ) {
         buf[index++] = '=';
      }

      buf[index] = 0;
      ncpy(xout,buf,index);
	return TRUE;
}

char *mime_strings[]={"MIME_END","MIME_HEADER","MIME_BODY","MIME_BOUNDARY","MIME_CONTENT","MIME_HEADER_END"};
void mime_log(char*txt, char*line, int k, int fn(const char * arg_list, ...))
{
	char bf[BFSZ];
	static int count=0;
	if (fn==NULL) return;	
	ncpy(bf,line,BFSZ-1);
	strip_eol(bf);
	fn("%s%s [%s] line count=%d", txt, bf, mime_strings[k],++count);
}

// Clean up remnants of MIME header linebreaks
// eg for headers like:
//	Content-Disposition: attachment;
//		filename="Mary John Award Nomination Guidelines 2009 -03 compatible
//		format.doc"

char *mime_clean_line_breaks(char *bf)
{
	char *pin,*pout,last='\0';

	if(!bf) return "";
	for(pin=pout=bf;*pin;pin++) {
		switch(*pin) {
			case '\r': 
			case '\n': 
				if(last!=' '){
					*pout=last=' '; 
					pout++;
				}
				while(*(pin+1)=='\r' || *(pin+1)=='\n' || *(pin+1)==' ' || *(pin+1)=='\t')
					pin++;
				break;

			default:
				*pout++=*pin;
				last=*pin;
				break;
		}
	}
	*pout='\0';
	return bf;
}



int word_add_boundary(Wordlist **w, char *hdr)
{
	char *s;
	int isquote=FALSE;
	char bound[BFSZ];

	s = strstrnc(hdr,"boundary");
	if (s==NULL) return FALSE;


	s += lib_strlen("boundary");
	if (*s==' ') s++;
	if (*s!='=') return FALSE;
	s++;
	for (;*s!=0;s++) if (*s!=' ') break;
	for (;*s!=0;s++) if (*s!='\t') break;
	
	if (*s=='"') {isquote=TRUE; s++;}

	ncpy(bound,s,BFSZ-1);
	s = strchr(bound,'\t');	if (s!=NULL) *s = 0;
	s = strchr(bound,';');	if (s!=NULL) *s = 0;
	s = strchr(bound,'\n');	if (s!=NULL) *s = 0;
	s = strchr(bound,'\r');	if (s!=NULL) *s = 0;
	if (isquote) {
		s = strchr(bound,'"'); 
		if (s!=NULL) *s = 0;
	}

	s=bound+lib_strlen(bound)-1;
	while (*s==' ' && s>bound) {*s = 0; s--;}
//	printf("Added boundary (%s)\n",bound);
	word_add(w,bound);
	return TRUE;
}

int word_isbound(Wordlist *list, char *bf, int *pisend)
{
	Wordlist *w;
	int isend = FALSE;
	char lastchar;
	*pisend = FALSE;
	for (w=list; w!=NULL; w=w->next) {
		if (strncmp(bf,"--",2)!=0) break;		
		if (strncmp(bf+2,w->word,lib_strlen(w->word))==0) {
			lastchar = *(bf+2+lib_strlen(w->word));
			if (lastchar==0 || lastchar == '\r' || lastchar == '\n' || lastchar==' '|| lastchar=='-') {
				if (strncmp(bf+2+lib_strlen(w->word),"--",2)==0) isend = TRUE;
				*pisend = isend;
				return TRUE;
			}
		}
	}
	return FALSE;
}
int mime_decode_file(char *fname, char *big, int maxbig)
{
	Mime *m=NULL;
	char raw[BFSZ];
	char bin[BFSZ];
	int len;
	int bigsz=0;
	FILE *f;
	int k;

	ncpy(big,"",maxbig);
	mime_new(&m);
	f = fopen(fname,"rb");
	if (f==NULL) {
		mime_free(&m);
		printf("Open (%s) failed %s\n",fname,strerror(errno));
		imsg("mime: Open (%s) failed %s\n",fname,strerror(errno));
		return FALSE;
	}
	setvbuf(f,NULL,_IOFBF,32000);
	for (k=TRUE;k;) {
		k = mime_line(m,f,raw,bin,&len,BFSZ-1);
		if (m->inhead) {
			if (strncmp(raw,"Subject: ",9)==0) {
				ncat(big,raw+9,BFSZ-1);
				ncat(big," ",BFSZ-1);
				bigsz = lib_strlen(big);
			}
			/*
			if (strncmpnc(raw,"X-Originating-IP: ",18)==0) { // experiment...
				char tmp[BFSZ];
				char *s;
				ncpy(tmp,raw+18,BFSZ-1);
				ncat(big,"ipz",BFSZ-1);
				for (s=tmp;*s!=0;s++) {
					if (*s=='.') *s = 'z';
					if (sisdigit(*s)) *s = 'a' + *s - '0';
				}
				ncat(big,tmp,BFSZ-1);
				ncat(big," ",BFSZ-1);
				bigsz = lib_strlen(big);
			}
			*/
		}
//printf("mime %d %d %s\n",k,bigsz,raw);
		switch (k) {
		case MIME_END:
			break;
		case MIME_HEADER:
			break;
		case MIME_CONTENT:
			break;
		case MIME_HEADER_END:
			break;
		case MIME_BODY:
			if ((len+bigsz+BFSZ+1000)<maxbig) {
				safe_memcpy(big+bigsz,bin,len,big,maxbig);
				bigsz = bigsz + len;
				big[bigsz] = 0;
			} 
			break;
		case MIME_BOUNDARY:
			break;
		}
	}
	fclose(f);

	mime_free(&m);
	return TRUE;
}

int mime_strip_invalid(char *bf, int n)
{
	int total=n, i;
	char *p,*s;
	p=s=bf;
	if(n>10000){
		if (emsg_fn) emsg_fn("mime_strip_invalid sanity check failed n=%d", n);
		n=10000;
	}

	for(i=n; i>0; i--){
		if(*s=='\0' || *s=='\r' || *s=='\n') {
			s++;
			total--;
			continue;
		}
		*p++ = *s++;
	}

	return total;
}

extern const unsigned char charmap[];
char *mime_header_inplace(char *bf)
{
	char tmp[BFSZ];
	mime_header_decode(bf,tmp,BFSZ-1);
	ncpy(bf,tmp,BFSZ-1);
	return bf;
}

char *mime_header_decode(char *header, char *res, int len)		//moved from sw_header_decode  (also cf wm_header_decode)
{
#ifndef NOICONV
	iconv_t ic = (iconv_t)-1;
#endif
	char *p,*e,*s,*r = res;
	char *bf1,bf1_s[BFSZ+2]; // chrisp paranoia, just adding 2 to buffer size in both cases...
	char *bf2,bf2_s[BFSZ+2];
	int bfsz;
	size_t n,left;


	// Use dynamic buffers if needed
	if (len>BFSZ-1){
		bfsz=len+1; bf1=calloc(1,bfsz+2); bf2=calloc(1,bfsz+2); 
	}else{
		bfsz=BFSZ; bf1=bf1_s; bf2=bf2_s;
	}

	if (!header) goto err;

	left = bfsz;
	for(p = ncpy(bf1,header,bfsz-1); p; ) {
		e = strstr(p,"=?");
		if (e) *e = 0;
		if (left<=0) break; // chrisp addition to fix crash
		ncpy(r,p,(int) left-1); // crash here with -1 as length
		n = lib_strlen(p);
		left -= n;
		r += n;

		if (!e){
			// Normal codepath when no decoding to do...
			break;
		}

		// Decoding required...
		//=?iso-8859-1?q?...?=
		e += 2;

		//iso-8859-1?q?...?=
		s = strchr(e,'?');
		if (!s) goto err;
		*s++ = 0;

#ifndef NOICONV
		ic = iconv_open("UTF-8",e);
		if (ic == (iconv_t)-1) {
			imsg("iconv: %s (1)\n",strerror(errno));
			goto err;
		}
#endif
		//q?...?=
		p = strchr(s,'?');
		if (!p) goto err;
		*p++ = 0;

		//...?=
		e = strstr(p,"?=");
		if (!e) goto err;
		*e = 0;

		if (charmap[*s] == charmap['B']){
			n = mime_decode_base64(p,bf2,BFSZ);
			n = mime_strip_invalid(bf2, n);
		}
		if (charmap[*s] == charmap['Q']) {
			n = mime_decode_quoted(p,bf2,BFSZ-1);
			for(s = bf2; s-bf2 < (int)n; s++) {
				if (*s == '_') *s = ' ';
			}
			n = mime_strip_invalid(bf2, n);
		}
		bf2[n] = 0;
		s = bf2;

#ifdef NOICONV
		memcpy(r,s,n);
		r += n;
		left -= n;
#else
		if (iconv(ic,&s,&n,&r,&left) == (size_t)-1) {
			imsg("iconv: %s (2)\n",strerror(errno));
			goto err;
		}
		iconv_close(ic);
		ic=(iconv_t)-1;
#endif

		p = e + 2;
	}

	*r = 0;
	if (bf1!=bf1_s){free(bf1); free(bf2);}
	return res;
err:
#ifndef NOICONV
	if (ic != (iconv_t)-1) iconv_close(ic);
#endif
	if (header) ncpy(res,header,bfsz-1);
	else *res = 0;
	if (bf1!=bf1_s){free(bf1); free(bf2);}

	return res;
}

