/********************************************************************
 *                                                                  *
 *  MODULE    :  WVSEND.C                                           *
 *                                                                  *
 *  PURPOSE   : Send/attachment engine                              *
 *                                                                  *
 *  ENTRY POINTS: SendComposition(WndEdit *compWnd)                 *
 *                DoSend (int action)                               *
 *                CompleteSend ()                                   *
 *                ReadTextFileIntoEditWnd ()                        *
 *                                                                  *
 * Author: John S. Cooper (jcooper@planetz.com)                     *
 *   Date: Nov 28, 1993                                             *
 *                                                                  *
 *  Massive overhaul on oct 8, 1994 (jsc)                           *
 *   This is now the main post/mail engine, and now supports        *
 *   multi-attachments.  Changed name from wvattach to wvsend       *
 ********************************************************************/

/* 
 * $Id: wvsend.c 1.28 1997/03/18 19:17:07 dumoulin Exp $
 */

#include <windows.h>
#include <windowsx.h>
#include "wvglob.h"
#include "winvn.h"
#pragma hdrstop
#include <string.h>
#include <ctype.h>                              /* for isspace, isalnum, etc */
#include <stdlib.h>                             /* for _itoa */
#include <assert.h>

#include "sha1.h"		//cancel-key, cancel-lock

/*
 * Forward Declarations
 */
BOOL InitiateSend (WndEdit * compWnd);
void CompleteSend (WndEdit * compWnd, int action);
BOOL EndSendSection (WndEdit * compWnd);

BOOL GenerateMIMEVersion (TypTextBlock * header);
void GenerateMessageID(char *buf, int nPart, int nVersion = 0);
BOOL GenerateMIMEMultipartMixedHeader (TypTextBlock * textBlock);
BOOL GenerateMIMEPartialHeader (TypTextBlock * textBlock, int nSending, int nTotal);
BOOL GenerateMIMEContentHeader (TypTextBlock * textBlock, TypAttachment * thisAttach);
BOOL CalcNumParts (WndEdit * compWnd);
void GenerateSubject (WndEdit * compWnd);
void UpdateMessageID (WndEdit * compWnd);

BOOL SendOneLineWithLen (char *str, unsigned long len);
BOOL SendTextBlock (TypTextBlock * textBlock);
BOOL SendEditText (HWND hWnd);
unsigned long SendPartialEditText (HWND hWnd, unsigned long startByte, unsigned long maxBytes);
unsigned long SendPartialTextFile (TypAttachment * thisAttach, unsigned long startByte, unsigned long maxBytes);

#ifdef RFC1522
BOOL SendHeaderTextBlock (TypTextBlock * textBlock);
#endif


/*
 * Globals 
 */

#define ATTACH_NONE              0      /* state constants for DoSend */
#define ATTACH_START             1
#define ATTACH_FIRST_IN_THIS_WND 2
#define ATTACH_FIRST_IN_NEXT_WND 3
#define ATTACH_START_NEXT_SEND   4
#define ATTACH_MAIN              5
#define ATTACH_DONE              6
#define ATTACH_BEGIN_ATTACHMENT  7

int SendAttachNum;
TypTextBlock *MainHeader, *SectionHeader, *WorkBlock;
int SendingPart, SendNumParts;
int AttachmentState;
char origSubject[MAXHEADERLINE], attachmentID[MAXINTERNALLINE];
unsigned long SentNumBytes;

HFILE hMailLog, hPostLog;

/* To diagnose send/attachments by writing output to file instead of sending,
 * undef ENABLE_COMM_SEND, and activate mail/post logging 
 */
#ifndef _DEBUG
#define ENABLE_COMM_SEND
#else
#define ENABLE_COMM_SEND
//#undef ENABLE_COMM_SEND
#endif

/* ---------------------------------------------------------------------------
 * EncodingStrToType converts a string describing a coding type into the
 * internal numeric representation
 */

eEncodingType EncodingStrToType (const char *str)
{
	if (!_stricmp (str, "Base-64"))
		return CODE_BASE64;
	else if (!_stricmp (str, "UU"))
		return CODE_UU;
	else if (!_stricmp (str, "XX"))
		return CODE_XX;
	else if (!_stricmp (str, "Custom"))
		return CODE_CUSTOM;
	else if (!_stricmp (str, "yEnc"))
		return CODE_YENC;
	else if (!_stricmp (str, "None") ||
			!_stricmp(str, "Binary") || !_stricmp (str, "8Bit") || !_stricmp (str, "7Bit"))
		return CODE_NONE; /* Heirich 19960826 note:binary is not used yet,but in rfc defined */
	else
		return CODE_UNKNOWN;
}

const char * EncodingTypeToStr (eEncodingType Type)
{
	switch (Type)
	{
		case CODE_BASE64:
			return "Base-64";
			break;
		case CODE_UU:
			return "UU";
			break; 
		case CODE_XX:
			return "XX";
			break; 
		case CODE_CUSTOM:
			return "Custom";
			break;
		case CODE_YENC: 
			return "yEnc";
			break;
		case CODE_QP:
			return "Quoted-Printable";
			break;
		case CODE_8BIT:
			return "8bit";
			break;
		case CODE_7BIT:
			return "7bit";
			break;
		case CODE_BINARY:
			return "binary";
			break;
		//default:
		//case CODE_UNKNOWN:
		//case CODE_NONE:
		//	return "None";
		//	break;
	}
	return "None";

}


/* ---------------------------------------------------------------------------
 * Logging
 */
void AbortMailLog (HWND hWnd)
{
	_snprintf (str, MAXINTERNALLINE, "Mail log error in file %s.  Logging disabled.", MailLogFile);
	WVMessageBox (hWnd, str, "Logging Error", MB_OK | MB_ICONINFORMATION);
	MailLog = FALSE;
	if (hMailLog) 
	{
		_lclose (hMailLog);
		hMailLog = (HFILE) NULL;
	}
}
void AbortPostLog (HWND hWnd)
{
	_snprintf (str, MAXINTERNALLINE, "Post log error in file %s.  Logging disabled.", PostLogFile);
	MessageBox (hWnd, str, "Logging Error", MB_OK | MB_ICONINFORMATION);
	PostLog = FALSE;
	if (hPostLog) 
	{
		_lclose (hPostLog);
		hPostLog = (HFILE) NULL;
	}
}

void LogLine (HWND hWnd, char *str, int len)
{
	if (SendingMail && MailLog && hMailLog) 
	{
		if (len < 0)
			len = strlen(str);
		if (_lwrite (hMailLog, str, len) == HFILE_ERROR) 
		{
			AbortMailLog (hWnd);
		}
	}
	if (SendingPost && PostLog && hPostLog) 
	{
		
		if (len < 0)
			len = strlen(str);
		if (_lwrite (hPostLog, str, len) == HFILE_ERROR) 
		{
			AbortPostLog (hWnd);
		}
	}
}

void InitLogging (HWND hWnd)
{

	OFSTRUCT ofs;
	hMailLog = hPostLog = (HFILE) NULL;

	if (SendingPost && PostLog) 
	{
		if (OpenFile (PostLogFile, &ofs, OF_EXIST) != HFILE_ERROR) 
		{
			if ((hPostLog = OpenFile (PostLogFile, &ofs, OF_WRITE)) == HFILE_ERROR) 
			{
				AbortPostLog (hWnd);
			}
			else 
			{
				if (_llseek (hPostLog, 0L, 2) == HFILE_ERROR) 
				{         /* seek eof */
					AbortPostLog (hWnd);
				}
			}
		}
		else 
		{
			if ((hPostLog = OpenFile (PostLogFile, &ofs, OF_CREATE)) == HFILE_ERROR) 
			{
				AbortPostLog (hWnd);
			}
		}
	}

	// avoid logging mail if also logging post, and log files have same name
	if (SendingMail && MailLog &&
		(!SendingPost || !PostLog || strcmp (MailLogFile, PostLogFile))) 
	{
		if (OpenFile (MailLogFile, &ofs, OF_EXIST) != HFILE_ERROR) 
		{
			if ((hMailLog = OpenFile (MailLogFile, &ofs, OF_WRITE)) == HFILE_ERROR) 
			{
				AbortMailLog (hWnd);
			}
			else 
			{
				if (_llseek (hMailLog, 0L, 2) == HFILE_ERROR) 
				{         /* seek eof */
					AbortMailLog (hWnd);
				}
			}
		}
		else 
		{
			if ((hMailLog = OpenFile (MailLogFile, &ofs, OF_CREATE)) == HFILE_ERROR) 
			{
				AbortMailLog (hWnd);
			}
		}
	}
}

void EndLogging (HWND hWnd)
{
	/* add a blank line to the end */
	LogLine (hWnd, "\r\n", 2);
	if (SendingMail && MailLog && hMailLog) 
	{
		_lclose (hMailLog);
	}
	if (SendingPost && PostLog && hPostLog) 
	{
		_lclose (hPostLog);
	}
	hMailLog = hPostLog = (HFILE) NULL;
}

void AbortSend (HWND hNotifyWnd)
{
	SendingMail = FALSE;
	if (SendingPost) 
	{
		SendingPost = FALSE;
		CommBusy = FALSE;
		CommState = ST_NONE;
		SetStatbarText (NetDoc.hWndFrame, "", &NetDoc, TRUE, TRUE);
		InvalidateRect (NetDoc.hDocWnd, NULL, TRUE);
		if (CommDoc != &NetDoc && CommDoc) 
		{
			SetStatbarText (CommDoc->hWndFrame, "", CommDoc, TRUE, TRUE);
			InvalidateRect (CommDoc->hDocWnd, NULL, TRUE);
		}
	}
}

void AbortSendMail (HWND hNotifyWnd)
{
	MessageBox (hNotifyWnd, "Mail has failed.  Send aborted.", "Mail Failure", MB_OK | MB_ICONSTOP);
	AbortSend(hNotifyWnd);
}

void AbortSendPost (HWND hNotifyWnd)
{
	MessageBox (hNotifyWnd, "Post has failed.  Send aborted.", "Post Failure", MB_OK | MB_ICONSTOP);
	AbortSend(hNotifyWnd);
}


char* GenerateMIMEBoundaryString()
{
	char* pStr;
	static char sRetval[32];
	const char szBChars[] =
	"0123456789abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ'()+_,-./:=?";

	strcpy(sRetval, "*_Boundary_");
	for(pStr = sRetval + strlen(sRetval); pStr < sRetval + sizeof(sRetval) - 1; pStr++)
	{
		*pStr = szBChars[rand() % sizeof(szBChars)];
	}
	*pStr = '\0';
	return sRetval;
}

/* ---------------------------------------------------------------------------
 * Processes the Attachments in WndEdit->attachments
 */
BOOL SendComposition (WndEdit * compWnd)
{
	/* Initialize text blocks */
	if ((SectionHeader = InitTextBlock (compWnd->hWnd)) == NULL)
		return FAIL;
	if ((WorkBlock = InitTextBlock (compWnd->hWnd)) == NULL)
		return FAIL;
	if ((MainHeader = InitTextBlock (compWnd->hWnd)) == NULL)
		return FAIL;

	if (GenerateHeadersForSend (compWnd, MainHeader, origSubject, ConfirmXPosting) == FAIL)
		return FAIL;

	SendingMail = (MailCtrl.MailType == MT_SMTP && 
					(compWnd->composeType == DOCTYPE_MAIL ||
					compWnd->composeType == DOCTYPE_FORWARD ||
					(compWnd->composeType == DOCTYPE_POSTING && *CcAddress)));

	SendingPost = ((compWnd->composeType == DOCTYPE_POSTING || compWnd->composeType == DOCTYPE_SUPERSEDE) ||
					(compWnd->composeType == DOCTYPE_CANCEL));

	InitLogging (compWnd->hWnd);

	/* init hCodedBlockWnd and currentCoded */
	CodingState = ATTACH_WAITING; /* status info */
	CreateStatusArea (compWnd->hWnd, "Send Status", SW_SHOWNORMAL);
	InvalidateRect (hCodedBlockWnd, NULL, TRUE);  /* clear background */

	/* set body (attachment 0) size */
	compWnd->attachment[0]->numBytes =
		(unsigned long) SendMessage (compWnd->hWndEdit, WM_GETTEXTLENGTH, 0, 0L);

	if (GenerateMIME) 
	{
		MIMEBoundary = GenerateMIMEBoundaryString();
		if (GenerateMIMEVersion (MainHeader)) 
		{
			CompleteSend (compWnd, ABORT);
			return FAIL;
		}
	}
	else 
	{
		if (AddEndedLineToTextBlock (MainHeader, "")) 
		{
			CompleteSend (compWnd, ABORT);
			return FAIL;
		}
	}

	// we need this global since when wvutil.c calls DoSend, it doesn't
	// have a handle to the current compose window
	ComposeWnd = compWnd;
	compWnd->busy = TRUE;

	// lock out changes during send
	SendMessage (compWnd->hWndEdit, EM_SETREADONLY, TRUE, 0L);
	EnableSendComposition (compWnd, FALSE);

	AttachmentState = ATTACH_START;
	DoSend (CONTINUE);
	return SUCCESS;
}

/* ---------------------------------------------------------------------------
 * DoSend
 * this is written as a state machine to allow interruption in !ReviewMode
 * to wait for News Server comm responses.
 * Thus in !ReviewMode, after an Initiate/EndSendSection, we give up control
 * and wvutil calls DoSend when ready to continue
 */
void DoSend (int action)
{
	static unsigned long attachSentNumBytes  = 0;
	static BOOL MultipartMixed;
	char temp[MAXHEADERLINE], dateStr[MAXINTERNALLINE];
	static int abort, sectionNumInThisSend, nextState;
	unsigned long maxAttach;
	unsigned long offset;
	TypAttachment *thisAttach;
	int nType = 0;
	int len;

	if (action == ABORT || (!SendingMail && !SendingPost)) 
	{
		abort = ABORT;
		AttachmentState = ATTACH_DONE;
	}

	while (AttachmentState != ATTACH_NONE) 
	{
		switch (AttachmentState) 
		{

			case ATTACH_START:
				abort = ABORT;
				MultipartMixed = FALSE;
				currentCoded->numLines = 0;
				currentCoded->numBytes = 0;
				SentNumBytes = 0;
				GenerateMIMEAttachmentID ();

				if (ComposeWnd->attachment[0]->numBytes) 
				{    // skip empty body
					SendAttachNum = 0;
				}
				else if (ComposeWnd->numAttachments > 1) 
				{
					SendAttachNum = 1;
				}
				else 
				{
					MessageBox (ComposeWnd->hWnd, "Nothing to send.", "Send Aborted", MB_OK | MB_ICONHAND);
					AttachmentState = ATTACH_DONE;
					break;
				}

				if (CalcNumParts (ComposeWnd) == FAIL)        // sets SendNumParts
				{
					AttachmentState = ATTACH_DONE;
					break;
				}
				if (SendNumParts > 1 && ComposeWnd->numAttachments == 1 && !ComposeWnd->nextBatchIndex)
				{
					//warn user about split message body when not batch MDS 20001127
					//should this be done regardless of attachments?
					unsigned int uFormat = MB_OKCANCEL; 
					if (WVArgXMessageBox(ComposeWnd->hWndEdit, IDS_MESSAGE_SPLIT, IDS_WINVN_CONFIRM1, uFormat, (long)ArticleSplitLength, (int)SendNumParts) != IDOK)
					{
						AttachmentState = ATTACH_DONE;
						abort = ABORT;
						break;
					}
				}
				SendingPart = 1;
				AttachmentState = ATTACH_START_NEXT_SEND;
				nextState = ATTACH_BEGIN_ATTACHMENT;
				// falls into next state

			case ATTACH_START_NEXT_SEND:
				if (InitiateSend (ComposeWnd)) 
				{
					AttachmentState = ATTACH_DONE;
					break;
				}
				thisAttach = ComposeWnd->attachment[SendAttachNum]; 
				ResetTextBlock (SectionHeader);
				if (SendNumParts > 1 && GenerateMIME && !DefaultNewArtForAttach &&
					GenerateMIMEPartialHeader (SectionHeader, SendingPart,SendNumParts ) == FAIL) 
				{
					AttachmentState = ATTACH_DONE;
					break;
				}
				else if ( GenerateMIME && DefaultNewArtForAttach && thisAttach->nAttachParts > 1 &&
						  GenerateMIMEPartialHeader (SectionHeader, thisAttach->nCurPart+1,thisAttach->nAttachParts ) == FAIL) 
				{
					AttachmentState = ATTACH_DONE;
					break;
				}


				if (SendingPart == 1 && ComposeWnd->numAttachments > SendAttachNum + 1 &&
					GenerateMIME && !DefaultNewArtForAttach) 
				{
					MultipartMixed = TRUE;
					if (GenerateMIMEMultipartMixedHeader (SectionHeader)) 
					{
						AttachmentState = ATTACH_DONE;
						break;
					}
				}

				AttachmentState = nextState;
				sectionNumInThisSend = 0;
#ifdef ENABLE_COMM_SEND
				if (SendingPost) 
				{
					return;                                 // waiting for post authorization

				}
#endif
				break;

			case ATTACH_BEGIN_ATTACHMENT:
				thisAttach = ComposeWnd->attachment[SendAttachNum];   // convenient
				nType = thisAttach->encodingType;

				if (SendAttachNum == 0) 
				{     // status info
					attachSentNumBytes = ComposeWnd->bodyOffset;
					strcpy (currentCoded->ident, "Body");
				}
				else 
				{
					attachSentNumBytes = 0;
					strcpy (currentCoded->ident, NameWithoutPath (str, thisAttach->fileName));
				}
				currentCoded->sequence = SendingPart;
				currentCoded->nTotal = SendNumParts;
				//      currentCoded->numLines = 0;
				//      currentCoded->numBytes = 0;
				if (GenerateMIME || thisAttach->encodingType == CODE_BASE64 ) 
				{
					GenerateMIMEContentHeader (SectionHeader, thisAttach);
				}
				AttachmentState = ATTACH_MAIN;
				break;

			case ATTACH_MAIN:
				thisAttach = ComposeWnd->attachment[SendAttachNum];   // convenient
				nType = thisAttach->encodingType;
				if (SendingPost) 
				{
					CommState = ST_POST_WAIT_END;
				}
				UpdateBlockStatus ();

				if ( (!GenerateMIME) && (SendNumParts > 1) && (SendAttachNum > 0) ) 
				{
					//this should only be added when there is text in the body
					if (nType != CODE_YENC)
					{
					if (AddEndedLineToTextBlock (SectionHeader, CUT_HERE_TEXT)) 
					{
						AttachmentState = ATTACH_DONE;
						break;
					}
					}
					else
					{
					if (AddEndedLineToTextBlock (SectionHeader, "")) 
					{
						AttachmentState = ATTACH_DONE;
						break;
					}

					}
				}

				// if 1st section of a new send, then need to show main headers
				if (sectionNumInThisSend == 0) 
				{
					if (ComposeWnd->numAttachments > 1 || SendNumParts > 1) 
					{
						GenerateSubject (ComposeWnd);
						UpdateMessageID(ComposeWnd);
					}
					/* if logging, start with a mailbox header */
					if (hMailLog || hPostLog) 
					{
						GetMailboxDate (dateStr, MAXINTERNALLINE - 1);
						len = _snprintf (temp, MAXHEADERLINE, "From %s %s\r\n",
										(*MailAddress) ? MailAddress : "Unknown", dateStr);
						LogLine (ComposeWnd->hWnd, temp, len);
					}
#ifdef RFC1522
					SendHeaderTextBlock (MainHeader);
#else
					SendTextBlock(MainHeader);
#endif
				}
				// send stuff prepared in START_NEXT_SEND/BEGIN_ATTACHMENT
#ifdef RFC1522
				SendHeaderTextBlock (SectionHeader);
#else
				SendTextBlock (SectionHeader);
#endif
				ResetTextBlock (SectionHeader);

				if (SendingPart == SendNumParts) 
				{
					// add the rest of the attachment (this is the case for numParts == 1)
					maxAttach = 0;
				}
				else 
				{
					maxAttach = ArticleSplitLength - SentNumBytes;
				}
			if (SendAttachNum == 0) 
			{     // first attachment is body (from editWnd)
				if ((offset = SendPartialEditText (ComposeWnd->hWndEdit, attachSentNumBytes, maxAttach)) == 0) 
				{
					AttachmentState = ATTACH_DONE;
					break;
				}
			}
			else 
			{  //Turn off logging of attachments MDS 2000 03 07
				if ((MailLog && !MailLogAttach && !SendingPost) || (PostLog && !PostLogAttach && SendingPost)) 
				{
					char lbuf[_MAX_PATH + 100];

					if (!hMailLog && !hPostLog && (PostLog || MailLog) )
						InitLogging (ComposeWnd->hWnd);
					sprintf(lbuf, " *** File attachment %s part# %02d not logged. ***\r\n", thisAttach->fileName, SendingPart);
					LogLine (ComposeWnd->hWnd, lbuf, strlen(lbuf));
					EndLogging(ComposeWnd->hWnd);
				}
				if (nType!= CODE_NONE) 
				{
					if ((offset = EncodeAndSend (thisAttach, attachSentNumBytes, maxAttach)) == 0) 
					{
						AttachmentState = ATTACH_DONE;
						if (nType == CODE_YENC)
						{

						}
						break;
					}
				}
				else 
				{
					if ((offset = SendPartialTextFile (thisAttach, attachSentNumBytes, maxAttach)) == 0) 
					{
						AttachmentState = ATTACH_DONE;
						break;
					}
				}
			}
			attachSentNumBytes += offset;

			if (maxAttach && attachSentNumBytes < thisAttach->numBytes) 
			{
			SendingPart++;
			//          currentCoded->numLines = 0;
			currentCoded->sequence = SendingPart;   // status window info

			AttachmentState = ATTACH_START_NEXT_SEND;
			nextState = ATTACH_MAIN;

			if (EndSendSection (ComposeWnd) == FAIL) 
			{
				AttachmentState = ATTACH_DONE;
				UpdateBlockStatus (TRUE);

				abort = ABORT;
			}
#ifdef ENABLE_COMM_SEND

			else if (SendingPost)
				return;
#endif
			}
			else  // Done with attachment
			{
				if (SendAttachNum + 1 < ComposeWnd->numAttachments) 
				{
					if (GenerateMIME && !DefaultNewArtForAttach) 
					{
						// we are multipart/mixed if we are here
						_snprintf (temp, MAXHEADERLINE, "--%s\r\n", MIMEBoundary);
						SendOneLine (temp);
					}

					SendAttachNum++;
					if (ComposeWnd->attachment[SendAttachNum]->attachInNewArt || thisAttach->encodingType == CODE_YENC || DefaultNewArtForAttach) 
					{
						SendingPart++;
						AttachmentState = ATTACH_START_NEXT_SEND;
						nextState = ATTACH_BEGIN_ATTACHMENT;
						//each attachment group needs it own MIME id
						if (DefaultNewArtForAttach)
							GenerateMIMEAttachmentID (SendAttachNum);

					}
					else 
					{
						// begin new attachment in this same send
						sectionNumInThisSend++;
						AttachmentState = ATTACH_BEGIN_ATTACHMENT;
					}
				}
				else 
				{
					// done with all attachments successfully
					AttachmentState = ATTACH_DONE;
					abort = CONTINUE;
					if (GenerateMIME && MultipartMixed) 
					{
						// if multi-part/mixed then need to attach final boundary
						_snprintf (temp, MAXHEADERLINE, "--%s--\r\n", MIMEBoundary);
						SendOneLine (temp);
					}
				}
				if (AttachmentState != ATTACH_BEGIN_ATTACHMENT) 
				{
					if (EndSendSection (ComposeWnd) == FAIL) 
					{
						AttachmentState = ATTACH_DONE;
						abort = ABORT;
					}
#ifdef ENABLE_COMM_SEND
					else if (SendingPost)
						return;
#endif
				}
			}
			break;

			case ATTACH_DONE:
				AttachmentState = ATTACH_NONE;

				if (ComposeWnd)
#ifdef ENABLE_COMM_SEND
					CompleteSend (ComposeWnd, abort);
#else
					//leave post around for next test
					CompleteSend (ComposeWnd, ABORT);
#endif

				break;

		}  // end of switch (AttachmentState)

	}  // end of while (AttachmentState != ATTACH_NONE)

	return;
}


/* ------------------------------------------------------------------------
 *    Send routines
 */
BOOL InitiateSend (WndEdit * compWnd)
{
	int result = 0;

	CodingState = ATTACH_WAITING;
	UpdateBlockStatus ();

#ifdef ENABLE_COMM_SEND
	if (SendingMail) 
	{
		if ((compWnd->composeType == DOCTYPE_POSTING) ||
			(compWnd->composeType == DOCTYPE_CANCEL))
		{
			result = prepare_cc_smtp_message (CcAddress, MainHeader);
		}
		else 
		{
			result = prepare_smtp_message (MainHeader, compWnd->hWnd);
		}
		if (result != 0) 
		{
			AbortSendMail (compWnd->hWnd);
		}
	}

	if (!result && SendingPost) 
	{
		CommLinePtr = CommLineIn;
		CommBusy = TRUE;
		CommState = ST_POST_WAIT_PERMISSION;
		
		if (PutCommLine ("POST") < 0)
		{
			AbortSendPost (compWnd->hWnd);
			return -1;	
		}
	}
#endif
	SentNumBytes = 0;

	return (result);
}

BOOL EndSendSection (WndEdit * compWnd)
{
	int result = 0;

	CodingState = ATTACH_WAITING;
	UpdateBlockStatus (TRUE);

	/* ensure ends with blank line */
	SendOneLine("\r\n");

#ifdef ENABLE_COMM_SEND
	if (SendingMail) 
	{
		result = end_send_smtp (compWnd->hWnd);
		if (result != 0) 
		{
			AbortSendMail (compWnd->hWnd);
		}
	}
	if (!result && SendingPost) 
	{
		result = end_post ();
		if (result != 0) 
		{
			AbortSendPost (compWnd->hWnd);
		}
	}
#endif
	SentNumBytes = 0;

	return (result);
}

void CompleteSend (WndEdit * compWnd, int action)
{
	if (!compWnd)
		return;

	EndLogging (compWnd->hWnd);

	FreeTextBlock (MainHeader);
	FreeTextBlock (SectionHeader);
	FreeTextBlock (WorkBlock);

	DestroyStatusArea ();
	CodingState = INACTIVE;
	ComposeWnd = (WndEdit *) NULL;
	if (SendingPost) 
	{
		CommBusy = FALSE;
		CommState = ST_NONE;
	}
	if (CommDoc) 
	{
		SetStatbarText (CommDoc->hWndFrame, "", CommDoc, TRUE, TRUE);
		InvalidateRect (CommDoc->hDocWnd, NULL, TRUE);
	}
	SetStatbarText (NetDoc.hWndFrame, "", &NetDoc, TRUE, TRUE);
	InvalidateRect (NetDoc.hDocWnd, NULL, TRUE);
	SendingMail = SendingPost = FALSE;
	compWnd->busy = FALSE;

	if (action == CONTINUE) 
	{
		/* if successful, close the compose window */
		DestroyWindow (compWnd->hWnd);

		/* if we're sending a batch, send the next */
		if (compWnd->nextBatchIndex) 
		{
			//S.Yoshida BatchSend (compWnd->composeType);
			if (compWnd->composeType == DOCTYPE_MAIL) 
			{
				PostMessage (NetDoc.hWndFrame, WM_COMMAND, IDM_SEND_NEXT_MAIL, 0);
			} 
			else 
			{
				PostMessage (NetDoc.hWndFrame, WM_COMMAND, IDM_SEND_NEXT_POST, 0);
			}
		}
	} 
	else 
	{
		/* unsuccessful, unset read-only bit to allow fixes, and allow send */
		SendMessage (compWnd->hWndEdit, EM_SETREADONLY, FALSE, 0L);
		EnableSendComposition (compWnd, TRUE);
		/* cancel any batch */
		compWnd->nextBatchIndex = 0;
	}
}

BOOL SendOneLineWithLen (char *str, unsigned long len)
{
	int result = 0;

#ifdef ENABLE_COMM_SEND
	if (SendingMail) 
	{
		result = send_smtp_line (str, len);
		if (result != 0) 
		{
			AbortSendMail(ComposeWnd->hWnd);
		}
	}
	if (!result && SendingPost) 
	{
		result = post_line (str, len);
		if (result != 0) 
		{
			AbortSendPost(ComposeWnd->hWnd);
		}
	}
#endif
	SentNumBytes += len;
	currentCoded->numLines++;
	if (currentCoded->numLines % STATUS_UPDATE_FREQ == 0)
		UpdateBlockStatus ();

	LogLine (ComposeWnd->hWnd, str, (unsigned int) len);

	return (result);
}

BOOL SendOneLine (char *str)
{
  return (SendOneLineWithLen (str, (unsigned long) strlen (str)));
}

BOOL SendTextBlock (TypTextBlock * textBlock)
{
	register unsigned long i;
	unsigned long len;
	for (i = 0; i < textBlock->numLines; i++) 
	{
		len = (unsigned long) strlen (TextBlockLine (textBlock, i));
		SendOneLineWithLen (TextBlockLine (textBlock, i), len);
		currentCoded->numBytes += len;
	}
	return SUCCESS;
}

unsigned long SendPartialEditText (HWND hWnd, unsigned long startByte,
                                   unsigned long maxBytes)
{
	unsigned long len, numBytes;
	char *text;
	char *ptr, *endPtr;
	int result = SUCCESS;

	CodingState = ATTACH_SENDING;
	UpdateBlockStatus ();

	if ((text = GetEditText (hWnd)) == NULL) 
	{
		return 0L;
	}

	numBytes = 0L;
	ptr = &text[startByte];
	while (*ptr && result == SUCCESS && (!maxBytes || numBytes < maxBytes)) 
	{
		if ((endPtr = strchr (ptr, '\r')) == NULL) 
		{
			/* last line didn't end with \r\n */
			if ((result = SendOneLine (ptr)) == SUCCESS) 
			{
				result = SendOneLineWithLen ("\r\n", 2);
			}
			numBytes += strlen (ptr) + 2;
			break;
		}
		endPtr++;                                       /* move past \r */
		/* handle soft line break \r\r\n (see EM_FMTLINES) */
		if (*endPtr == '\r') 
		{
			*endPtr = '\n';
		}
		endPtr++;                                       /* move past \n */

		len = (unsigned long) (endPtr - ptr);
		result = SendOneLineWithLen (ptr, len);
		ptr = endPtr;
		numBytes += len;

		if (*ptr == '\n') 
		{
			numBytes++;
			ptr++;                                        /* move past extra \n (in case of soft line break extra \n */
		}
	}
	GlobalFreePtr (text);

	return numBytes;
}

#ifdef RFC1522

BOOL SendOneHeaderLineWithLen (char *str, unsigned long len)
{
	char mybuff[MAXCOMMLINE];

	strncpy(mybuff,str, min(len, MAXCOMMLINE-1) );
	mybuff[min(len, MAXCOMMLINE-1)] = 0;
	encodeStrHeader(mybuff);
	return (SendOneLineWithLen(mybuff,(unsigned long)strlen(mybuff)));
}

BOOL SendOneHeaderLine (char *str)
{
	return (SendOneHeaderLineWithLen (str, (unsigned long) strlen (str)));
}

BOOL SendHeaderTextBlock (TypTextBlock * textBlock)
{
	register unsigned long i;
	unsigned long len;
	BOOL bFailed = FALSE;
	for (i = 0; !bFailed && i < textBlock->numLines; i++) 
	{
		len = (unsigned long) strlen (TextBlockLine (textBlock, i));
		bFailed = SendOneHeaderLineWithLen (TextBlockLine (textBlock, i), len);
		currentCoded->numBytes += len;
	}
	return bFailed;
}

#endif


BOOL SendEditText (HWND hWnd)
{
	if (SendPartialEditText (hWnd, 0L, 0L) == 0L) 
	{
		return FAIL;
	}
	else 
	{
		return SUCCESS;
	}
}

unsigned long SendPartialTextFile (TypAttachment * thisAttach, unsigned long startByte,
                                    unsigned long maxBytes)
{
	HFILE hFile;
	int numRead;
	char inBuf[MAXOUTLINE];
	unsigned long totalNumRead;
	register int i;
	BOOL done;
	int result = SUCCESS;

	CodingState = ATTACH_READFILE;
	UpdateBlockStatus ();

	if ((hFile = _lopen (thisAttach->fileName, LOPEN_READ)) == HFILE_ERROR) 
	{
		return (0);
	}
	
	if (startByte && _llseek (hFile, startByte, 0) == HFILE_ERROR) 
	{
		_lclose (hFile);
		return (0);
	}

	done = FALSE;
	totalNumRead = 0L;
	while (!done && result == SUCCESS && (!maxBytes || totalNumRead < maxBytes)) 
	{
		if ((numRead = _lread (hFile, inBuf, MAXOUTLINE)) == HFILE_ERROR) 
		{
			_lclose (hFile);
			return (0);
		}
		if (numRead < MAXOUTLINE) 
		{
			done = TRUE;
		}
		else if (maxBytes && totalNumRead + numRead > maxBytes) 
		{
		
			// seek cr/lf
			for (i = 0; i < numRead && !strchr ("\r\n", inBuf[i]); i++);
			
			// skip over cr/lf's
			for (; i < numRead && strchr ("\r\n", inBuf[i]); i++);
			numRead = i;
			done = TRUE;
		}
		totalNumRead += numRead;
		currentCoded->numBytes += numRead;
		currentCoded->numLines++;

		result = SendOneLineWithLen (inBuf, numRead);

		if (currentCoded->numLines % STATUS_UPDATE_FREQ == 0)
			UpdateBlockStatus ();
	}

	_lclose (hFile);

	UpdateBlockStatus ();
	return (totalNumRead);
}

//actually run through an encoding process to determine encoded size because for yEnc, length
//is determined by data content
unsigned long CalcYEncSize (char * szFile, unsigned long * pnheaderSize = NULL)
{

	HANDLE hFile;
	int start;
	unsigned char inBuf[MAXINTERNALLINE];	 //yEnc needs 129+
	unsigned char outLine[MAXINTERNALLINE];
	DWORD numRead;
	BOOL done;
	int remain = 0;
	long lCount = 0;
	size_t lHeader = 0;

	if (pnheaderSize)
		*pnheaderSize = 0;

	// open the file and have at it 
	if ((hFile = CreateFile ((char*) szFile, GENERIC_READ, FILE_SHARE_WRITE, NULL, OPEN_EXISTING,
			FILE_ATTRIBUTE_NORMAL, NULL)) == INVALID_HANDLE_VALUE ) 
	{
		return (0);
	}

	done = FALSE;

	DWORD dwFileSize = GetFileSize(hFile, NULL);
	NameWithoutPath (str, szFile);
	lHeader = _snprintf ((char*)outLine, MAXINTERNALLINE, "=ybegin part=1 total=1 line=128 size=%ld name=%s\r\n", dwFileSize, str);
	lHeader += 32; //assume multipart "=ypart begin=50000 end=100000\r\n"

	start = 0;

	while (!done ) 
	{
		DWORD i;
		int c, len=0; 
		if (!ReadFile (hFile, inBuf+remain, YENCODE_LINE_LEN-remain, &numRead, NULL)) 
		{
			CloseHandle (hFile);
			return (0);
		}
		numRead += remain;
		remain = 0;
		if (numRead < YENCODE_LINE_LEN) 
		{        // change last line length value
			done = TRUE;
		}
		i = 0;
		while (i < numRead && len < YENCODE_LINE_LEN)
		{
		c = inBuf[i++];
		c = ((c+42) & 0xFF); //add the secret number
		//handle problem characters, or dot in first column
		if (c == 0x00 || (c == 0x09 && (len == 0 || len+1 == YENCODE_LINE_LEN)) || c == 0x0A || c == 0x0D || c == '=' || (len == 0 && c == '.') )
		{
			lCount+=2;
			len+=2;
		}
		else
		{
			lCount++;
			len++;
		}	

		}
		lCount+=2; //<cr><lf>
		len+=2;

		if (i < numRead)
		{
			remain = numRead - i;
			memmove(inBuf, inBuf+i, remain);
		}

		if (done)
		{
			_snprintf( (char*)outLine, MAXINTERNALLINE, "=yend part=1 size=%ld crc32=%08lx \r\n",dwFileSize, 0xFFFFFFFF);
			lHeader += strlen( (char *) outLine);
		}

	}

	CloseHandle (hFile);

	if (pnheaderSize)
	{
		*pnheaderSize = lHeader;
		return lCount;
	}


	return lCount + lHeader;




}

/* ------------------------------------------------------------------------
 * Calculate the # parts needed for body and all attachments
 * This basically does a pre-flight of the entire send-generation process,
 * specifically paying attention to what headers will be generated, etc.
 * It's a bit clunky, and hard to maintain - since it must match the 
 * logic flow in the DoSend switch.  However, this _should_ give us
 * accurate NumParts _up front_ so we don't need to generate the full contents
 * in memory before sending it
 *
 */
BOOL CalcNumParts (WndEdit * compWnd)
{
	register int i;
	unsigned long workSize, addSize, pSize, sizeLeft, artspace;
	unsigned long aSize[MAX_NUM_ATTACHMENTS];
	unsigned long hSize[MAX_NUM_ATTACHMENTS] = {0};

	BOOL msgBreak = FALSE;
	int nType = CODE_NONE;

	if (ArticleSplitLength == 0) 
	{
		// just count # of attachInNewArt flags
		SendNumParts = 1;
		// if empty body, make first attachment act like the body
		i = (compWnd->attachment[0]->numBytes) ? 1 : 2;
		for (; i < compWnd->numAttachments; i++) 
		{
			compWnd->attachment[i]->nAttachParts = 1;
			if (compWnd->attachment[i]->attachInNewArt) 
			{
				SendNumParts++;
			}
		}
		return SUCCESS;
	}

	// generate an approximate subject line (only if it's obvious that we'll need one)
	if (compWnd->numAttachments > 1 ||
		compWnd->attachment[0]->numBytes > (unsigned long) ArticleSplitLength) 
	{
		SendingPart = SendNumParts = 20;        // dummy #s
		GenerateSubject (compWnd);
	}

	// calculate total number of bytes for all the body + attachments

	// body is attachment 0 
	aSize[0] = 0L;
	if (GenerateMIME && compWnd->numAttachments > 1 &&
		compWnd->attachment[0]->numBytes > 0) 
	{       // non-empty body

		ResetTextBlock (WorkBlock);
		if (GenerateMIMEMultipartMixedHeader (WorkBlock) == FAIL) 
		{
			return FAIL;
		}
		aSize[0] += WorkBlock->numBytes;
	}
	aSize[0] += compWnd->attachment[0]->numBytes;
	workSize = aSize[0];

	for (i = 1; i < compWnd->numAttachments; i++) 
	{
		nType = compWnd->attachment[i]->encodingType;
		compWnd->attachment[i]->nCurPart = 0;

		if ( (GenerateMIME || compWnd->attachment[i]->encodingType == CODE_BASE64 ) && nType != CODE_YENC) 
		{
			aSize[i] = WorkBlock->numBytes + strlen (MIMEBoundary) + 2;
			ResetTextBlock (WorkBlock);
			if (GenerateMIMEContentHeader (WorkBlock, compWnd->attachment[i]) == FAIL) 
			{
				return FAIL;
			}
			aSize[i] += WorkBlock->numBytes;
		}
		else  if (nType != CODE_YENC)
		{
			aSize[i] = strlen (CUT_HERE_TEXT);
		}
		if ( nType != CODE_NONE) 
		{
			unsigned long nAttachSpace = ArticleSplitLength - (MainHeader->numBytes > 0 ? MainHeader->numBytes : 512);
			unsigned long size;
			if (nType == CODE_YENC)
			{
				//this is the encoded file size, but yenc send actual file bytes in data
				//so we need original size
				DWORD dwSize = GetFileLength(compWnd->attachment[i]->fileName);
				size = CalcYEncSize (compWnd->attachment[i]->fileName, &hSize[i]);
			
			
				compWnd->attachment[i]->nAttachParts = 1 + size / nAttachSpace;
				//avg number of data bytes to encode per segment
				compWnd->attachment[i]->nAttachBytes = MulDiv(nAttachSpace, dwSize, size);
				
				addSize = size;
				aSize[i] = hSize[i];
				 

			}
			else
			{
				addSize = (compWnd->attachment[i]->numBytes * 4) / 3;
				 // estimate prefix & \r\n for each line

				addSize += (addSize / 60) * (nType == CODE_BASE64 ? 2 : 3);       
				
				compWnd->attachment[i]->nAttachParts = 1 + addSize / nAttachSpace;
			}

		}
		else 
		{
			addSize = compWnd->attachment[i]->numBytes;
		}
		aSize[i] += addSize;
		workSize += aSize[i];
		//yENC should always start in new msg 
		if (DefaultNewArtForAttach || compWnd->attachment[i]->attachInNewArt || (i > 2 && nType == CODE_YENC) )
		{
			msgBreak = TRUE;
		}
	}

	// if they all fit in one art, and the attachments were not broken by an
	// attachInNewArt flag, then done
	if (!msgBreak &&
		workSize + MainHeader->numBytes < (unsigned long) ArticleSplitLength) 
	{
		SendNumParts = 1;
		return SUCCESS;
	}

	// they don't all fit, or there was an attachInNewArt break, so do message/partial
	if (GenerateMIME) 
	{
		ResetTextBlock (WorkBlock);
		if (!DefaultNewArtForAttach)	 //MDS TODO: 1st attach in same message need Multipart
			GenerateMIMEPartialHeader (WorkBlock, SendingPart, SendNumParts);
		pSize = WorkBlock->numBytes;
	}
	else 
	{
		pSize = strlen (CUT_HERE_TEXT);
		if (compWnd->attachment[1]->encodingType == CODE_YENC)
			pSize = hSize[1];
	}

	// calc number of bytes that will fit in each subsequent send (after headers)
	artspace = ArticleSplitLength - (MainHeader->numBytes + pSize);

	SendNumParts = 1;
	workSize = artspace;   // space left in art after headers

	for (i = 0; i < compWnd->numAttachments; i++) 
	{

		nType = compWnd->attachment[i]->encodingType;

		sizeLeft = aSize[i];

		while (sizeLeft > workSize) 
		{   // sizeLeft - workSize > 0

			sizeLeft -= workSize;
			SendNumParts++;
			workSize = artspace;            // new article full-sized

		}
		// start new art with sizeLeft already used up
		if (i + 1 < compWnd->numAttachments ) 
		{
			if (!DefaultNewArtForAttach && !compWnd->attachment[i + 1]->attachInNewArt && !(i > 0 && compWnd->attachment[i+1]->encodingType == CODE_YENC) ) 
			{
				workSize -= sizeLeft;
			}
			else 
			{
				workSize = artspace;
				SendNumParts++;
			}
		}
	}
	return SUCCESS;
}

//  RFC-1521 says MIME encoding must use the lowest common denominator
//  character set possible.  Thus is a message contains US-ASCII
//  characters only, it must be marked as US-ASCII and not ISO-8859-1
//  which is a superset of US-ASCII.
//
//    Check Routines submitted by:  TANAKA Goh, <goh@yamato.ibm.co.jp>
//                                  IBM Japan Ltd.  Yamato Lab.

BOOL isasciistr(char *string)
{
	unsigned char c;

	while ((c = *string++) != 0) 
	{
		if (!isascii(c)) 
		{
			return FALSE;
		}
	}
	return TRUE;
}

BOOL  MakeTextContentTypeField(char *string, TypAttachment *thisAttach)
{
	char  *text;
	HFILE hf;
	int   cbRead;
	BYTE  bBuf[MAXOUTLINE];

	if (SendAttachNum == 0) 
	{
		if ((text = GetEditText(ComposeWnd->hWndEdit)) == NULL) 
		{
			return FAIL;
		}
		if (isasciistr(text)) 
		{
			_snprintf(string, MAXHEADERLINE, "Content-Type: %s; charset=%s", thisAttach->contentType, "US-ASCII");
			if (thisAttach->encodingType == CODE_NONE) /* Heirich 19960821 */
				thisAttach->encodingSubType = CODE_7BIT;               
			return SUCCESS;
		} 
		else 
		{
			_snprintf(string, MAXHEADERLINE, "Content-Type: %s; charset=%s", thisAttach->contentType, MIMECharset);
			if (thisAttach->encodingType == CODE_NONE) /* Heirich 19960821 */
				thisAttach->encodingSubType = CODE_8BIT;      
			return SUCCESS;
		}
	} 
	else 
	{
		if ((hf = _lopen(thisAttach->fileName, LOPEN_READ)) == HFILE_ERROR) 
		{
			return FAIL;
		}

		do 
		{
			if ((cbRead = _lread(hf, bBuf, MAXOUTLINE)) == HFILE_ERROR) 
			{
				_lclose(hf);
				return FAIL;
			}
			if (cbRead && !isasciistr((char *) bBuf)) 
			{
				_lclose(hf);
				_snprintf(string, MAXHEADERLINE, "Content-Type: %s; charset=%s", thisAttach->contentType, MIMECharset);
				if (thisAttach->encodingType == CODE_NONE) /* Heirich 19960821 */
					thisAttach->encodingType = CODE_8BIT;              
				return SUCCESS;
			}
		} while (cbRead != 0);

		_lclose(hf);
		_snprintf(string, MAXHEADERLINE, "Content-Type: %s; charset=%s", thisAttach->contentType, "US-ASCII");
		if (thisAttach->encodingType == CODE_NONE) /* Heirich 19960821 */
			thisAttach->encodingSubType = CODE_7BIT;      
		return SUCCESS;
	}
}

/* ------------------------------------------------------------------------
 *    MIME block generation routines
 */
BOOL GenerateMIMEVersion (TypTextBlock * textBlock)
{
	char temp[MAXINTERNALLINE];

	sprintf (temp, "MIME-Version: %s", MIME_VERSION);
	return (AddEndedLineToTextBlock (textBlock, temp));
}



void GenerateMessageID(char *buf, int nPart, int nVersion)
{
	
	if (!buf)
		return;
	char part[40] = {".0"};
	char version[10] = {""};

	SYSTEMTIME stm;
	GetSystemTime(&stm);


	if (nPart > 0)
	_snprintf(part, 40, ".%d", nPart);

	//version used for supersedes
	if (nVersion > 0)
		_snprintf(version, 10, "%%v=%d", nVersion);

 									 //yr   mo   dy   hr   min  sec
	_snprintf(buf, MAXINTERNALLINE, "%s%04d%02d%02d-%02d%02d%02d.%d%s%s@%s",
		IDToken, stm.wYear, stm.wMonth, stm.wDay, stm.wHour, stm.wMinute,
		stm.wSecond, stm.wMilliseconds,  part, version, MyOwnHost);

}

void GenerateMIMEAttachmentID (int nAttach /*=-1*/)
{
	
	GenerateMessageID(attachmentID, -1);
}


BOOL GenerateMIMEMultipartMixedHeader (TypTextBlock * textBlock)
{
	char temp[MAXINTERNALLINE];

	if (AddEndedLineToTextBlock (textBlock, "Content-Type: multipart/mixed;"))
		return FAIL;

	sprintf (temp, "     Boundary=\"%s\"", MIMEBoundary);
	if (AddEndedLineToTextBlock (textBlock, temp))
		return FAIL;

	/* blank line to end headers */
	if (AddEndedLineToTextBlock (textBlock, ""))
		return FAIL;

	/* empty preamble here */
	sprintf (temp, "--%s", MIMEBoundary);
	if (AddEndedLineToTextBlock (textBlock, temp))
		return FAIL;
	
	return SUCCESS;
}

BOOL GenerateMIMEPartialHeader (TypTextBlock * textBlock, int nSending, int nTotal)
{
	char temp[MAXHEADERLINE];

	if (AddEndedLineToTextBlock (textBlock, "Content-Type: message/partial;"))
		return FAIL;

	_snprintf (temp, MAXHEADERLINE, "     id=\"%s\";", attachmentID);
	if (AddEndedLineToTextBlock (textBlock, temp))
		return FAIL;

	_snprintf (temp, MAXHEADERLINE, "     number=%d; total=%d", nSending, nTotal);

	if (AddEndedLineToTextBlock (textBlock, temp))
		return FAIL;

	/* this is the delimiter between header and the included message */
	if (AddEndedLineToTextBlock (textBlock, ""))
		return FAIL;

	if (SendingPart == 1) 
	{
		_snprintf(temp, MAXHEADERLINE,"Subject: %s",origSubject);
		if (AddEndedLineToTextBlock (textBlock,temp))
			return FAIL;
		if (SendingPost)
		{
			_snprintf(temp, MAXHEADERLINE,"Message-ID: <ID%s>",attachmentID);
			if (AddEndedLineToTextBlock (textBlock,temp))
				return FAIL;
		}

		if (GenerateMIMEVersion(textBlock))
			return FAIL;
	}      
	return SUCCESS;
}

BOOL GenerateMIMEContentHeader (TypTextBlock * textBlock, TypAttachment * thisAttach)
{
	int type;
	register int i;
	char temp[MAXHEADERLINE], name[MAXFILENAME];

	for (i = 0; i < NUM_CONTENT_TYPES; i++)
		if (!_stricmp (thisAttach->contentType, ContentTypes[i]))
			break;

	type = thisAttach->encodingType;

#if 0   // jsc: never use default content-type.  always be explicit
	// if this is Text/Plain, non-encoded, don't need the Content-Type header
	if (!_stricmp (thisAttach->contentType, "Text/Plain") && type == CODE_NONE) 
	{
		if (AddEndedLineToTextBlock (textBlock, ""))
			return FAIL;
		return SUCCESS;
	}
#endif
	if (!_stricmp (thisAttach->contentType, "Text/Plain") && type == CODE_NONE) 
	{
	// _snprintf (temp, MAXHEADERLINE, "Content-Type: %s; charset=%s", 
	//                      thisAttach->contentType, MIMECharset);
	if (MakeTextContentTypeField(temp, thisAttach) == FAIL) 
	{
		return FAIL;
	}
	} else
	// note Text/Plain encoded gets turned into Application/octet-stream 
	//(MDS added quotes around name for filenames w/ spaces  20000401)
	if (!_stricmp (thisAttach->contentType, "Other") || i == NUM_CONTENT_TYPES ||
		!_stricmp (thisAttach->contentType, "Text/Plain")) 
	{
		_snprintf (temp, MAXHEADERLINE, "Content-Type: Application/octet-stream; name=\"%s\"",
				   NameWithoutPath (name, thisAttach->fileName));

	}
	else 
	{
		if (*thisAttach->fileName)
			_snprintf (temp, MAXHEADERLINE, "Content-Type: %s; name=\"%s\"", 
					   thisAttach->contentType, NameWithoutPath (name, thisAttach->fileName));
		else
			_snprintf (temp, MAXHEADERLINE, "Content-Type: %s", thisAttach->contentType);
	}

	if (AddEndedLineToTextBlock (textBlock, temp))
		return FAIL;

	switch (type) 
	{
		case CODE_UU:
			_snprintf (temp, MAXHEADERLINE, "Content-Transfer-Encoding: %s", MIMEUUType);
			break;
		case CODE_XX:
			_snprintf (temp, MAXHEADERLINE, "Content-Transfer-Encoding: %s", MIMEXXType);
			break;
		case CODE_CUSTOM:
			_snprintf (temp, MAXHEADERLINE, "Content-Transfer-Encoding: %s", MIMECustomType);
			break;
		case CODE_BASE64:
			strcpy (temp, "Content-Transfer-Encoding: Base64");
			break;
		case CODE_YENC:
		case CODE_NONE:
		default:
			if (thisAttach->encodingSubType == CODE_8BIT) 
			{ /* Heirich 19960821 */
				strcpy(temp,"Content-Transfer-Encoding: 8Bit");
				break;
			}
			if (thisAttach->encodingSubType == CODE_BINARY) 
			{ /* Heirich 19960826 */
				strcpy(temp,"Content-Transfer-Encoding: binary");
				break;
			}
			if (thisAttach->encodingSubType == CODE_7BIT) 
			{ 
				strcpy(temp,"Content-Transfer-Encoding: 7Bit");
				break;
			}
			*temp = '\0';
	}
	if (*temp && AddEndedLineToTextBlock (textBlock, temp))
		return FAIL;

	//Add Content-Disposition MDS 20000401
	if (_stricmp (thisAttach->contentType, "Text/Plain")) 
	{
		_snprintf (temp, MAXHEADERLINE, "Content-Disposition: attachment; filename=\"%s\"",
					   NameWithoutPath (name, thisAttach->fileName));
		if (AddEndedLineToTextBlock (textBlock, temp))
			return FAIL;
	}


	if (AddEndedLineToTextBlock (textBlock, ""))
		return FAIL;

	return SUCCESS;
}

/* ------------------------------------------------------------------------
 *    Generate a subject line based on orig subject line, and the
 *  subject template
 *      Replace %f with fileName
 *      Replace %p with part #
 *      Replace %0p with part # 0 padded to length of total # parts
 *      Replace %t with total # parts
 *      Replace %s with orig subject content
 *
 */
void GenerateSubject (WndEdit * compWnd)
{
	register char *src, *dest, *ptr;
	register unsigned long num;
	char newSubject[MAXHEADERLINE];
	char numStr[10];
	int numZeros, part, numParts;
	BOOL bDoingYENC = FALSE;
#ifndef __GNUC__
	extern char *NameWithoutPath (char *name, char *fileName);
#endif
	bool thisparthasYENC = false;

	strcpy (newSubject, "Subject: ");

	numParts = SendNumParts;
	part = SendingPart;

	bDoingYENC = compWnd->numAttachments >= 1 && compWnd->attachment[1]->encodingType == CODE_YENC;
	thisparthasYENC =  (bDoingYENC && (SendAttachNum > 0 || !compWnd->attachment[SendAttachNum]->attachInNewArt)); 

	// if non-empty body, and attachments exist, and first attachment starts 
	// in new art, then show count from zero instead of one.  (kinda kludgey!)
	if (compWnd->numAttachments > 1 && compWnd->attachment[0]->numBytes > 0 &&
		(compWnd->attachment[1]->attachInNewArt || DefaultNewArtForAttach ) ) 
	{
		numParts--;
		part--;
		if (!SendAttachNum)
			thisparthasYENC = false;
	}
	dest = &newSubject[9];
	if (thisparthasYENC)
	{
		//"yEnc" must be in Subject:
		if (!stristr(newSubject, "yEnc"))
		{
			strcat (newSubject, "yEnc ");
			dest +=	5;
		}
	}

	for (src = SubjectTemplate; *src;) 
	{
		if (*src == '%') 
		{
			src++;
			switch (*src) 
			{
				case '%':                             // literal percent is %%
					*dest++ = '%';
					break;
				case 's':
					for (ptr = origSubject; *ptr; *dest++ = *ptr++);
					break;
				case 'f':
					if (thisparthasYENC || DefaultNewArtForAttach)
					{
						NameWithoutPath (str, compWnd->attachment[SendAttachNum == 0 ? 1 : SendAttachNum]->fileName);
					}
					else if (compWnd->numAttachments == 1) 
					{
						break;
					}
					else if (compWnd->numAttachments == 2) 
					{
						NameWithoutPath (str, compWnd->attachment[1]->fileName);
					}
					else 
					{
						_snprintf (str, MAXINTERNALLINE, "%d Attachments", compWnd->numAttachments - 1);
					}
					for (ptr = str; *ptr; *dest++ = *ptr++);
					break;
				case 'b': //bytes
					if (compWnd->numAttachments == 2)
					{
						_snprintf( str, MAXINTERNALLINE, "%ld", compWnd->attachment[1]->numBytes);
						for (ptr = str; *ptr; *dest++ = *ptr++);
					}
					break;
				case 'k': //Kbytes
					if (compWnd->numAttachments == 2 )
					{
						_snprintf( str, MAXINTERNALLINE, "%ld", compWnd->attachment[1]->numBytes/1024);
						for (ptr = str; *ptr; *dest++ = *ptr++);
					}
					break;
				case '0':
					if (*(src + 1) == 'p') 
					{
						src++;                                // skip zero
						// zero-pad number to length of numParts string
						if (thisparthasYENC || DefaultNewArtForAttach)
						{
							if (SendAttachNum == 0 && SendingPart > part)
								//send (0/n) part
								_itoa (compWnd->attachment[ (SendAttachNum == 0 ? 1 : SendAttachNum)]->nCurPart, str, 10);
							else
								_itoa (compWnd->attachment[ (SendAttachNum == 0 ? 1 : SendAttachNum)]->nCurPart+1, str, 10);
							_itoa (compWnd->attachment[ (SendAttachNum == 0 ? 1 : SendAttachNum)]->nAttachParts, numStr, 10);

						}
						else
						{
							_itoa (numParts, numStr, 10);  // always length >= to part
							_itoa (part, str, 10);
						}

						for (numZeros = strlen (numStr) - strlen (str); numZeros; numZeros--)
							*dest++ = '0';

						for (ptr = str; *ptr; *dest++ = *ptr++);
					}
					break;

				case 'p':
					if (thisparthasYENC || DefaultNewArtForAttach)
					{
						_itoa (compWnd->attachment[ (SendAttachNum == 0 ? 1 : SendAttachNum)]->nCurPart+1, numStr, 10);
					}
					else
					_itoa (part, numStr, 10);
					for (ptr = numStr; *ptr; *dest++ = *ptr++);
					break;

				case 't':
					if (thisparthasYENC || DefaultNewArtForAttach)
					{
						_itoa (compWnd->attachment[ (SendAttachNum == 0 ? 1 : SendAttachNum)]->nAttachParts, numStr, 10);
					}
					else
					_itoa (numParts, numStr, 10);
					for (ptr = numStr; *ptr; *dest++ = *ptr++);
					break;

				default:
					*dest++ = '%';
					*dest++ = *src;
					break;

			}
			src++;                                        // skip control char after %

			continue;
		}
		*dest++ = *src++;
	}
	*dest = '\0';

	strcat (dest, "\r\n");

	/* find subject line in headers to replace */
	for (num = 0; num < MainHeader->numLines; num++)
		if (!_strnicmp (TextBlockLine (MainHeader, num), "subject:", 8))
			break;

	ReplaceLineInTextBlock (MainHeader, num, newSubject);

}



/* ------------------------------------------------------------------------
 * Increment our custom message ID based on part number
 *
 */
void UpdateMessageID (WndEdit * compWnd)
{

	char newMessageID[MAXHEADERLINE];
	char curMessageID[MAXHEADERLINE];
	
	newMessageID[0] = 0;
  
	/* find message-id line in headers to replace */
	for (unsigned int num = 0; num < MainHeader->numLines; num++)
	{
        if (!_strnicmp (TextBlockLine (MainHeader, num), "message-id: ", 12))
        {
			char *p, *s;
			strcpy(curMessageID, TextBlockLine (MainHeader, num));
			strcpy(newMessageID, curMessageID);
			p = strrchr( curMessageID, '@');
			if (p)
			{
				s = strrchr( newMessageID, '@');
				while (s > newMessageID && *s != '.')
					s--;
				if (s > newMessageID)
				{
					_itoa(SendingPart, ++s, 10);
					strcat(newMessageID, p);
				}
				
				ReplaceLineInTextBlock (MainHeader, num, newMessageID);
				break;  
			}
		}
	}

	if (UseCancelKey )//&& SendingPost)
		UpdateCancelLock(MainHeader);

}



/* ------------------------------------------------------------------------
 *    Directly modifies the Edit buffer of the given multi-line edit wnd
 *  Appends contents of fileName to buffer
 */
void ReadTextFileIntoEditWnd (HWND hEditWnd, char *fileName)
{
	HFILE hFile;
	register unsigned int i;
	char *editBuf;
	unsigned int numRead;
	unsigned long size, expectedLen;

	expectedLen = GetFileLength (fileName);
	size = (unsigned long) SendMessage (hEditWnd, WM_GETTEXTLENGTH, 0, 0L);
	UINT nMax = 0;
	//should we try?
	if (hEditWnd)
	{
		long nVal = SendMessage( hEditWnd, EM_GETLIMITTEXT, 0, 0) ;
		
		if (nVal == (UINT) 0xFFFF || nVal == 0)
			nMax = 60000L; //it lies
		else 
			nMax = nVal;
	}


	if (size + expectedLen > nMax) 
	{
		sprintf (str, "File %s is too large to read into the edit buffer.\nTry attaching it instead.", fileName);
		MessageBox (hEditWnd, str, "File Too Large", MB_OK | MB_ICONSTOP);
		return;
	}
	editBuf = (char *) GlobalAllocPtr (GMEM_MOVEABLE, expectedLen + 3);

	if ((hFile = _lopen (fileName, LOPEN_READ)) == HFILE_ERROR) 
	{
		sprintf (str, "Could not open file %s for read", fileName);
		MessageBox (hEditWnd, str, "Open File Error", MB_OK | MB_ICONSTOP);
		goto abortReadFile;
	}

	if ((numRead = _lread (hFile, editBuf, (unsigned int) expectedLen)) != expectedLen) 
	{
		sprintf (str, "File %s not read correctly", fileName);
		MessageBox (hEditWnd, str, "File Read Error", MB_OK | MB_ICONSTOP);
		goto abortReadFile;
	}
	for (i = 0; i < numRead; i++)
		if (!__isascii (editBuf[i])) 
		{
			sprintf (str, "File %s appears to contain non-ASCII data.\n"
			"Binary files should be encoded for attachment.\n"
			"Continue?", fileName);
			if (MessageBox (hEditWnd, str, "Non-ASCII File", MB_YESNO | MB_ICONSTOP) == IDNO) 
			{
				_lclose (hFile);
				goto abortReadFile;
			}
		}
	_lclose (hFile);

	editBuf[numRead] = '\r';
	editBuf[numRead + 1] = '\n';
	editBuf[numRead + 2] = '\0';

	// insert at current position
	if (GetFocus () != hEditWnd) 
	{
#ifdef _WIN32
		SendMessage (hEditWnd, EM_SETSEL, (WPARAM) (INT) size, (LPARAM) (INT) size);
#else
		SendMessage (hEditWnd, EM_SETSEL, (WPARAM) (UINT) 1, MAKELONG (size, size));
#endif
	}
	SendMessage (hEditWnd, EM_REPLACESEL, 0, (LPARAM) (LPCSTR) editBuf);

	abortReadFile:
	GlobalFreePtr (editBuf);
}



#ifdef OLD_HEADER
/* ------------------------------------------------------------------------
 * Returns true for lines which look like standard header lines
 * Returns non-zero if should skip this line, else zero
 * A binary search through a list of words would be really smart
 * Lazy approach
 */
int	TestRfc822HeaderLine (char *line)
{
	switch (tolower (line[0])) 
	{
		case 'd':
			return (!_strnicmp (line, "date", 4));
		case 'f':
			return (!_strnicmp (line, "follow", 6) ||
							!_strnicmp (line, "from", 4));
		case 'k':
			return (!_strnicmp (line, "keywords", 8));

		case 'l':
			return (!_strnicmp (line, "lines", 5));
		case 'm':
			return (!_strnicmp (line, "message", 7));
		case 'n':
			return (!_strnicmp (line, "newsgrou", 8) ||
							!_strnicmp (line, "nntp", 4));
		case 'o':
			return (!_strnicmp (line, "organiza", 8) ||
							!_strnicmp (line, "originat", 8));
		case 'p':
			return (!_strnicmp (line, "path", 4));
		case 'r':
			return (!_strnicmp (line, "referenc", 8) ||
							!_strnicmp (line, "reply", 5));
		case 's':
			return (!_strnicmp (line, "sender", 6) ||
							!_strnicmp (line, "subject:", 8) ||
							!_strnicmp (line, "summary", 7));
		case 'x':
			return (!_strnicmp (line, "xref:", 5) ||
							!_strnicmp (line, "x-news", 6));
		default:
			return (0);
	}
}

#endif


/*
 * Local Variables:
 * tab-width: 2
 * end:
 */
