1 line
127 KiB
C
Executable File
1 line
127 KiB
C
Executable File
/* Copyright (c) 2017, Computer History Museum
|
||
All rights reserved.
|
||
Redistribution and use in source and binary forms, with or without modification, are permitted (subject to
|
||
the limitations in the disclaimer below) provided that the following conditions are met:
|
||
* Redistributions of source code must retain the above copyright notice, this list of conditions and the following disclaimer.
|
||
* Redistributions in binary form must reproduce the above copyright notice, this list of conditions and the following
|
||
disclaimer in the documentation and/or other materials provided with the distribution.
|
||
* Neither the name of Computer History Museum nor the names of its contributors may be used to endorse or promote products
|
||
derived from this software without specific prior written permission.
|
||
NO EXPRESS OR IMPLIED LICENSES TO ANY PARTY'S PATENT RIGHTS ARE GRANTED BY THIS LICENSE. THIS SOFTWARE IS PROVIDED BY THE
|
||
COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
|
||
IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
|
||
HOLDER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES
|
||
(INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
|
||
INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING
|
||
NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH
|
||
DAMAGE. */
|
||
|
||
#include "sendmail.h"
|
||
#include "myssl.h"
|
||
#define FILE_NUM 34
|
||
/* Copyright (c) 1990-1992 by the University of Illinois Board of Trustees */
|
||
/************************************************************************
|
||
* functions for dealing with a sendmail (or other?) smtp server
|
||
************************************************************************/
|
||
|
||
#pragma segment SMTP
|
||
typedef enum
|
||
{
|
||
helo=1, mail, rcpt, data, rset, send, soml, saml,
|
||
vrfy, expn, help, noop, quit, turn, ehlo, auth, starttls
|
||
} SMTPEnum;
|
||
|
||
typedef enum {k1342LWSP,k1342Word,k1342Plain,k1342End} Enum1342;
|
||
typedef struct {
|
||
Str255 word;
|
||
Enum1342 wordType;
|
||
} Token1342, *Token1342Ptr;
|
||
|
||
typedef struct
|
||
{
|
||
Boolean sawGreeting;
|
||
long maxSize;
|
||
Boolean mime8bit;
|
||
Boolean pipeline;
|
||
SASLEnum saslMech;
|
||
Boolean starttls;
|
||
Str255 digest;
|
||
} EhloStuff, *EhloStuffPtr, **EhloStuffHandle;
|
||
|
||
EhloStuffHandle Ehlo;
|
||
|
||
#define RingNext(pointer,array,size) ((array) + (((pointer)-(array))+1)%(size))
|
||
#define kSpecialSendDidntPanOut -1
|
||
#define CMD_BUFFER 1024
|
||
typedef struct wdsEntry *WDSPtr;
|
||
/************************************************************************
|
||
* declarations for private routines
|
||
************************************************************************/
|
||
OSErr SendPtrHead(TransStream stream, Ptr label, long labelLen, Ptr body, long bodyLen, Boolean allowQP,short tid);
|
||
OSErr SendRawMIME(TransStream stream,FSSpecPtr spec);
|
||
OSErr SendEnriched(TransStream stream,UHandle text,DecoderFunc *encoder);
|
||
void EhloLine(UPtr line,long size);
|
||
int DoIntroductions(TransStream stream);
|
||
OSErr DoSMTPAuth(TransStream stream);
|
||
int SayByeBye(TransStream stream);
|
||
int SendCmd(TransStream stream, int cmd, UPtr args, AccuPtr argsAcc);
|
||
int SendCmdGetReply(TransStream stream, int cmd, UPtr args, Boolean chatter,MessHandle messH);
|
||
int SendHeaderLine(TransStream stream,MessHandle messH,short index,Boolean allowQP,short tid);
|
||
int SendAttachments(TransStream stream,MessHandle messH,long flags,PStr boundary,short tableID,short idBase);
|
||
OSErr SendNewsGroups(TransStream stream,AccuPtr newsGroupAcc,short tid);
|
||
OSErr SendCID(TransStream stream,MessHandle messH,long part,short n);
|
||
int SMTPCmdError(int cmd, UPtr args, UPtr message);
|
||
void PrimeProgress(MessHandle messH);
|
||
OSErr SendDigest(TransStream stream,FSSpecPtr spec);
|
||
int WannaSend(MyWindowPtr win);
|
||
short SendXSender(TransStream stream,MessHandle messH);
|
||
OSErr SendMIMEHeaders(TransStream stream,MessHandle messH,UHandle enriched,PStr boundary,emsMIMEHandle *tlMIME,Boolean isRelated);
|
||
OSErr SendContentType(TransStream stream,Handle text1, long offset1, Handle text2, long offset2, short tableID,long *flags,long *opts,PStr name,emsMIMEHandle *tlMIME,PStr subType);
|
||
long DecideEncoding(Handle text1, Handle text2,Boolean anyfunny,short etid,long flags);
|
||
Boolean SevenBitTable(short tableID);
|
||
Boolean Any2022(Handle text,long offset);
|
||
PStr NameCharset(PStr charset,short tid,emsMIMEHandle *tlMIME);
|
||
Boolean LongerThan(Handle text, short len);
|
||
Boolean LongerWordThan(Handle text, short len);
|
||
Handle Encode1342(UPtr source,long len,short lineLimit,short *charsOnLine,PStr nl,short tid);
|
||
void Encode1342String(PStr s,short tid);
|
||
void Next1342Word(UPtr *startP,UPtr end,Token1342Ptr current,PStr delim,Boolean *wasQuote,Boolean *encQuote);
|
||
OSErr SendAnonFTP(TransStream stream,FSSpecPtr spec);
|
||
OSErr SendSpecial(TransStream stream,FSSpecPtr spec,AttMapPtr amp);
|
||
OSErr SendAddressHead(TransStream stream,PETEHandle pte,HSPtr hs,Boolean allowQP,short tid);
|
||
OSErr SendNormalHead(TransStream stream,PETEHandle pte,HSPtr hs,Boolean allowQP,short tid);
|
||
OSErr SendSubjectHead(TransStream stream,PETEHandle pte,HSPtr hs, Boolean allowQP,short tid);
|
||
//OSErr SendPipeRCPT(TransStream stream,PStr newRecip,AccuPtr pipe,Boolean chatter);
|
||
OSErr SendPipeRCPT(TransStream stream,PStr newRecip,AccuPtr pipe,Boolean chatter, MessHandle messH);
|
||
OSErr PeriodEncoder(CallType callType,DecoderPBPtr pb);
|
||
long StuffPeriods(UPtr in, long inLen, UPtr out, PStr newLine, short *nlStatePtr);
|
||
int SendRelatedParts(TransStream stream,MessHandle messH,long flags,StackHandle parts,PStr boundary);
|
||
int SendAnAttachment(TransStream stream,MessHandle messH,long flags,Boolean canQP,Boolean plainText,short tableID,PStr boundary,FSSpecPtr spec,short multiID,short partID);
|
||
int SendAttachmentFolder(TransStream stream,MessHandle messH,long flags,Boolean canQP,Boolean plainText,short tableID,PStr boundary,FSSpecPtr folderSpec,short multiID,short partID,short *partBase,CInfoPBRec *hfi);
|
||
int sErr;
|
||
void ConvertPictPart(FSSpecPtr origSpec,FSSpecPtr spec);
|
||
OSErr FlattenAndSpool(FSSpecPtr spec);
|
||
OSErr FlattenQTMovie(FSSpecPtr inSpec,FSSpecPtr outSpec);
|
||
OSErr AllAttachOnBoard(MessHandle messH);
|
||
|
||
OSErr TransmitMessageMixed(TransmitPBPtr pb,Boolean topLevel);
|
||
OSErr TransmitMessageRelated(TransmitPBPtr pb,Boolean topLevel);
|
||
OSErr TransmitMessageText(TransmitPBPtr pb,Boolean sigToo,Boolean topLevel);
|
||
OSErr TransmitMessageBody(TransmitPBPtr pb,Boolean withClosure);
|
||
OSErr TransmitMessageBodyHeaders(TransmitPBPtr pb,Boolean sigToo,Boolean topLevel);
|
||
OSErr TransmitTopHeaders(TransmitPBPtr pb);
|
||
OSErr TransmitMultiHeaders(TransmitPBPtr pb,short subType,PStr boundary,short otherParm,PStr otherVal);
|
||
OSErr TransmitMessageTextBloat(TransmitPBPtr pb,Boolean sigToo,Boolean topLevel);
|
||
OSErr TransmitMessageTextStrip(TransmitPBPtr pb,Boolean sigToo,Boolean topLevel);
|
||
OSErr TransmitMessageTextPlain(TransmitPBPtr pb,Boolean sigToo,Boolean topLevel);
|
||
OSErr TransmitMessageTextRich(TransmitPBPtr pb,Boolean sigToo,Boolean topLevel);
|
||
OSErr TransmitMessageSig(TransmitPBPtr pb);
|
||
OSErr TransmitMessageSigBloat(TransmitPBPtr pb);
|
||
OSErr TransmitMessageSigBody(TransmitPBPtr pb,Boolean withHeaders);
|
||
OSErr TransmitMessageSigBodyPlain(TransmitPBPtr pb);
|
||
|
||
/************************************************************************
|
||
* Public routines
|
||
************************************************************************/
|
||
|
||
/************************************************************************
|
||
* StartSMTP - initiate a connection with the specified SMTP server
|
||
************************************************************************/
|
||
int StartSMTP(TransStream stream, UPtr serverName, long port)
|
||
{
|
||
if (UUPCOut) sErr=UUPCPrime(serverName);
|
||
else if (!(sErr=ConnectTrans(stream,serverName,port,False,GetRLong(OPEN_TIMEOUT))))
|
||
sErr=DoIntroductions(stream);
|
||
return(sErr);
|
||
}
|
||
|
||
/************************************************************************
|
||
* MySendMessage - send a message to the SMTP server
|
||
************************************************************************/
|
||
int MySendMessage(TransStream stream,TOCHandle tocH,int sumNum,CSpecHandle specList)
|
||
{
|
||
WindowPtr messWinWP;
|
||
Str255 buffer;
|
||
Str255 param;
|
||
MessHandle messH;
|
||
Accumulator newsGroupAcc;
|
||
|
||
Zero(newsGroupAcc);
|
||
|
||
/*
|
||
* handle open, dirty windows
|
||
*/
|
||
if (!(messH=SaveB4Send(tocH,sumNum))) return(1);
|
||
|
||
messWinWP = GetMyWindowWindowPtr((*messH)->win);
|
||
/*
|
||
* Log, if we must
|
||
*/
|
||
GetWTitle(messWinWP,buffer);
|
||
ComposeLogR(LOG_SEND,nil,SENDING,buffer);
|
||
|
||
#ifdef TWO
|
||
if (PrefIsSet(PREF_POP_SEND))
|
||
{
|
||
long size=sizeof(buffer);
|
||
*buffer = '+';
|
||
if (POPCmdGetReply(stream,kpcXmit,"",buffer,&size) || *buffer!='+')
|
||
{
|
||
if (*buffer=='-') POPCmdError(kpcXmit,"",buffer);
|
||
return(1);
|
||
}
|
||
}
|
||
else
|
||
#endif
|
||
{
|
||
/*
|
||
* reset SMTP
|
||
*/
|
||
sErr = SendCmdGetReply(stream,rset,nil,True,messH);
|
||
if (sErr/100 != 2) return(sErr);
|
||
sErr = 0;
|
||
|
||
/*
|
||
* envelope
|
||
*/
|
||
MessReturnAddr(messH,buffer);
|
||
if (Ehlo && *(*Ehlo)->digest)
|
||
{
|
||
ComposeString(param,"\p %r=%p",EsmtpStrn+esmtpAmd5,LDRef(Ehlo)->digest);
|
||
PSCat(buffer,param);
|
||
UL(Ehlo);
|
||
}
|
||
if (Ehlo && (*Ehlo)->maxSize)
|
||
{
|
||
ComposeString(param,"\p %r=%d",EsmtpStrn+esmtpSize,ApproxMessageSize(messH) K);
|
||
PSCat(buffer,param);
|
||
}
|
||
if (Ehlo && (*Ehlo)->mime8bit && PrefIsSet(PREF_ALLOW_8BITMIME))
|
||
{
|
||
ComposeRString(param,BODY_EQUALS,EsmtpStrn+esmtp8BMIME);
|
||
PSCat(buffer,param);
|
||
}
|
||
sErr = SendCmdGetReply(stream,mail,buffer,True,messH);
|
||
|
||
if (Ehlo && *(*Ehlo)->digest && IsAddrErr(sErr)) InvalidatePasswords(False,True,False);
|
||
if (sErr/100 != 2) return(sErr);
|
||
sErr = 0;
|
||
|
||
if (WrapWrong) OffsetWindow(messWinWP);
|
||
|
||
if (sErr=DoRcptTos(stream,messH,True,specList,&newsGroupAcc)) goto done;
|
||
}
|
||
|
||
(*messH)->newsGroupAcc = newsGroupAcc;
|
||
if (sErr=TransmitMessageHi(stream,messH,True,!PrefIsSet(PREF_POP_SEND))) goto done;
|
||
AccuZap(newsGroupAcc);
|
||
Zero((*messH)->newsGroupAcc);
|
||
|
||
//TimeStamp(tocH,sumNum,GMTDateTime(),ZoneSecs());
|
||
|
||
done:
|
||
(void)ComposeLogR(LOG_SEND,nil,sErr?LOG_FAILED:LOG_SUCCEEDED,sErr);
|
||
AccuZap(newsGroupAcc);
|
||
return(sErr);
|
||
}
|
||
|
||
/**********************************************************************
|
||
* MessReturnAddr - get the right return addr for this message; if addr
|
||
* is in "Me" nickname, returns from addr. Else returns configured addr
|
||
**********************************************************************/
|
||
PStr MessReturnAddr(MessHandle messH,PStr buffer)
|
||
{
|
||
Str255 from;
|
||
Str255 returnAddr;
|
||
short template = MessOptIsSet(messH,OPT_RECEIPT) && MessOptIsSet(messH,OPT_BULK) ? EMPTY_MFROM:NORMAL_MFROM;
|
||
|
||
// return address usually
|
||
GetReturnAddr(returnAddr,False);
|
||
|
||
// or contents of from field
|
||
if (!CompHeadGetStr(messH,FROM_HEAD,from) && !StringSame(returnAddr,from) && IsMe(from))
|
||
PCopy(returnAddr,from);
|
||
|
||
// strip <>'s
|
||
ShortAddr(returnAddr,returnAddr);
|
||
|
||
// and compose the argument
|
||
ComposeRString(buffer,template,returnAddr);
|
||
|
||
return(buffer);
|
||
}
|
||
|
||
/************************************************************************
|
||
* EndSMTP - done talking to SMTP
|
||
************************************************************************/
|
||
int EndSMTP(TransStream stream)
|
||
{
|
||
if (UUPCOut) UUPCDry(stream);
|
||
else
|
||
{
|
||
SilenceTrans(stream,True); // ignore all TCP/IP errors from here on out -jdboyd 030113
|
||
if ((!sErr || (sErr<600 && sErr>=400)) && !(sErr=SayByeBye(stream)))
|
||
DisTrans(stream);
|
||
sErr = DestroyTrans(stream);
|
||
}
|
||
ZapHandle(eSignature);
|
||
ZapHandle(RichSignature);
|
||
ZapHandle(HTMLSignature);
|
||
ZapHandle(Ehlo);
|
||
return(sErr);
|
||
}
|
||
|
||
/************************************************************************
|
||
* SMTPError - return the last SMTP error
|
||
************************************************************************/
|
||
int SMTPError(TransStream stream)
|
||
{
|
||
return (sErr);
|
||
}
|
||
|
||
/************************************************************************
|
||
* Private routines
|
||
************************************************************************/
|
||
/************************************************************************
|
||
* DoIntroductions - take care of the beginning of the SMTP protocol
|
||
************************************************************************/
|
||
int DoIntroductions(TransStream stream)
|
||
{
|
||
Str255 buffer;
|
||
|
||
Ehlo = NewZH(EhloStuff);
|
||
|
||
/*
|
||
* get banner from the remote end
|
||
*/
|
||
sErr = GetReply(stream,buffer,sizeof(buffer),True,False);
|
||
if (sErr/100 != 2) return(sErr);
|
||
sErr = 0;
|
||
|
||
SayHello :
|
||
/*
|
||
* tell it who we are
|
||
*/
|
||
sErr = SendCmdGetReply(stream,ehlo,WhoAmI(stream,buffer),True,nil);
|
||
if (sErr>=400) sErr = SendCmdGetReply(stream,helo,WhoAmI(stream,buffer),True,nil);
|
||
|
||
if (sErr/100 == 2)
|
||
{
|
||
#ifdef ESSL
|
||
if( ShouldUseSSL(stream) && !(stream->ESSLSetting & esslSSLInUse))
|
||
{
|
||
if (!(*Ehlo)->starttls)
|
||
{
|
||
if(!(stream->ESSLSetting & esslOptional))
|
||
{
|
||
sErr = 502;
|
||
// SMTPCmdError(starttls,nil,GetRString(buffer,SSL_ERR_STRING)+1);
|
||
ComposeStdAlert ( Note, ALRTStringsStrn+NO_SERVER_SSL );
|
||
|
||
}
|
||
}
|
||
else
|
||
{
|
||
OSStatus sslErr;
|
||
int tempErr;
|
||
|
||
tempErr = SendCmdGetReply(stream,starttls,nil,(stream->ESSLSetting & esslOptional) != 0,nil);
|
||
if(tempErr/100 == 2)
|
||
{
|
||
sslErr = ESSLStartSSL(stream);
|
||
if(sslErr)
|
||
{
|
||
if(!(stream->ESSLSetting & esslOptional))
|
||
{
|
||
sErr = 554;
|
||
SMTPCmdError(starttls,nil,GetRString(buffer,SSL_ERR_STRING)+1);
|
||
}
|
||
}
|
||
else if(stream->ESSLSetting & esslSSLInUse)
|
||
{
|
||
ZeroHandle(Ehlo);
|
||
goto SayHello;
|
||
}
|
||
}
|
||
else if(!(stream->ESSLSetting & esslOptional))
|
||
{
|
||
sErr = tempErr;
|
||
}
|
||
}
|
||
}
|
||
#endif
|
||
if ((sErr/100 == 2))
|
||
{
|
||
if (!PrefIsSet(PREF_SMTP_AUTH_NOTOK))
|
||
{
|
||
short (*authfunc)(TransStream stream) = nil;
|
||
|
||
if (!(*Ehlo)->saslMech)
|
||
{
|
||
ComposeLogS(LOG_PROTO,nil,"\pSMTP auth not available, disabling");
|
||
SetPref(PREF_SMTP_GAVE_530,NoStr);
|
||
SetPref(PREF_SMTP_DOES_AUTH,NoStr);
|
||
}
|
||
else
|
||
{
|
||
SetPref(PREF_SMTP_DOES_AUTH,YesStr);
|
||
if (PrefIsSet(PREF_KERBEROS) || *(*CurPers)->password)
|
||
{
|
||
ComposeLogS(LOG_PROTO,nil,"\pSMTP auth %r under way...",SASLStrn+(*Ehlo)->saslMech);
|
||
// We have everything we need to attempt authentication
|
||
sErr = DoSMTPAuth(stream);
|
||
}
|
||
else
|
||
ComposeLogS(LOG_PROTO,nil,"\pSMTP auth %r not being attempted, no credentials",SASLStrn+(*Ehlo)->saslMech);
|
||
}
|
||
}
|
||
else if ((*Ehlo)->saslMech)
|
||
ComposeLogS(LOG_PROTO,nil,"\pSMTP auth %r available but forbidden",SASLStrn+(*Ehlo)->saslMech);
|
||
}
|
||
}
|
||
|
||
return((sErr/100 != 2) ? sErr : (sErr=0));
|
||
}
|
||
|
||
/************************************************************************
|
||
* DoSMTPAuth - authenticate for SMTP
|
||
************************************************************************/
|
||
OSErr DoSMTPAuth(TransStream stream)
|
||
{
|
||
Accumulator chalAcc, respAcc;
|
||
short rounds = 0;
|
||
Str63 service;
|
||
long state;
|
||
|
||
Zero(chalAcc);
|
||
Zero(respAcc);
|
||
|
||
// put auth command in initial response
|
||
AccuAddRes(&respAcc,EsmtpStrn+esmtpAuth);
|
||
AccuAddChar(&respAcc,' ');
|
||
|
||
// grab service name for kerberos
|
||
GetRString(service,K5_SMTP_SERVICE);
|
||
|
||
// run the mechanism
|
||
do
|
||
{
|
||
// Build the response
|
||
if (SASLDo(service,(*Ehlo)->saslMech,rounds++,&state,&chalAcc,&respAcc)) sErr = 601;
|
||
else
|
||
{
|
||
// Send the response
|
||
if (SendCmd(stream,0,nil,&respAcc)) sErr = 601;
|
||
else
|
||
{
|
||
// get the reply
|
||
sErr = GetReplyLo(stream,nil,0,&respAcc,false,false);
|
||
chalAcc.offset = 0;
|
||
if (sErr==334)
|
||
{
|
||
UPtr spot;
|
||
|
||
// extract the challenge token
|
||
AccuAddChar(&respAcc,0);
|
||
spot = *respAcc.data;
|
||
while (*spot && !IsWhite(*spot)) spot++; // skip code
|
||
|
||
// rest will be base64 or whitespace, the decoder won't care
|
||
AccuAddFromHandle(&chalAcc,respAcc.data,spot-*respAcc.data,respAcc.offset-(spot-*respAcc.data)-1);
|
||
}
|
||
}
|
||
}
|
||
|
||
// the response needs no pre-loading anymore.
|
||
respAcc.offset = 0;
|
||
}
|
||
while (sErr/100 == 3);
|
||
|
||
// if the server rejects our auth, but doesn't actually
|
||
// require auth for anything, pretend like nothing happened
|
||
if (sErr/100==5 && !ShouldSMTPAuth()) sErr = 0;
|
||
|
||
// Kill the accumulators
|
||
AccuZap(chalAcc);
|
||
AccuZap(respAcc);
|
||
|
||
// Let the sasl mechanism know how it all came out
|
||
SASLDone(service,(*Ehlo)->saslMech,rounds,&state,sErr);
|
||
|
||
return sErr;
|
||
}
|
||
|
||
/************************************************************************
|
||
* SayByeBye - take care of the end of the SMTP protocol
|
||
************************************************************************/
|
||
int SayByeBye(TransStream stream)
|
||
{
|
||
sErr = SendCmdGetReply(stream,quit,nil,False,nil);
|
||
return((sErr/100 != 2) ? sErr : (sErr=0));
|
||
}
|
||
|
||
/************************************************************************
|
||
* SendCmd - send an smtp command, with optional arguments
|
||
************************************************************************/
|
||
int SendCmd(TransStream stream, int cmd, UPtr args, AccuPtr argsAcc)
|
||
{
|
||
Byte buffer[CMD_BUFFER];
|
||
|
||
GetRString(buffer,SMTP_STRN+cmd);
|
||
if (args && *args)
|
||
PCat(buffer,args);
|
||
if (cmd) ProgressMessage(kpMessage,buffer);
|
||
|
||
if (!argsAcc)
|
||
{
|
||
PCat(buffer,NewLine);
|
||
if (sErr=SendPString(stream,buffer)) return(sErr);
|
||
}
|
||
else
|
||
{
|
||
// send the command
|
||
if (cmd) PCatC(buffer,' ');
|
||
if (sErr=SendPString(stream,buffer)) return(sErr);
|
||
|
||
// add a newline to the accumulator
|
||
AccuAddStr(argsAcc,NewLine);
|
||
|
||
// send the data
|
||
sErr = SendTrans(stream,LDRef(argsAcc->data),argsAcc->offset,nil);
|
||
|
||
// erase what we did to the accumulator
|
||
UL(argsAcc->data);
|
||
argsAcc->offset -= *NewLine;
|
||
|
||
if (sErr) return sErr;
|
||
}
|
||
|
||
return(noErr);
|
||
}
|
||
|
||
/************************************************************************
|
||
* SMTPCmdError - report an error for an SMTP command
|
||
************************************************************************/
|
||
int SMTPCmdError(int cmd, UPtr args, UPtr message)
|
||
{
|
||
Str255 theCmd;
|
||
Str255 theError;
|
||
int err;
|
||
|
||
GetRString(theCmd,2800+cmd);
|
||
if (args && *args)
|
||
PCat(theCmd,args);
|
||
strcpy(theError+1,message);
|
||
*theError = strlen(theError+1);
|
||
if (theError[*theError]=='\012') (*theError)--;
|
||
if (theError[*theError]=='\015') (*theError)--;
|
||
MyParamText(theCmd,theError,"\pSMTP","");
|
||
err = ReallyDoAnAlert(PROTO_ERR_ALRT,Note);
|
||
return(err);
|
||
}
|
||
|
||
/************************************************************************
|
||
* SendCmdGetReply - send an smtp command, with optional arguments, and
|
||
* wait for the reply. Returns reply code.
|
||
************************************************************************/
|
||
int SendCmdGetReply(TransStream stream, int cmd, UPtr args, Boolean chatter,MessHandle messH)
|
||
{
|
||
char buffer[CMD_BUFFER];
|
||
|
||
if (sErr=SendCmd(stream,cmd,args,nil)) return(601); /* error in transmission */
|
||
sErr = GetReply(stream,buffer,sizeof(buffer),False,cmd==ehlo);
|
||
if (cmd==rcpt && sErr>499 && sErr<600) sErr = 550;
|
||
if (sErr>399 && sErr<=600 && (IsAddrErr(sErr)||cmd!=rcpt) && cmd!=ehlo && chatter)
|
||
SMTPCmdError(cmd?cmd:auth,(cmd&&cmd!=auth)?args:nil,buffer);
|
||
if (messH && IsAddrErr(sErr) && cmd!=rcpt)
|
||
{
|
||
if (strchr(buffer,'\015')) *strchr(buffer,'\015') = 0;
|
||
AddOutgoingMesgError((*messH)->sumNum,SumOf(messH)->uidHash,sErr,BAD_XMIT_ERR_TEXT,"",buffer);
|
||
}
|
||
return(sErr);
|
||
}
|
||
|
||
|
||
/************************************************************************
|
||
* EhloLine - process an Ehlo return
|
||
************************************************************************/
|
||
void EhloLine(UPtr line,long size)
|
||
{
|
||
Str63 directive;
|
||
Str255 value,digest;
|
||
UPtr start,end,stop;
|
||
long longVal;
|
||
|
||
if (!Ehlo) return;
|
||
|
||
if ((*Ehlo)->sawGreeting)
|
||
{
|
||
(*Ehlo)->sawGreeting = True;
|
||
return;
|
||
}
|
||
|
||
// Stupid AUTH= nonsense
|
||
GetRString(value,EsmtpStrn+esmtpAuth);
|
||
PCatC(value,'=');
|
||
if (start=PPtrFindSub(value,line,size))
|
||
start[4] = ' ';
|
||
|
||
/*
|
||
* grab the Ehlo directive
|
||
*/
|
||
end = line+size;
|
||
start = line+4;
|
||
while (start<end && IsWhite(*start)) start++;
|
||
for (stop=start;stop<end && !IsWhite(*stop);stop++);
|
||
MakePStr(directive,start,stop-start);
|
||
if (directive[*directive]=='\r') --*directive;
|
||
|
||
/*
|
||
* and the value
|
||
*/
|
||
while (stop<end && IsWhite(*stop)) stop++;
|
||
MakePStr(value,stop,size-(stop-line));
|
||
|
||
/*
|
||
* now what?
|
||
*/
|
||
switch(FindSTRNIndex(EsmtpStrn,directive))
|
||
{
|
||
case esmtpSize:
|
||
(*Ehlo)->maxSize = 0x7fffffff;
|
||
if (*value <= 9) // avoid rilly big numbers
|
||
{
|
||
StringToNum(value,&longVal);
|
||
if (longVal > 1 K) (*Ehlo)->maxSize = longVal;
|
||
ComposeLogS(LOG_PROTO,nil,"\pESMTP size %d",longVal);
|
||
}
|
||
else
|
||
ComposeLogS(LOG_PROTO,nil,"\pESMTP size invalid (%p)",value);
|
||
break;
|
||
case esmtp8BMIME:
|
||
(*Ehlo)->mime8bit = True;
|
||
ComposeLogS(LOG_PROTO,nil,"\pESMTP mime8bit");
|
||
break;
|
||
case esmtpAuth:
|
||
{
|
||
SASLEnum mech = (*Ehlo)->saslMech;
|
||
UPtr spot = value+1;
|
||
Str31 service;
|
||
|
||
GetRString(service,K5_SMTP_SERVICE);
|
||
|
||
while (PToken(value,directive,&spot," \011\012\015"))
|
||
mech = SASLFind(service,directive, mech);
|
||
|
||
(*Ehlo)->saslMech = mech;
|
||
if (mech) ComposeLogS(LOG_PROTO,nil,"\pESMTP SASL mech %r",SASLStrn+mech);
|
||
}
|
||
break;
|
||
case esmtpAmd5:
|
||
SetPref(PREF_SMTP_DOES_AUTH,YesStr);
|
||
PSCopy(directive,(*CurPers)->password);
|
||
GenDigest(value,directive,digest);
|
||
PCopy((*Ehlo)->digest,digest);
|
||
ComposeLogS(LOG_PROTO,nil,"\pESMTP AMD5 auth found");
|
||
break;
|
||
case esmtpPipeline:
|
||
(*Ehlo)->pipeline = True;
|
||
ComposeLogS(LOG_PROTO,nil,"\pESMTP pipeline");
|
||
break;
|
||
case esmtpStartTLS:
|
||
(*Ehlo)->starttls = True;
|
||
ComposeLogS(LOG_PROTO,nil,"\pESMTP starttls");
|
||
break;
|
||
default:
|
||
ComposeLogS(LOG_PROTO,nil,"\pESMTP has NFI what <20>%p<> is supposed to mean",directive);
|
||
break;
|
||
}
|
||
}
|
||
|
||
/************************************************************************
|
||
* DoRcptTos - tell the remote sendmail who is getting the message
|
||
************************************************************************/
|
||
int DoRcptTos(TransStream stream,MessHandle messH, Boolean chatter, CSpecHandle fccList,AccuPtr newsGroupAcc)
|
||
{
|
||
sErr=DoRcptTosFrom(stream,messH,TO_HEAD,chatter,fccList,newsGroupAcc);
|
||
if (sErr) return(sErr);
|
||
sErr=DoRcptTosFrom(stream,messH,BCC_HEAD,chatter,fccList,newsGroupAcc);
|
||
if (sErr) return(sErr);
|
||
sErr=DoRcptTosFrom(stream,messH,CC_HEAD,chatter,fccList,newsGroupAcc);
|
||
return(sErr);
|
||
}
|
||
|
||
/************************************************************************
|
||
* DoRcptTosFrom - do the Rcpt to's from a particular TERec
|
||
************************************************************************/
|
||
int DoRcptTosFrom(TransStream stream, MessHandle messH, short index, Boolean chatter,CSpecHandle fccSpecs,AccuPtr newsGroupAcc)
|
||
{
|
||
Str255 toWhom;
|
||
UHandle addresses=nil;
|
||
UHandle rawAddresses;
|
||
UPtr address;
|
||
HeadSpec hs;
|
||
UHandle text;
|
||
Boolean evilSendmail=PrefIsSet(PREF_EVIL_SENDMAIL);
|
||
UPtr spot;
|
||
Str255 server;
|
||
long junk;
|
||
Accumulator pipe;
|
||
short err;
|
||
|
||
Zero(pipe);
|
||
|
||
if (evilSendmail)
|
||
{
|
||
GetSMTPInfo(server);
|
||
if (DotToNum(server,&junk))
|
||
{
|
||
/* ip address; turn into domain literal */
|
||
PInsert(server,sizeof(server),"\p[",server+1);
|
||
PCatC(server,']');
|
||
}
|
||
}
|
||
|
||
sErr = 550;
|
||
if (CompHeadFind(messH,index,&hs) && !CompHeadGetText(TheBody,&hs,&text))
|
||
{
|
||
if (!(sErr=SuckAddresses(&rawAddresses,text,False,True,False,nil)))
|
||
{
|
||
sErr=200;
|
||
if (**rawAddresses)
|
||
{
|
||
ExpandAliases(&addresses,rawAddresses,0,False);
|
||
ZapHandle(rawAddresses);
|
||
if (!addresses)
|
||
{
|
||
AddOutgoingMesgError ((*messH)->sumNum, (*(*messH)->tocH)->sums[(*messH)->sumNum].uidHash, sErr, BAD_ADDRESS);
|
||
return(sErr=550);
|
||
}
|
||
for (address=LDRef(addresses); *address; address += *address + 2)
|
||
{
|
||
/*
|
||
* skip groups
|
||
*/
|
||
if (address[*address]==':' || address[1]==';') continue; /* skip group identifiers */
|
||
|
||
/*
|
||
* handle Fcc's
|
||
*/
|
||
//Folder Carbon Copy - do not support FCC in Light
|
||
if (HasFeature (featureFcc)) {
|
||
if (IsFCCAddr(address))
|
||
{
|
||
if (fccSpecs)
|
||
{
|
||
if (sErr=AddFccToList(address,fccSpecs)) break;
|
||
}
|
||
continue;
|
||
}
|
||
else if (IsNewsgroupAddr(address))
|
||
{
|
||
if (newsGroupAcc)
|
||
{
|
||
sErr = noErr;
|
||
if (newsGroupAcc->offset) sErr = AccuAddRes(newsGroupAcc,COMMA_SPACE);
|
||
if (!sErr) sErr=AccuAddPtr(newsGroupAcc,address+2,*address-1);
|
||
if (sErr) break;
|
||
}
|
||
continue;
|
||
}
|
||
}
|
||
|
||
if (*address > MAX_ALIAS)
|
||
{
|
||
sErr = 550;
|
||
AddOutgoingMesgError ((*messH)->sumNum, (*(*messH)->tocH)->sums[(*messH)->sumNum].uidHash, sErr, BAD_ADDRESS);
|
||
break;
|
||
}
|
||
if (UUPCOut)
|
||
{
|
||
if (sErr=UUPCWriteAddr(address)) break;
|
||
}
|
||
else
|
||
{
|
||
toWhom[0] = 1; toWhom[1] = '<';
|
||
if (evilSendmail && *toWhom+*server+6<sizeof(toWhom))
|
||
{
|
||
for (spot=address+*address;spot>address;spot--)
|
||
if (*spot=='@') *spot = '%';
|
||
}
|
||
PCat(toWhom,address);
|
||
if (evilSendmail && *toWhom+*server+6<sizeof(toWhom))
|
||
{
|
||
PCatC(toWhom,'@');
|
||
PSCat(toWhom,server);
|
||
}
|
||
PCatC(toWhom,'>');
|
||
|
||
if (Ehlo && (*Ehlo)->pipeline)
|
||
sErr = SendPipeRCPT(stream,toWhom,&pipe,chatter, messH);
|
||
else
|
||
{
|
||
char buffer[CMD_BUFFER];
|
||
|
||
if (sErr=SendCmd(stream,rcpt,toWhom,nil)) sErr = 601; /* error in transmission */
|
||
sErr = GetReply(stream,buffer,sizeof(buffer),False,false);
|
||
if (sErr>499 && sErr<600) sErr = 550;
|
||
if (IsAddrErr(sErr))
|
||
{
|
||
c2pstr(buffer); if (buffer[*buffer]=='\r') --*buffer;
|
||
AddOutgoingMesgError ((*messH)->sumNum, (*(*messH)->tocH)->sums[(*messH)->sumNum].uidHash, sErr, BAD_ADDRESS_ERR_TEXT,address,buffer);
|
||
}
|
||
}
|
||
if (sErr/100 != 2) {chatter = False;break;}
|
||
}
|
||
}
|
||
ZapHandle(addresses);
|
||
if (Ehlo && (*Ehlo)->pipeline)
|
||
{
|
||
err = SendPipeRCPT(stream,nil,&pipe,chatter, messH);
|
||
if (sErr/100==2 || !sErr) sErr = err;
|
||
}
|
||
}
|
||
else
|
||
ZapHandle(rawAddresses);
|
||
}
|
||
else
|
||
{
|
||
sErr = 550;
|
||
AddOutgoingMesgError ((*messH)->sumNum, (*(*messH)->tocH)->sums[(*messH)->sumNum].uidHash, sErr, BAD_ADDRESS);
|
||
}
|
||
ZapHandle(text);
|
||
}
|
||
return(sErr/100!=2 ? sErr : (sErr=0));
|
||
}
|
||
|
||
/************************************************************************
|
||
* SendPipeRCPT - send rcpt to commands in a pipeline
|
||
************************************************************************/
|
||
OSErr SendPipeRCPT(TransStream stream,PStr newRecip,AccuPtr pipe,Boolean chatter, MessHandle messH)
|
||
{
|
||
OSErr err = 200;
|
||
OSErr firstErr = 200;
|
||
char buffer[CMD_BUFFER];
|
||
Str255 address;
|
||
|
||
/*
|
||
* reap outstanding rcpts if need be
|
||
*/
|
||
while (!newRecip && pipe->offset || pipe->offset > 3 K)
|
||
{
|
||
PCopy(address,*pipe->data);
|
||
err = GetReply(stream,buffer,sizeof(buffer),chatter,false);
|
||
if (err>499 && err<600) err = 550;
|
||
if (firstErr/100==2) firstErr = err;
|
||
if (err/100 != 2)
|
||
{
|
||
if (chatter && IsAddrErr(err))
|
||
{
|
||
c2pstr(buffer); if (buffer[*buffer]=='\r') --*buffer;
|
||
AddOutgoingMesgError ((*messH)->sumNum, (*(*messH)->tocH)->sums[(*messH)->sumNum].uidHash, err, BAD_ADDRESS_ERR_TEXT,address,buffer);
|
||
}
|
||
chatter = False;
|
||
}
|
||
// take the first address out of the accumulator
|
||
BMD(*pipe->data+*address+1,*pipe->data,pipe->offset-*address-1);
|
||
pipe->offset -= *address+1;
|
||
}
|
||
|
||
/*
|
||
* send current address
|
||
*/
|
||
if (err/100 == 2 && newRecip)
|
||
{
|
||
if (err=SendCmd(stream,rcpt,newRecip,nil)) return(601); /* error in transmission */
|
||
if (err=AccuAddPtr(pipe,newRecip,*newRecip+1))
|
||
{
|
||
WarnUser(MEM_ERR,err);
|
||
return(601);
|
||
}
|
||
}
|
||
return(firstErr);
|
||
}
|
||
|
||
/**********************************************************************
|
||
* AddFccToList - add a mailbox to the fcc list.
|
||
**********************************************************************/
|
||
OSErr AddFccToList(PStr fcc,CSpecHandle list)
|
||
{
|
||
FSSpec spec;
|
||
Str255 name;
|
||
Str15 prefix;
|
||
OSErr err;
|
||
|
||
UseFeature (featureFcc);
|
||
PSCopy(name,fcc);
|
||
if (name[1]=='"')
|
||
{
|
||
BMD(name+2,name+1,*name-1);
|
||
*name -= 2;
|
||
}
|
||
|
||
TrimPrefix(name,GetRString(prefix,FCC_PREFIX));
|
||
|
||
if (err=BoxSpecByName(&spec,name))
|
||
return(FileSystemError(NOT_MAILBOX,name,err));
|
||
|
||
AddSpecToList(&spec,list);
|
||
|
||
if (err=MemError()) WarnUser(MEM_ERR,err);
|
||
return(err);
|
||
}
|
||
|
||
/************************************************************************
|
||
* TransmitMessage - send a message to the remote sendmail
|
||
************************************************************************/
|
||
int TransmitMessage(TransStream stream, MessHandle messH, Boolean chatter,Boolean mime,Boolean others,emsMIMEHandle *tlMIME,Boolean sendDataCmd)
|
||
{
|
||
return TransmitMessageLo(stream, messH, chatter, mime, others, tlMIME, sendDataCmd, !UUPCOut, true);
|
||
}
|
||
|
||
/************************************************************************
|
||
* TransmitMessageLo - send a message to the remote sendmail
|
||
************************************************************************/
|
||
int TransmitMessageLo(TransStream stream, MessHandle messH, Boolean chatter,Boolean mime,Boolean others,emsMIMEHandle *tlMIME,Boolean sendDataCmd,Boolean finishSMTP,Boolean doTopLevel)
|
||
{
|
||
TransmitPB pb;
|
||
FSSpec spec;
|
||
Str255 scratch;
|
||
FSSpec errSpec;
|
||
|
||
Zero(pb);
|
||
pb.messH = messH;
|
||
pb.mime = mime;
|
||
pb.others = others;
|
||
pb.receipt = MessOptIsSet(messH,OPT_BULK) && MessOptIsSet(messH,OPT_RECEIPT);
|
||
pb.parts = nil;
|
||
pb.isRelated = false;
|
||
pb.hasAttachments = 1!=GetIndAttachment(messH,1,&spec,&pb.hs);
|
||
pb.flags = SumOf(messH)->flags;
|
||
pb.opts = SumOf(messH)->opts;
|
||
pb.stream = stream;
|
||
pb.tlMIME = tlMIME;
|
||
pb.html = MessOptIsSet(messH,OPT_HTML);
|
||
pb.rich = MessFlagIsSet(messH,FLAG_RICH);
|
||
pb.allLWSP = !pb.hasAttachments && !pb.html && !pb.rich && IsAllLWSPMess(messH);
|
||
pb.hasSig = !pb.allLWSP && (SumOf(messH)->sigId!=-1) && !MessOptIsSet(messH,OPT_INLINE_SIG) && eSignature && *eSignature && GetHandleSize(eSignature);
|
||
pb.strip = !pb.allLWSP && MessOptIsSet(messH,OPT_STRIP) || MessOptIsSet(messH,OPT_JUST_EXCERPT);
|
||
pb.bloat = !pb.allLWSP && MessOptIsSet(messH,OPT_BLOAT);
|
||
if (pb.allLWSP) pb.flags &= ~FLAG_WRAP_OUT;
|
||
|
||
Zero(pb.enriched);
|
||
if (tlMIME && NewTLMIME(tlMIME)) goto fail;
|
||
|
||
PrimeProgress(messH);
|
||
|
||
//Make sure we have all the attachments
|
||
if (sErr = AllAttachOnBoard(messH)) goto fail;
|
||
|
||
CompHeadFind(messH,0,&pb.hs);
|
||
|
||
// Add back in the inline sig if removed...
|
||
pb.hs.stop = PeteLen(TheBody);
|
||
|
||
/*
|
||
* rich?
|
||
*/
|
||
if (pb.mime && !pb.strip && pb.html)
|
||
{
|
||
if (sErr=AccuInit(&pb.enriched)) goto fail;
|
||
if (sErr=HTMLPreamble(&pb.enriched,PCopy(scratch,SumOf(messH)->subj),0,False))
|
||
goto fail;
|
||
if (sErr=StackInit(sizeof(FSSpec),&pb.parts)) goto fail;
|
||
Zero(errSpec);
|
||
if (sErr=BuildHTML(&pb.enriched,TheBody,nil,pb.hs.stop,pb.hs.value,nil,nil,1,CompGetMID(messH,scratch),pb.parts,&errSpec))
|
||
{
|
||
if (errSpec.vRefNum)
|
||
{
|
||
// Report error with graphic file
|
||
AddOutgoingMesgError((*messH)->sumNum, (*(*messH)->tocH)->sums[(*messH)->sumNum].uidHash, sErr, GRAPHIC_FILE_ERR,sErr,errSpec.name);
|
||
}
|
||
sErr = 543;
|
||
goto fail;
|
||
}
|
||
AccuTrim(&pb.enriched);
|
||
pb.isRelated = pb.parts && (*pb.parts)->elCount > 0;
|
||
}
|
||
else if (pb.mime && !pb.strip && pb.rich)
|
||
{
|
||
if (sErr=AccuInit(&pb.enriched)) goto fail;
|
||
if (BuildEnriched(&pb.enriched,TheBody,nil,pb.hs.stop,pb.hs.value,nil,False))
|
||
goto fail;
|
||
}
|
||
|
||
if (sendDataCmd)
|
||
{
|
||
sErr = SendCmdGetReply(stream,data,nil,True,messH);
|
||
if (sErr && sErr/100!=3) goto fail;
|
||
}
|
||
|
||
if (pb.hasAttachments) sErr = TransmitMessageMixed(&pb,doTopLevel);
|
||
else if (pb.isRelated) sErr = TransmitMessageRelated(&pb,doTopLevel);
|
||
else sErr = TransmitMessageText(&pb,true,doTopLevel);
|
||
|
||
if (sErr) goto fail;
|
||
|
||
if (pb.mime) if (finishSMTP) sErr = FinishSMTP(stream,pb.messH);
|
||
|
||
done:
|
||
ZapHandle(pb.enriched.data);
|
||
ZapHandle(pb.parts);
|
||
return(sErr/100 != 2 ? sErr : (sErr=0));
|
||
|
||
fail:
|
||
if (IsAddrErr(sErr))
|
||
SumOf(messH)->state = MESG_ERR;
|
||
else
|
||
sErr = 600;
|
||
if (tlMIME) ZapTLMIME(*tlMIME);
|
||
goto done;
|
||
}
|
||
|
||
/************************************************************************
|
||
* AllAttachOnBoardLo - do we have all the attachments? No error reporting.
|
||
************************************************************************/
|
||
OSErr AllAttachOnBoardLo(MessHandle messH, Boolean errReport)
|
||
{
|
||
short index;
|
||
FSSpec spec;
|
||
OSErr err = noErr;
|
||
|
||
for (index=1;!err;index++)
|
||
{
|
||
if (err=GetIndAttachment(messH,index,&spec,nil))
|
||
if ((err!=1) && errReport)
|
||
{
|
||
AddOutgoingMesgError((*messH)->sumNum, (*(*messH)->tocH)->sums[(*messH)->sumNum].uidHash, err, ATTACH_MESS_ERR,err,spec.name);
|
||
FileSystemError(BINHEX_OPEN,spec.name,err);
|
||
}
|
||
}
|
||
if (err==1) err = noErr;
|
||
else if (err) err = 543;
|
||
return(err);
|
||
}
|
||
|
||
/************************************************************************
|
||
* AllAttachOnBoard - do we have all the attachments?
|
||
************************************************************************/
|
||
OSErr AllAttachOnBoard(MessHandle messH)
|
||
{
|
||
return AllAttachOnBoardLo(messH, true);
|
||
}
|
||
|
||
/************************************************************************
|
||
* TransmitMessageMixed - transmit a multipart/mixed message
|
||
************************************************************************/
|
||
OSErr TransmitMessageMixed(TransmitPBPtr pb,Boolean topLevel)
|
||
{
|
||
OSErr err;
|
||
Str127 boundary;
|
||
|
||
// build the boundary
|
||
BuildBoundary(pb->messH,boundary,"");
|
||
|
||
// mime-version first of all
|
||
if (topLevel) if (err=TransmitMimeVersion(pb)) return(err);
|
||
|
||
// send the non-mime headers
|
||
if (topLevel) if (err=TransmitTopHeaders(pb)) return(err);
|
||
|
||
// send the mime headers
|
||
if (pb->mime && (err=TransmitMultiHeaders(pb,MIME_MIXED,boundary,0,nil))) return(err);
|
||
|
||
if (pb->mime)
|
||
{
|
||
// send the header/body separator
|
||
if (err=SendPString(pb->stream,NewLine)) return(err);
|
||
|
||
// send the first boundary
|
||
if (err=SendBoundary(pb->stream)) return(err);
|
||
|
||
// send the message itself
|
||
if (pb->isRelated)
|
||
err = TransmitMessageRelated(pb,false);
|
||
else
|
||
err = TransmitMessageText(pb,false,false);
|
||
if (err) return(err);
|
||
|
||
// send the attachments
|
||
err = SendAttachments(pb->stream,pb->messH,pb->flags,boundary,SumOf(pb->messH)->tableId,pb->isRelated ? (*pb->parts)->elCount : 0);
|
||
if (err) return(err);
|
||
|
||
// signature?
|
||
if (pb->hasSig)
|
||
{
|
||
// attachment/signature boundary
|
||
if (err=SendBoundary(pb->stream)) return(err);
|
||
if (err=TransmitMessageSig(pb)) return(err);
|
||
}
|
||
|
||
// and the final boundary
|
||
PCat(boundary,"\p--");
|
||
if (err=SendBoundary(pb->stream)) return(err);
|
||
}
|
||
|
||
return(noErr);
|
||
}
|
||
|
||
/************************************************************************
|
||
* TransmitMessageRelated - transmit a multipart/related message
|
||
************************************************************************/
|
||
OSErr TransmitMessageRelated(TransmitPBPtr pb,Boolean topLevel)
|
||
{
|
||
OSErr err;
|
||
Str127 boundary;
|
||
Str31 textSlashHtml;
|
||
|
||
// build the boundary
|
||
BuildBoundary(pb->messH,boundary,"\pmr");
|
||
|
||
// mime-version first of all
|
||
if (topLevel) if (err=TransmitMimeVersion(pb)) return(err);
|
||
|
||
// send the non-mime headers
|
||
if (topLevel) if (err=TransmitTopHeaders(pb)) return(err);
|
||
|
||
// send the mime headers
|
||
if (pb->mime) if (err=TransmitMultiHeaders(pb,MIME_RELATED,boundary,AttributeStrn+aType,ComposeRString(textSlashHtml,THING_SLASH_THING,MIME_TEXT,HTMLTagsStrn+htmlTag))) return(err);
|
||
|
||
if (pb->mime)
|
||
{
|
||
// send the header/body separator
|
||
if (err=SendPString(pb->stream,NewLine)) return(err);
|
||
|
||
// send the first boundary
|
||
if (err=SendBoundary(pb->stream)) return(err);
|
||
|
||
// send the message itself
|
||
err = TransmitMessageText(pb,!pb->hasAttachments,false);
|
||
if (err) return(err);
|
||
|
||
// send the parts
|
||
err = SendRelatedParts(pb->stream,pb->messH,pb->flags,pb->parts,boundary);
|
||
if (err) return(err);
|
||
|
||
// and the final boundary
|
||
PCat(boundary,"\p--");
|
||
if (err=SendBoundary(pb->stream)) return(err);
|
||
}
|
||
|
||
return(noErr);
|
||
}
|
||
|
||
/************************************************************************
|
||
* TransmitMessageText - transmit body and sig, with or without bloat
|
||
************************************************************************/
|
||
OSErr TransmitMessageText(TransmitPBPtr pb,Boolean sigToo,Boolean topLevel)
|
||
{
|
||
OSErr err;
|
||
|
||
if (pb->bloat && !pb->strip && (pb->html||pb->rich))
|
||
{
|
||
// The user wants multipart/alternative
|
||
if (err = TransmitMessageTextBloat(pb,sigToo,topLevel)) return(err);
|
||
}
|
||
else if (!pb->strip && (pb->html||pb->rich))
|
||
{
|
||
// The user wants the rich version
|
||
if (err=TransmitMessageTextRich(pb,sigToo,topLevel)) return(err);
|
||
}
|
||
else
|
||
{
|
||
// The user is chicken
|
||
if (err=TransmitMessageTextStrip(pb,sigToo,topLevel)) return(err);
|
||
}
|
||
return(noErr);
|
||
}
|
||
|
||
/************************************************************************
|
||
* TransmitMessageTextBloat - transmit body and possibly sig with bloat
|
||
************************************************************************/
|
||
OSErr TransmitMessageTextBloat(TransmitPBPtr pb,Boolean sigToo,Boolean topLevel)
|
||
{
|
||
OSErr err;
|
||
Str127 boundary;
|
||
|
||
// we'll need a boundary
|
||
BuildBoundary(pb->messH,boundary,"\pma");
|
||
|
||
// mime-version first of all
|
||
if (topLevel) if (err=TransmitMimeVersion(pb)) return(err);
|
||
|
||
// send the non-mime headers
|
||
if (topLevel) if (err=TransmitTopHeaders(pb)) return(err);
|
||
|
||
// send the part headers, header/body separator, and initial boundary
|
||
if (pb->mime) if (err=TransmitMultiHeaders(pb,MIME_ALTERNATIVE,boundary,0,nil)) return(err);
|
||
|
||
if (pb->mime)
|
||
{
|
||
// now the header/body separator and the initial boundary
|
||
if (err=SendPString(pb->stream,NewLine)) return(err);
|
||
if (err=SendBoundary(pb->stream)) return(err);
|
||
|
||
// send the stripped version, not top-level
|
||
if (err=TransmitMessageTextStrip(pb,sigToo,false)) return(err);
|
||
|
||
// now the mid boundary
|
||
if (err=SendBoundary(pb->stream)) return(err);
|
||
|
||
// send the rich version, not top-level
|
||
if (err=TransmitMessageTextRich(pb,sigToo,false)) return(err);
|
||
|
||
// and the final boundary
|
||
PCat(boundary,"\p--");
|
||
if (err=SendBoundary(pb->stream)) return(err);
|
||
}
|
||
|
||
return(noErr);
|
||
}
|
||
|
||
/************************************************************************
|
||
* TransmitMessageTextStrip - strip styles before sending
|
||
************************************************************************/
|
||
OSErr TransmitMessageTextStrip(TransmitPBPtr pb,Boolean sigToo,Boolean topLevel)
|
||
{
|
||
Boolean oldFlat = Flatten!=nil;
|
||
OSErr err;
|
||
|
||
if ((pb->opts&OPT_BLOAT) || MessOptIsSet(pb->messH,FLAG_WRAP_OUT)) pb->flags |= FLAG_WRAP_OUT; // force wrapping on plain part of m/a
|
||
if ((pb->opts&OPT_BLOAT) && !Flatten) Flatten = GetFlatten(); // force flattening
|
||
ConvertExcerpt((*pb->messH)->bodyPTE,pb->hs.value,0x7fffffff,nil,nil); // and convert the excerpts
|
||
pb->hs.stop = PETEGetTextLen(PETE,(*pb->messH)->bodyPTE);
|
||
PeteCleanList((*pb->messH)->bodyPTE);
|
||
(*pb->messH)->win->isDirty = false;
|
||
|
||
// send it
|
||
if (err=TransmitMessageTextPlain(pb,sigToo,topLevel)) return(err);
|
||
|
||
// put flatten back
|
||
if (!oldFlat) ZapPtr(Flatten);
|
||
return(err);
|
||
}
|
||
|
||
/************************************************************************
|
||
* TransmitMessageTextPlain - send plaintext version of message & sig
|
||
************************************************************************/
|
||
OSErr TransmitMessageTextPlain(TransmitPBPtr pb,Boolean sigToo,Boolean topLevel)
|
||
{
|
||
OSErr err;
|
||
// save off some stuff
|
||
Boolean oldStrip = pb->strip; // save the old strip value
|
||
long oldOpts = pb->opts;
|
||
long oldFlags = pb->flags;
|
||
|
||
// we be strippin'
|
||
pb->strip = true;
|
||
pb->opts |= OPT_STRIP;
|
||
|
||
// mime-version first of all
|
||
if (topLevel) if (err=TransmitMimeVersion(pb)) return(err);
|
||
|
||
// send the non-mime headers
|
||
if (topLevel) if (err=TransmitTopHeaders(pb)) return(err);
|
||
|
||
// send the mime headers
|
||
if (pb->mime && !pb->receipt) if (err=TransmitMessageBodyHeaders(pb,sigToo,topLevel)) return(err);
|
||
|
||
if (pb->mime)
|
||
{
|
||
// send the header/body separator
|
||
if (!pb->receipt)
|
||
if (err=SendPString(pb->stream,NewLine)) return(err);
|
||
|
||
// send the message itself
|
||
err = TransmitMessageBody(pb,false);
|
||
if (err) return(err);
|
||
|
||
// signature? Do not generate headers.
|
||
if (sigToo && pb->hasSig)
|
||
if (err=TransmitMessageSigBody(pb,false)) return(err);
|
||
}
|
||
|
||
// put stuff back
|
||
pb->strip = oldStrip;
|
||
pb->opts = oldOpts;
|
||
pb->flags = oldFlags;
|
||
|
||
return(noErr);
|
||
}
|
||
|
||
/************************************************************************
|
||
* TransmitMessageTextRich - send rich version of message & sig
|
||
************************************************************************/
|
||
OSErr TransmitMessageTextRich(TransmitPBPtr pb,Boolean sigToo,Boolean topLevel)
|
||
{
|
||
OSErr err;
|
||
|
||
// we don't wrap rich text
|
||
pb->flags &= ~FLAG_WRAP_OUT;
|
||
|
||
// mime-version first of all
|
||
if (topLevel) if (err=TransmitMimeVersion(pb)) return(err);
|
||
|
||
// send the non-mime headers
|
||
if (topLevel) if (err=TransmitTopHeaders(pb)) return(err);
|
||
|
||
// send the mime headers
|
||
if (pb->mime) if (err=TransmitMessageBodyHeaders(pb,sigToo,topLevel)) return(err);
|
||
|
||
if (pb->mime)
|
||
{
|
||
// send the header/body separator
|
||
if (err=SendPString(pb->stream,NewLine)) return(err);
|
||
|
||
// send the message itself; close out html if no signature
|
||
err = TransmitMessageBody(pb,!pb->hasSig);
|
||
if (err) return(err);
|
||
|
||
// signature? Do not generate headers & preamble
|
||
if (sigToo && pb->hasSig)
|
||
if (err=TransmitMessageSigBody(pb,false)) return(err);
|
||
}
|
||
return(noErr);
|
||
}
|
||
|
||
/************************************************************************
|
||
* TransmitMimeVersion - transmit the silly mime-version header
|
||
************************************************************************/
|
||
OSErr TransmitMimeVersion(TransmitPBPtr pb)
|
||
{
|
||
OSErr err;
|
||
|
||
if (err = ComposeRTrans(pb->stream,MIME_V_FMT,InterestHeadStrn+hMimeVersion,MIME_VERSION,NewLine))
|
||
return(err);
|
||
|
||
return(noErr);
|
||
}
|
||
|
||
/************************************************************************
|
||
* TransmitTopHeaders - transmit the non-MIME headers at the top of the message
|
||
************************************************************************/
|
||
OSErr TransmitTopHeaders(TransmitPBPtr pb)
|
||
{
|
||
OSErr sErr=noErr;
|
||
Str255 buffer;
|
||
Str255 scratch;
|
||
short header;
|
||
short tid = EffectiveTID(SumOf(pb->messH)->tableId);
|
||
short priority;
|
||
Accumulator newsGroupAcc;
|
||
|
||
if (!pb->others) return(noErr);
|
||
|
||
/*
|
||
* The dreaded X-Sender:
|
||
*/
|
||
if (sErr=SendXSender(pb->stream,pb->messH)) return(sErr);
|
||
|
||
/*
|
||
* extra headers saved with message
|
||
*/
|
||
BufferSendRelease(pb->stream);
|
||
if ((*pb->messH)->extras.data) SendExtras(pb->stream,(*pb->messH)->extras.data,(pb->flags&FLAG_CAN_ENC)!=0,tid);
|
||
BSCLOSE(pb->stream,nil);
|
||
|
||
/*
|
||
* newsgroups
|
||
*/
|
||
newsGroupAcc = (*pb->messH)->newsGroupAcc;
|
||
if (newsGroupAcc.offset)
|
||
{
|
||
BufferSendRelease(pb->stream);
|
||
SendNewsGroups(pb->stream,&newsGroupAcc,tid);
|
||
BSCLOSE(pb->stream,nil);
|
||
}
|
||
|
||
/*
|
||
* Return Receipts
|
||
*/
|
||
if (MessFlagIsSet(pb->messH,FLAG_RR) && !MessOptIsSet(pb->messH,OPT_RECEIPT))
|
||
{
|
||
UseFeature (featureReturnReceiptTo);
|
||
if (PrefIsSet(PREF_RRT)) {
|
||
if (sErr=ComposeRTrans(pb->stream,RRT_FMT,GetReturnAddr(scratch,true),NewLine)) return(sErr);
|
||
}
|
||
if (sErr=ComposeRTrans(pb->stream,MIME_P_FMT,InterestHeadStrn+hMDN,GetReturnAddr(scratch,true),NewLine)) return(sErr);
|
||
}
|
||
|
||
/*
|
||
* Bulk?
|
||
*/
|
||
if (MessOptIsSet(pb->messH,OPT_BULK))
|
||
if (sErr=ComposeRTrans(pb->stream,IMPORTANCE_FMT,TOCHeaderStrn+tchPrecedence,BULK,NewLine))
|
||
return(sErr);
|
||
|
||
/*
|
||
* extra static headers
|
||
*/
|
||
if (GetResource_('STR#',EX_HEADERS_STRN))
|
||
{
|
||
for (header=1;;header++)
|
||
{
|
||
if (*GetRString(buffer,EX_HEADERS_STRN+header))
|
||
{
|
||
if (sErr=SendTrans(pb->stream,buffer+1,*buffer,NewLine+1,*NewLine,nil))
|
||
return(sErr);
|
||
}
|
||
else break;
|
||
}
|
||
}
|
||
|
||
/*
|
||
* Registration headers
|
||
*/
|
||
if (MessOptIsSet(pb->messH,OPT_SEND_REGINFO))
|
||
{
|
||
UserStateType state = GetNagState ();
|
||
|
||
BufferSendRelease(pb->stream);
|
||
|
||
GetRegFirst(state, scratch);
|
||
GetRString(buffer, RegCodeHeadStrn+hRegFirst);
|
||
buffer[++buffer[0]] = ':';
|
||
SendPtrHead(pb->stream, buffer+1, *buffer, scratch+1, *scratch, (pb->flags&FLAG_CAN_ENC)!=0, tid);
|
||
|
||
GetRegLast(state, scratch);
|
||
GetRString(buffer, RegCodeHeadStrn+hRegLast);
|
||
buffer[++buffer[0]] = ':';
|
||
SendPtrHead(pb->stream, buffer+1, *buffer, scratch+1, *scratch, (pb->flags&FLAG_CAN_ENC)!=0, tid);
|
||
|
||
GetRegCode(state, scratch);
|
||
GetRString(buffer, RegCodeHeadStrn+hRegCode);
|
||
buffer[++buffer[0]] = ':';
|
||
SendPtrHead(pb->stream, buffer+1, *buffer, scratch+1, *scratch, (pb->flags&FLAG_CAN_ENC)!=0, tid);
|
||
|
||
BSCLOSE(pb->stream,nil);
|
||
}
|
||
|
||
/*
|
||
* real headers
|
||
*/
|
||
// priority
|
||
priority = SumOf(pb->messH)->priority;
|
||
priority = Prior2Display(priority);
|
||
if (priority!=3)
|
||
{
|
||
if (!PrefIsSet(PREF_SUP_PRIORITY))
|
||
{
|
||
PriorityHeader(buffer,priority);
|
||
if (sErr=SendTrans(pb->stream,buffer+1,*buffer,NewLine+1,*NewLine,nil))
|
||
return(sErr);
|
||
}
|
||
|
||
if (PrefIsSet(PREF_GEN_IMPORTANCE))
|
||
{
|
||
ComposeRString(buffer,IMPORTANCE_FMT,TOCHeaderStrn+tchImportance,ImportanceOutStrn+priority,NewLine);
|
||
if (SendPString(pb->stream,buffer)) return(600);
|
||
}
|
||
}
|
||
|
||
// date
|
||
BuildDateHeader(buffer,SumOf(pb->messH)->seconds);
|
||
if (*buffer && SendTrans(pb->stream,buffer+1,*buffer,NewLine+1,*NewLine,nil))
|
||
return(600);
|
||
|
||
BufferSendRelease(pb->stream);
|
||
|
||
//finally what the user thinks of as the headers...
|
||
for (header=1;header<BODY_HEAD;header++)
|
||
if (header!=ATTACH_HEAD && (
|
||
#ifdef TWO
|
||
PrefIsSet(PREF_POP_SEND) ||
|
||
#endif
|
||
header!=BCC_HEAD))
|
||
if (sErr=SendHeaderLine(pb->stream,pb->messH,header,(pb->flags&FLAG_CAN_ENC)!=0,tid))
|
||
return(sErr);
|
||
BSCLOSE(pb->stream,nil);
|
||
|
||
done:
|
||
return(sErr/100 != 2 ? sErr : 0);
|
||
}
|
||
|
||
/************************************************************************
|
||
* TransmitMultiHeaders - transmit the headers for multipart/something
|
||
************************************************************************/
|
||
OSErr TransmitMultiHeaders(TransmitPBPtr pb,short subType,PStr boundary,short otherParam,PStr otherVal)
|
||
{
|
||
OSErr err;
|
||
Str255 scratch;
|
||
MessHandle messH = pb->messH;
|
||
UHandle headerContent = nil;
|
||
|
||
// The multipart header
|
||
if (err = ComposeRTrans(pb->stream,MIME_MP_FMT,InterestHeadStrn+hContentType,
|
||
MIME_MULTIPART,subType,
|
||
AttributeStrn+aBoundary,boundary,NewLine))
|
||
return(err);
|
||
if (otherParam && (err = ComposeRTrans(pb->stream,MIME_CT_ANNOTATE,otherParam,otherVal,NewLine)))
|
||
return(err);
|
||
|
||
if (!GetRHeaderAnywhere(messH,PLUGIN_INFO,&headerContent))
|
||
{
|
||
MakePStr(scratch,*headerContent+2,GetHandleSize(headerContent)-2); // 2 adjusts for colon-space
|
||
ZapHandle(headerContent);
|
||
if (err = ComposeRTrans(pb->stream,MIME_CT_ANNOTATE,PLUGIN_INFO,scratch,NewLine))
|
||
return err;
|
||
}
|
||
|
||
// Let the translators know
|
||
if (pb->tlMIME)
|
||
{
|
||
AddTLMIME(*pb->tlMIME,TLMIME_TYPE,GetRString(scratch,MIME_MULTIPART),nil);
|
||
AddTLMIME(*pb->tlMIME,TLMIME_SUBTYPE,GetRString(scratch,subType),nil);
|
||
AddTLMIME(*pb->tlMIME,TLMIME_PARAM,GetRString(scratch,AttributeStrn+aBoundary),boundary);
|
||
}
|
||
|
||
return(noErr);
|
||
}
|
||
|
||
/************************************************************************
|
||
* TransmitMessageBodyHeaders - send the header for the body
|
||
************************************************************************/
|
||
OSErr TransmitMessageBodyHeaders(TransmitPBPtr pb,Boolean withSignature,Boolean topLevel)
|
||
{
|
||
MessHandle messH = pb->messH; // keep macros happy
|
||
UHandle text;
|
||
UHandle sig;
|
||
OSErr err;
|
||
|
||
if (pb->strip)
|
||
{
|
||
PETEGetRawText(PETE,TheBody,&text);
|
||
sig = withSignature ? eSignature : nil;
|
||
err = SendContentType(pb->stream,text,BodyOffset(text),sig,0,SumOf(messH)->tableId,&pb->flags,&pb->opts,nil,topLevel?pb->tlMIME:nil,nil);
|
||
}
|
||
else
|
||
{
|
||
sig = pb->html ? HTMLSignature : RichSignature;
|
||
sig = withSignature ? eSignature : nil;
|
||
err = SendContentType(pb->stream,pb->enriched.data,0,sig,0,SumOf(messH)->tableId,&pb->flags,&pb->opts,nil,topLevel?pb->tlMIME:nil,nil);
|
||
}
|
||
pb->encoder = (pb->receipt || 0==(pb->flags&FLAG_ENCBOD)) ? nil : QPEncoder;
|
||
return(noErr);
|
||
}
|
||
|
||
/************************************************************************
|
||
* TransmitMessageBody - send the message's body
|
||
************************************************************************/
|
||
OSErr TransmitMessageBody(TransmitPBPtr pb,Boolean withClosure)
|
||
{
|
||
OSErr sErr = noErr;
|
||
UHandle body;
|
||
|
||
if (pb->strip)
|
||
{
|
||
// send the plain text
|
||
PETEGetRawText(PETE,(*pb->messH)->bodyPTE,&body);
|
||
sErr = SendBodyLines(pb->stream,body,pb->hs.stop,pb->hs.value,pb->flags,True,nil,0,False,pb->encoder);
|
||
}
|
||
else
|
||
{
|
||
if (pb->html)
|
||
{
|
||
// gotta close out the html?
|
||
if (withClosure) if (sErr=HTMLPostamble(&pb->enriched,False)) return(sErr);
|
||
|
||
// send the html
|
||
sErr = SendBodyLines(pb->stream,pb->enriched.data,pb->enriched.offset,0,pb->flags,True,nil,0,False,pb->encoder);
|
||
}
|
||
else if (pb->rich)
|
||
{
|
||
// send the enriched
|
||
sErr = SendEnriched(pb->stream,pb->enriched.data,pb->encoder);
|
||
}
|
||
|
||
// done with that...
|
||
pb->enriched.offset = 0;
|
||
AccuTrim(&pb->enriched);
|
||
}
|
||
|
||
BSCLOSE(pb->stream,pb->encoder);
|
||
if (withClosure) pb->encoder = nil;
|
||
|
||
done:
|
||
return(sErr);
|
||
}
|
||
|
||
/************************************************************************
|
||
* TransmitMessageSig - transmit the sig as its own part, possibly bloated
|
||
************************************************************************/
|
||
OSErr TransmitMessageSig(TransmitPBPtr pb)
|
||
{
|
||
OSErr err;
|
||
|
||
if (pb->bloat && SigStyled && !pb->strip && (pb->rich||pb->html))
|
||
err = TransmitMessageSigBloat(pb);
|
||
else if (SigStyled && !pb->strip)
|
||
err = TransmitMessageSigBody(pb,true);
|
||
else
|
||
err = TransmitMessageSigBodyPlain(pb);
|
||
return(err);
|
||
}
|
||
|
||
/************************************************************************
|
||
* TransmitMessageSigBodyPlain - send the sig, forcing to plain
|
||
************************************************************************/
|
||
OSErr TransmitMessageSigBodyPlain(TransmitPBPtr pb)
|
||
{
|
||
OSErr err;
|
||
// save off some stuff
|
||
Boolean oldStrip = pb->strip; // save the old strip value
|
||
long oldOpts = pb->opts;
|
||
long oldFlags = pb->flags;
|
||
Boolean oldSigStyled = SigStyled;
|
||
|
||
// we be strippin'
|
||
pb->strip = true;
|
||
pb->opts |= OPT_STRIP;
|
||
SigStyled = false;
|
||
|
||
// Send the darn thing
|
||
err = TransmitMessageSigBody(pb,true);
|
||
|
||
// put stuff back
|
||
pb->strip = oldStrip;
|
||
pb->opts = oldOpts;
|
||
pb->flags = oldFlags;
|
||
SigStyled = oldSigStyled;
|
||
return(err);
|
||
}
|
||
|
||
|
||
/************************************************************************
|
||
* TransmitMessageSigBloat - Send the message's signature, possibly with m/a
|
||
************************************************************************/
|
||
OSErr TransmitMessageSigBloat(TransmitPBPtr pb)
|
||
{
|
||
OSErr err;
|
||
Str127 boundary;
|
||
Boolean oldFlat = Flatten!=nil;
|
||
|
||
// we'll need a boundary
|
||
BuildBoundary(pb->messH,boundary,"\pma");
|
||
|
||
// send the part headers, header/body separator, and initial boundary
|
||
if (err=TransmitMultiHeaders(pb,MIME_ALTERNATIVE,boundary,0,nil)) return(err);
|
||
|
||
// now the header/body separator and the initial boundary
|
||
if (err=SendPString(pb->stream,NewLine)) return(err);
|
||
if (err=SendBoundary(pb->stream)) return(err);
|
||
|
||
// send the plain version
|
||
pb->flags |= FLAG_WRAP_OUT; // force wrapping on plain part
|
||
if (!Flatten) Flatten = GetFlatten(); // force flattening
|
||
if (err=TransmitMessageSigBodyPlain(pb)) return(err);
|
||
if (!oldFlat) ZapPtr(Flatten);
|
||
|
||
// now the mid boundary
|
||
if (err=SendBoundary(pb->stream)) return(err);
|
||
|
||
// send the rich version
|
||
if (err=TransmitMessageSigBody(pb,true)) return(err);
|
||
|
||
// and the final boundary
|
||
PCat(boundary,"\p--");
|
||
if (err=SendBoundary(pb->stream)) return(err);
|
||
|
||
return(noErr);
|
||
}
|
||
|
||
/************************************************************************
|
||
* TransmitMessageSigBody - Send the message's signature
|
||
************************************************************************/
|
||
OSErr TransmitMessageSigBody(TransmitPBPtr pb,Boolean withHeaders)
|
||
{
|
||
Handle sigTemp=nil;
|
||
Str63 scratch;
|
||
|
||
// which sig do we want?
|
||
if (!SigStyled&&withHeaders || pb->strip || !(pb->rich||pb->html))
|
||
sigTemp = eSignature;
|
||
else if (pb->html)
|
||
sigTemp = HTMLSignature;
|
||
else if (pb->rich)
|
||
sigTemp = RichSignature;
|
||
|
||
// copy the sig
|
||
if (sErr = MyHandToHand(&sigTemp)) {sigTemp = nil; goto done;}
|
||
|
||
// send headers if we must
|
||
if (withHeaders)
|
||
{
|
||
if (sErr = SendContentType(pb->stream,sigTemp,0,nil,0,SumOf(pb->messH)->tableId,&pb->flags,&pb->opts,nil,nil,nil)) goto done;
|
||
pb->encoder = (pb->receipt || 0==(pb->flags&FLAG_ENCBOD)) ? nil : QPEncoder;
|
||
|
||
// header/body separator
|
||
if (withHeaders) if (sErr=SendPString(pb->stream,NewLine)) goto done;
|
||
|
||
// preamble if html
|
||
if (!pb->strip && pb->html)
|
||
{
|
||
// generate the preamble
|
||
AccuZap(pb->enriched);
|
||
if (sErr = AccuInit(&pb->enriched)) goto done;
|
||
sErr = HTMLPreamble(&pb->enriched,GetRString(scratch,SIGNATURE),0,False);
|
||
|
||
// add the signature to it
|
||
if (!sErr) sErr = AccuAddHandle(&pb->enriched,sigTemp);
|
||
if (sErr) goto done;
|
||
AccuTrim(&pb->enriched);
|
||
|
||
// now put the accumulator handle in sigTemp and zero the accumulator
|
||
ZapHandle(sigTemp);
|
||
sigTemp = pb->enriched.data;
|
||
Zero(pb->enriched);
|
||
}
|
||
}
|
||
|
||
// and finally, send it
|
||
if (sErr=SendBodyLines(pb->stream,sigTemp,GetHandleSize_(sigTemp),0,pb->flags,True,nil,0,False,pb->encoder))
|
||
goto done;
|
||
BSCLOSE(pb->stream,pb->encoder);
|
||
pb->encoder = nil;
|
||
|
||
done:
|
||
ZapHandle(sigTemp);
|
||
return(sErr);
|
||
}
|
||
|
||
/**********************************************************************
|
||
* SendEnriched - send a block of text as text/enriched
|
||
**********************************************************************/
|
||
OSErr SendEnriched(TransStream stream,UHandle text,DecoderFunc *encoder)
|
||
{
|
||
UPtr start, space, spot, end;
|
||
long soft = GetRLong(ENRICHED_SOFT_LINE);
|
||
Boolean wasNl=True;
|
||
long lastLen, lastC;
|
||
short nofill=0;
|
||
Str31 dir;
|
||
|
||
LDRef(text);
|
||
end = *text + GetHandleSize(text);
|
||
|
||
for (start=*text;start<end;start=spot+1)
|
||
{
|
||
for (space=spot=start;spot<end;spot++)
|
||
{
|
||
if (*spot=='\015') break;
|
||
if (*spot==' ') space = spot;
|
||
if (!nofill && space>start && spot-start>soft) break;
|
||
}
|
||
|
||
if (!nofill && spot-start>soft && space>start) spot = space;
|
||
if (!UUPCOut && *start=='.') BS(stream,encoder,".",1);
|
||
BS(stream,encoder,start,spot-start);
|
||
|
||
if (*start=='<' && spot[-1]=='>')
|
||
{
|
||
if (start[1]=='/') MakePStr(dir,start+2,spot-start-3);
|
||
else MakePStr(dir,start+1,spot-start-2);
|
||
|
||
if (EqualStrRes(dir,EnrichedStrn+enNoFill))
|
||
{
|
||
if (start[1]=='/') nofill = MAX(nofill-1,0);
|
||
else nofill++;
|
||
}
|
||
}
|
||
BS(stream,encoder,NewLine+1,*NewLine);
|
||
if (space>start && spot==space && spot<end-1 && spot[1]=='\015')
|
||
spot++; // we already sent a newline, don't want to send another.
|
||
}
|
||
|
||
done:
|
||
UL(text);
|
||
return(sErr);
|
||
}
|
||
|
||
/**********************************************************************
|
||
* SMTPFinish - close out the SMTP session
|
||
**********************************************************************/
|
||
OSErr FinishSMTP(TransStream stream,MessHandle messH)
|
||
{
|
||
Str255 buffer;
|
||
|
||
if (!UUPCOut)
|
||
{
|
||
ComposeString(buffer,"\p.%p",NewLine);
|
||
if (sErr=SendTrans(stream,buffer+1,*buffer,nil)) return(600);
|
||
}
|
||
|
||
if (!UUPCOut)
|
||
{
|
||
sErr = GetReply(stream,buffer,sizeof(buffer),False,False);
|
||
if (sErr > 399) SMTPCmdError(data,"",buffer);
|
||
if (IsAddrErr(sErr))
|
||
{
|
||
if (*buffer) buffer[strlen(buffer)-1] = 0;
|
||
AddOutgoingMesgError((*messH)->sumNum,(*(*messH)->tocH)->sums[(*messH)->sumNum].uidHash, sErr, BAD_XMIT_ERR_TEXT,"",buffer);
|
||
}
|
||
}
|
||
|
||
return(sErr/100 != 2 ? sErr : (sErr=0));
|
||
}
|
||
|
||
/************************************************************************
|
||
* SendHeaderLine - send a line of header information to sendmail
|
||
************************************************************************/
|
||
int SendHeaderLine(TransStream stream,MessHandle messH,short header,Boolean allowQP,short tid)
|
||
{
|
||
Str63 note;
|
||
Str63 label;
|
||
HeadSpec hs;
|
||
|
||
if (!CompHeadFind(messH,header,&hs))
|
||
{
|
||
if (header!=TO_HEAD) return(noErr);
|
||
else
|
||
// something wrong. We didn't find the header we should have. This message is toast
|
||
return fnfErr;
|
||
}
|
||
|
||
if (hs.stop==hs.value)
|
||
{
|
||
if (header==TO_HEAD && *GetRString(note,BCC_ONLY))
|
||
{
|
||
GetRString(label,HEADER_STRN+TO_HEAD);
|
||
return(sErr=SendPtrHead(stream,label+1,*label,note+1,*note,allowQP,tid));
|
||
}
|
||
else return(noErr);
|
||
}
|
||
|
||
/*
|
||
* is it an address header?
|
||
*/
|
||
else if (IsAddressHead(header))
|
||
sErr = SendAddressHead(stream,TheBody,&hs,allowQP,tid);
|
||
else
|
||
sErr = SendNormalHead(stream,TheBody,&hs,allowQP,tid);
|
||
|
||
done:
|
||
return(sErr);
|
||
}
|
||
|
||
/************************************************************************
|
||
* SendAddressHead - send an address header
|
||
************************************************************************/
|
||
OSErr SendAddressHead(TransStream stream,PETEHandle pte,HSPtr hs, Boolean allowQP,short tid)
|
||
{
|
||
UPtr start;
|
||
int lineLimit = GetRLong(WRAP_SPOT)-2;
|
||
UHandle safe=nil;
|
||
Handle fix = nil;
|
||
Boolean high, wasHigh;
|
||
Boolean first = True;
|
||
short lastLen, lastC;
|
||
UHandle addresses=nil;
|
||
UHandle rawAddresses;
|
||
short inGroup = 0;
|
||
UHandle text;
|
||
short charsOnLine = 0;
|
||
OSErr err;
|
||
Boolean popSend = PrefIsSet(PREF_POP_SEND);
|
||
Boolean wasGroup = False;
|
||
Str31 dontHide;
|
||
|
||
PETEGetRawText(PETE,pte,&text);
|
||
GetRString(dontHide,GROUP_DONT_HIDE);
|
||
|
||
/*
|
||
* start by sending the label
|
||
*/
|
||
BS(stream,nil,LDRef(text)+hs->start,hs->value-hs->start-1);
|
||
charsOnLine = hs->value-hs->start;
|
||
|
||
SuckPtrAddresses(&rawAddresses,*text+hs->value,hs->stop-hs->value,True,True,False,nil);
|
||
UL(text);
|
||
if (rawAddresses && **rawAddresses)
|
||
{
|
||
err = ExpandAliases(&addresses,rawAddresses,0,True);
|
||
ZapHandle(rawAddresses);
|
||
if (err) return(err);
|
||
|
||
/*
|
||
* now we have the fully-expanded address list
|
||
*/
|
||
if (addresses)
|
||
{
|
||
for (start=LDRef(addresses); *start; start += *start+2)
|
||
{
|
||
//Folder Carbon Copy - do no support FCC in Light
|
||
if (HasFeature (featureFcc)) {
|
||
if (IsFCCAddr(start)) continue;
|
||
if (IsNewsgroupAddr(start)) continue;
|
||
}
|
||
if (inGroup && start[1]==';') inGroup--;
|
||
if (popSend || !inGroup)
|
||
{
|
||
if (Flatten) TransLit(start+1,*start,Flatten);
|
||
high = allowQP && AnyHighBits(start+1,*start);
|
||
|
||
/*
|
||
* put out a return if we need to start a new line, or a comma if we just
|
||
* need a separator. If it's the very first address, it goes on the
|
||
* first line, period. If it's the trailing semicolon, it goes on this line,
|
||
* period.
|
||
*/
|
||
if (first || *start==1 && start[1]==';') first = False;
|
||
else
|
||
{
|
||
if (!wasGroup && start[*start]!=';') {BS(stream,nil,",",1); charsOnLine++;} /* send the comma separator */
|
||
if ((charsOnLine && *start+charsOnLine>lineLimit) || /* overflow */
|
||
(high || wasHigh)) /* 1522 stuff gets own line always */
|
||
{
|
||
BS(stream,nil,NewLine+1,*NewLine);
|
||
charsOnLine = 0;
|
||
}
|
||
}
|
||
|
||
/*
|
||
* space
|
||
*/
|
||
if (start[*start]!=';') {BS(stream,nil," ",1); charsOnLine++;}
|
||
|
||
/*
|
||
* hey! we can finally send this!
|
||
*/
|
||
wasHigh = high; /* make sure 1522 stuff gets own line next time */
|
||
|
||
/*
|
||
* need to use RFC 1522
|
||
*/
|
||
if (high)
|
||
{
|
||
fix=Encode1342(start+1,*start,lineLimit,&charsOnLine,NewLine,tid);
|
||
if (fix) BS(stream,nil,LDRef(fix),GetHandleSize_(fix));
|
||
}
|
||
|
||
/*
|
||
* RFC 1522 not used
|
||
*/
|
||
if (!fix)
|
||
{
|
||
BS(stream,nil,start+1,*start);
|
||
charsOnLine += *start;
|
||
}
|
||
ZapHandle(fix);
|
||
}
|
||
if (wasGroup = start[*start]==':')
|
||
if (!PFindSub(dontHide,start)) inGroup++;
|
||
}
|
||
|
||
/*
|
||
* finish off header with newline
|
||
*/
|
||
BS(stream,nil,NewLine+1,*NewLine);
|
||
}
|
||
ZapHandle(addresses);
|
||
}
|
||
else
|
||
ZapHandle(rawAddresses);
|
||
|
||
done:
|
||
ZapHandle(fix);
|
||
return(sErr);
|
||
}
|
||
|
||
/************************************************************************
|
||
* SendNormalHead - send a normal header
|
||
************************************************************************/
|
||
OSErr SendNormalHead(TransStream stream,PETEHandle pte,HSPtr hs, Boolean allowQP,short tid)
|
||
{
|
||
UHandle text;
|
||
Str15 kiran;
|
||
|
||
if (hs->index == SUBJ_HEAD && *GetRString(kiran,JUST_FOR_KIRAN))
|
||
return(SendSubjectHead(stream,pte,hs,allowQP,tid));
|
||
|
||
PETEGetRawText(PETE,pte,&text);
|
||
|
||
LDRef(text);
|
||
|
||
sErr = SendPtrHead(stream,*text+hs->start,hs->value-hs->start-1,*text+hs->value,hs->stop-hs->value,allowQP,tid);
|
||
|
||
UL(text);
|
||
|
||
return(sErr);
|
||
}
|
||
|
||
/************************************************************************
|
||
* SendSubjectHead - send the subject header
|
||
************************************************************************/
|
||
OSErr SendSubjectHead(TransStream stream,PETEHandle pte,HSPtr hs, Boolean allowQP,short tid)
|
||
{
|
||
UHandle text;
|
||
Str15 kiran;
|
||
long offset;
|
||
long len;
|
||
long stop;
|
||
long colon;
|
||
long k;
|
||
|
||
GetRString(kiran,JUST_FOR_KIRAN);
|
||
|
||
PETEGetRawText(PETE,pte,&text);
|
||
|
||
LDRef(text);
|
||
len = hs->stop;
|
||
offset = hs->start;
|
||
|
||
do
|
||
{
|
||
// find delimitter
|
||
k = SearchStrPtr(kiran,*text,offset,len,true,false,nil);
|
||
// if not found, pretend at end
|
||
stop = k < 0 ? len : k;
|
||
|
||
if (k>=0) (*text)[k] = '\015'; // replace first delim with newline 'cuz sendptrhead expects it
|
||
|
||
// find colon
|
||
colon = SearchPtrPtr(":",1,*text,offset,stop,true,false,nil);
|
||
if (colon<0)
|
||
sErr = SendPtrHead(stream," ",1,*text+offset,stop-offset,allowQP,tid);
|
||
else
|
||
sErr = SendPtrHead(stream,*text+offset,colon-offset+1,*text+colon+2,stop-colon-2,allowQP,tid);
|
||
// skip delimitter
|
||
offset = stop + *kiran;
|
||
|
||
if (k>=0) (*text)[k] = kiran[1]; // put char back.
|
||
|
||
}
|
||
while (stop<len && !sErr);
|
||
|
||
UL(text);
|
||
|
||
return(sErr);
|
||
}
|
||
|
||
/************************************************************************
|
||
* SendPtrHead - send a normal header with label and text
|
||
************************************************************************/
|
||
OSErr SendPtrHead(TransStream stream,Ptr label, long labelLen, Ptr body, long bodyLen, Boolean allowQP,short tid)
|
||
{
|
||
UPtr start, stop, end, space, limit;
|
||
int lineLimit = GetRLong(WRAP_SPOT)-2;
|
||
UHandle safe=nil;
|
||
Handle fix = nil;
|
||
Boolean first = True;
|
||
short lastLen, lastC;
|
||
short charsOnLine;
|
||
|
||
/*
|
||
* start by sending the label
|
||
*/
|
||
BS(stream,nil,label,labelLen);
|
||
charsOnLine = labelLen;
|
||
|
||
/*
|
||
* send it, a line at a time
|
||
* prepend a ' ' to each line, ala RFC 822
|
||
*/
|
||
start = body;
|
||
stop = start + bodyLen;
|
||
|
||
// trim trailing spaces
|
||
if (stop>start && IsWhite(stop[-1]))
|
||
{
|
||
while (stop>start && IsWhite(stop[-1])) --stop;
|
||
bodyLen = stop-start;
|
||
start[bodyLen] = '\015'; // pretend we have newline there
|
||
}
|
||
|
||
if (Flatten) TransLit(start,bodyLen,Flatten); /* yes, tromps on in-memory copy;
|
||
doesn't matter, will get pitched at close anyway */
|
||
if (allowQP && AnyHighBits(start,stop-start))
|
||
{
|
||
if (fix=Encode1342(start,stop-start,lineLimit,nil,NewLine,tid))
|
||
{
|
||
start = LDRef(fix);
|
||
BS(stream,nil," ",1); BS(stream,nil,start,GetHandleSize_(fix));
|
||
start = stop;
|
||
}
|
||
}
|
||
for (;start<stop;start=end+1)
|
||
{
|
||
Boolean shortLine;
|
||
restart:
|
||
limit = start + lineLimit - charsOnLine;
|
||
if ((shortLine=stop<limit)) limit = stop;
|
||
for (space=end=start;end<limit && *end!='\015'; end++)
|
||
if (IsSpace(*end)) space=end;
|
||
if (!shortLine && space==start && charsOnLine && *end!='\015')
|
||
{
|
||
BS(stream,nil,NewLine+1,*NewLine);
|
||
charsOnLine = 0; /* charsOnLine used only for header label */
|
||
goto restart;
|
||
}
|
||
charsOnLine = 0; /* charsOnLine used only for header label */
|
||
if (space>start && end >= limit && limit<stop) end = space;
|
||
ByteProgress(nil,-1,0);
|
||
BS(stream,nil," ",1); BS(stream,nil,start,end-start);
|
||
if (end<stop)
|
||
{
|
||
BS(stream,nil,NewLine+1,*NewLine);
|
||
if (!IsSpace(*end)) end--;
|
||
}
|
||
}
|
||
BS(stream,nil,NewLine+1,*NewLine);
|
||
done:
|
||
ZapHandle(fix);
|
||
return(sErr);
|
||
}
|
||
|
||
/************************************************************************
|
||
* SendExtras - send extra headers
|
||
************************************************************************/
|
||
OSErr SendExtras(TransStream stream, Handle extras,Boolean allowQP,short tid)
|
||
{
|
||
UPtr start, stop, limit;
|
||
UPtr labelStart, labelStop;
|
||
Str31 label;
|
||
Str31 uglyStupidHackForWindowsIMAP;
|
||
|
||
GetRString(uglyStupidHackForWindowsIMAP,PLUGIN_INFO);
|
||
|
||
start = LDRef(extras);
|
||
limit = start + GetHandleSize_(extras);
|
||
|
||
for (;start<limit;start=stop+1)
|
||
{
|
||
labelStart = start;
|
||
if(*start==' ' || *start==9)
|
||
{
|
||
labelStop = labelStart;
|
||
}
|
||
else
|
||
{
|
||
/*
|
||
* find the colon
|
||
*/
|
||
for (stop=start;stop<limit && *stop!=':';stop++);
|
||
if (stop==limit) break;
|
||
labelStop = stop+1;
|
||
if (labelStop-labelStart>2) MakePStr(label,labelStart,labelStop-labelStart-1);
|
||
else *label = 0;
|
||
|
||
/*
|
||
* find the newline
|
||
*/
|
||
start=stop+1;
|
||
if (*start==' ') start++;
|
||
}
|
||
for (stop=start;stop<limit && *stop!='\015';stop++);
|
||
|
||
/*
|
||
* send the header
|
||
*/
|
||
if (!StringSame(label,uglyStupidHackForWindowsIMAP))
|
||
if (sErr = SendPtrHead(stream,labelStart,labelStop-labelStart,start,stop-start,allowQP,tid)) break;
|
||
}
|
||
UL(extras);
|
||
return(sErr);
|
||
}
|
||
|
||
/************************************************************************
|
||
* SendNewsGroups - send the NewsGroups header
|
||
************************************************************************/
|
||
OSErr SendNewsGroups(TransStream stream,AccuPtr newsGroupAcc,short tid)
|
||
{
|
||
Str63 headerName;
|
||
|
||
AccuAddChar(newsGroupAcc,'\015');
|
||
AccuTrim(newsGroupAcc);
|
||
GetRString(headerName,NEWSGROUPS);
|
||
return(sErr=SendPtrHead(stream,headerName+1,*headerName,LDRef(newsGroupAcc->data),newsGroupAcc->offset-1,false,tid));
|
||
}
|
||
|
||
/************************************************************************
|
||
* SendXSender - construct and send the X-Sender header
|
||
************************************************************************/
|
||
short SendXSender(TransStream stream,MessHandle messH)
|
||
{
|
||
Handle popCanon=nil,returnCanon=nil;
|
||
Str255 buffer;
|
||
Str255 from;
|
||
short err = 0;
|
||
|
||
if ((*CurPers)->popSecure)
|
||
{
|
||
CompHeadGetStr(messH,FROM_HEAD,from);
|
||
/* figure out what the return addr means */
|
||
SuckPtrAddresses(&returnCanon,from+1,*from,False,True,False,nil);
|
||
|
||
/* grab the POP account, and figure out what it means */
|
||
GetPOPPref(buffer);
|
||
SuckPtrAddresses(&popCanon,buffer+1,*buffer,False,True,False,nil);
|
||
}
|
||
|
||
/* if different or no password, send Sender field */
|
||
if (!UUPCOut && (!(*CurPers)->popSecure || !popCanon || !returnCanon || !StringSame(LDRef(popCanon),LDRef(returnCanon))))
|
||
{
|
||
if (!(*CurPers)->popSecure) GetRString(buffer,UNVERIFIED);
|
||
else *buffer = 0;
|
||
|
||
err = ComposeRTrans(stream,XSENDER_FMT,UUPCIn ? 0 : GetPOPPref(from),buffer,NewLine);
|
||
}
|
||
|
||
ZapHandle(popCanon);
|
||
ZapHandle(returnCanon);
|
||
return(err);
|
||
}
|
||
|
||
/************************************************************************
|
||
* SendBodyLines - send the actual body of the message
|
||
* Don't look at this; it's a mess.
|
||
* text Handle to the text to send
|
||
* length length of same
|
||
* offset offset at which to begin
|
||
* flags message flags
|
||
* forceLines should I force a newline at the end of the text?
|
||
* lineStarts pointer to array for determining wrap (may be nil)
|
||
* nLines length of same
|
||
* partial should I listen to the partial information?
|
||
************************************************************************/
|
||
int SendBodyLines(TransStream stream,UHandle text,long length,long offset,long flags,Boolean forceLines,short *lineStarts,short nLines,Boolean partial,DecoderFunc *encoder)
|
||
{
|
||
UPtr start; /* the beginning of the text left to be sent */
|
||
UPtr stop; /* the end of the entire text block */
|
||
UPtr end; /* one past the last character of a line of text to be sent
|
||
for complete lines, this will be a return */
|
||
UPtr space; /* the last space before end */
|
||
int lineLimit; /* # of chars at which to wrap */
|
||
int hardLimit; /* limit for hard returns; don't wrap if para < hardLimit */
|
||
static short quoteLevel; /* the # of quote chars at start of line */
|
||
Byte suspendChar; /* the quote character */
|
||
static short partialSize; /* the size of the last line output, if it
|
||
was an incomplete line */
|
||
static Boolean softNewline; /* was the last newline added by us? */
|
||
Str31 scratch;
|
||
short i;
|
||
UPtr nl;
|
||
Boolean doWrap = 0!=(flags&FLAG_WRAP_OUT);
|
||
short lastC, lastLen;
|
||
short maxQuote = GetRLong(MAX_QUOTE);
|
||
short curLen;
|
||
short tab = GetRLong(TAB_DISTANCE);
|
||
Boolean flowed = doWrap && UseFlowOut;
|
||
Boolean withSpace;
|
||
int smtpMaxLen = GetRLong(MAX_SMTP_LINE);
|
||
Str15 spaceNewLine;
|
||
|
||
if (flowed)
|
||
{
|
||
*spaceNewLine = 0;
|
||
PCatC(spaceNewLine,' ');
|
||
PCat(spaceNewLine,NewLine);
|
||
}
|
||
|
||
if (!partial)
|
||
{
|
||
softNewline=False; /* the caller has told us not to */
|
||
partialSize = 0; /* bother with partial processing */
|
||
}
|
||
start = LDRef(text)+offset;
|
||
stop = *text + length;
|
||
|
||
/*
|
||
* gather up important info for wrap calculations
|
||
*/
|
||
if (doWrap)
|
||
{
|
||
suspendChar = (GetRString(scratch,UseFlowOut ? FLOWED_QUOTE:QUOTE_PREFIX))[1];
|
||
withSpace = UseFlowOut ? scratch[0]>1 : false;
|
||
lineLimit = GetRLong(UseFlowOut ? (encoder?ENCODED_FLOWED_WRAP_SPOT:FLOW_WRAP_SPOT):WRAP_SPOT);
|
||
hardLimit = GetRLong(UseFlowOut ? FLOW_WRAP_THRESH:WRAP_THRESH);
|
||
|
||
/*
|
||
* if this is a new line, count the quote level
|
||
*/
|
||
if (!partialSize)
|
||
{
|
||
for (end = start;end<stop && *end==suspendChar && end-start<10;end++);
|
||
quoteLevel = end-start;
|
||
if (quoteLevel > maxQuote) quoteLevel = 0;
|
||
}
|
||
}
|
||
else lineLimit = REAL_BIG;
|
||
|
||
/*
|
||
* main loop; loop through the buffer, sending one line at a time
|
||
*/
|
||
for (; start<stop; start = end+1)
|
||
{
|
||
/* calculate the spot before which we should wrap */
|
||
if (doWrap)
|
||
curLen = partialSize; //limit = start + lineLimit - partialSize;
|
||
|
||
/* if we don't want wrapping, or there is less text than the wrap limit */
|
||
//if (!doWrap) limit = stop; /* no need to wrap */
|
||
|
||
/*
|
||
* look through the buffer, from start to the calculated line limit
|
||
* keep track of the last space we see, since it's a potential wrap point
|
||
* if we find a return, we have a whole line, and can send it
|
||
*/
|
||
if (doWrap)
|
||
{
|
||
if (softNewline) while (*start==' ' && start<stop) start++; /* skip leading spaces after a soft newline */
|
||
for (space=nl=start;nl<stop && *nl!='\015'; nl++)
|
||
{
|
||
if ((curLen<lineLimit||space==start&&curLen<smtpMaxLen) && (*nl==' ' || *nl=='\t') && (nl==stop-1||nl[1]!='>')) space=nl;
|
||
if (*nl=='\t') curLen = tab*((curLen+tab)/tab);
|
||
else curLen++;
|
||
}
|
||
|
||
// adjust for lines ending in whitespace
|
||
if (nl<stop)
|
||
{
|
||
// special case for "-- "
|
||
if (nl-start==3 && start[0]==start[1] && start[0]=='-' && start[2]==' ')
|
||
; // leave it alone
|
||
else
|
||
for (end=nl-1;end>start;end--)
|
||
{
|
||
if (*end==' ') curLen--;
|
||
else if (*end=='\t') curLen = tab*((curLen-tab)/tab);
|
||
else break;
|
||
}
|
||
}
|
||
end = nl;
|
||
|
||
if (curLen>lineLimit) /* we went over the wrap limit */
|
||
{
|
||
if (space>start) /* and we found a space */
|
||
end = space; /* Wrap it! */
|
||
else if (curLen>smtpMaxLen)
|
||
end = start+smtpMaxLen;
|
||
}
|
||
}
|
||
else
|
||
{
|
||
for (nl=start;nl<stop && *nl!='\015'; nl++); /* just look for newlines */
|
||
end = nl;
|
||
}
|
||
|
||
/*
|
||
* make special allowance for lines >wrap limit but < 80
|
||
*/
|
||
if (!softNewline && end<stop && curLen<hardLimit) end = nl;
|
||
|
||
/* are we adding the newline? We'll want to know for the next line. */
|
||
if (end<stop) softNewline = *end!='\015';
|
||
|
||
/*
|
||
* at this point, start points at the beginning of the line to send,
|
||
* and end points one character past the end of the line to send
|
||
*/
|
||
|
||
// Protect a few things that are liable to transport damage
|
||
if (!encoder && !partialSize && end>start && ((void*)SendTrans)!=((void*)WrapSendTrans))
|
||
{
|
||
/* escape initial periods, if need be */
|
||
if (!UUPCOut && *start=='.') BS(stream,encoder,".",1);
|
||
/* if doing f=f, space-stuff "From " */
|
||
else if (flowed && *start=='F' && end-start>5 && *(uLong*)(start+1)=='rom ') BS(stream,encoder," ",1);
|
||
}
|
||
|
||
/*
|
||
* find last non-space character on line,
|
||
* unless it's the end of the buffer, or not being wrapped,
|
||
* in which case we'd best not drop spaces
|
||
*/
|
||
space = end;
|
||
// special case for "-- "
|
||
if (space-start==3 && start[0]==start[1] && start[0]=='-' && start[2]==' ')
|
||
; // leave it alone
|
||
else if (doWrap && end<stop) while(space>start && (space[-1]=='\t'||space[-1]==' ')) space--;
|
||
|
||
/* if there is data to send, send it */
|
||
if (space>start)
|
||
{
|
||
// Ok, we might need to insert an extra space if doing f=f
|
||
if (flowed && !partialSize && quoteLevel && space-start>quoteLevel && start[quoteLevel]==' ' && ((void*)SendTrans)!=((void*)WrapSendTrans))
|
||
{
|
||
BS(stream,encoder,start,quoteLevel);
|
||
BS(stream,encoder," ",1);
|
||
BS(stream,encoder,start+quoteLevel,space-start-quoteLevel);
|
||
}
|
||
else
|
||
{
|
||
/* if doing f=f, space-stuff initial space */
|
||
if (!partialSize && flowed && *start==' ') BS(stream,encoder," ",1);
|
||
/* Now send the line */
|
||
BS(stream,encoder,start,space-start);
|
||
}
|
||
}
|
||
|
||
/*
|
||
* send the newline, unless we've run out of characters and so don't know
|
||
* if this should be a complete line or not
|
||
*/
|
||
if (forceLines || end<stop)
|
||
{
|
||
if (softNewline && flowed) BS(stream,encoder,spaceNewLine+1,*spaceNewLine); // indicate that the newline is soft
|
||
else BS(stream,encoder,NewLine+1,*NewLine);
|
||
}
|
||
|
||
/*
|
||
* We just put out a line, so we know we're starting fresh for
|
||
* the next one, if there is a next one
|
||
*/
|
||
partialSize = 0;
|
||
|
||
/*
|
||
* quoted line processing, if there are any chars left
|
||
*/
|
||
if (end<stop)
|
||
/*
|
||
* if we sent out a complete line, peek at the next line to see how
|
||
* many quote characters it has
|
||
*/
|
||
if (*end=='\015')
|
||
{
|
||
UPtr p;
|
||
for (p=end+1;p<stop && *p==suspendChar;p++);
|
||
quoteLevel = p-end-1;
|
||
if (quoteLevel > maxQuote) quoteLevel = 0;
|
||
}
|
||
else /* if we wrapped it, prequote the next line */
|
||
{
|
||
for (i=0;i<quoteLevel;i++) BS(stream,encoder,&suspendChar,1);
|
||
partialSize = quoteLevel; /* guess we have a partial line after all */
|
||
if (flowed && *end=='>') BS(stream,encoder," ",1); // space-stuff initial >
|
||
if (quoteLevel && withSpace) {BS(stream,encoder," ",1);partialSize++;}
|
||
}
|
||
|
||
/*
|
||
* normally, end points at a newline (for complete lines) or space
|
||
* (for wrapped ones). So, we normally skip the character end points
|
||
* to. However, long solid lines or Rong-wrapped lines might not obey
|
||
* this behavior; adjust end back by one to make up for the increment
|
||
* we'll do in just a few cycles...
|
||
*/
|
||
if (end<stop && *end!=' ' && *end!='\015') end--;
|
||
}
|
||
|
||
/*
|
||
* all done with that buffer. If the last character is a newline,
|
||
* we don't have much to do. Otherwise, we may (forceLines) wish to
|
||
* newline-terminate, else we want to remember how long the line
|
||
* fragment we sent was
|
||
*/
|
||
if (lastC!=NewLine[*NewLine])
|
||
if (forceLines) BS(stream,encoder,NewLine+1,*NewLine);
|
||
else partialSize = lastLen;
|
||
|
||
done:
|
||
UL(text);
|
||
return(sErr);
|
||
}
|
||
|
||
#pragma segment SMTP2
|
||
|
||
/************************************************************************
|
||
* PrimeProgress - get the progress window started.
|
||
************************************************************************/
|
||
void PrimeProgress(MessHandle messH)
|
||
{
|
||
Str255 buff;
|
||
|
||
MyGetWTitle(GetMyWindowWindowPtr((*messH)->win),buff);
|
||
// ByteProgress(buff,0,CountCompBytes(messH));
|
||
Progress(NoChange,NoChange,nil,nil,buff);
|
||
}
|
||
|
||
|
||
/************************************************************************
|
||
* WannaSend - find out of the user wants to send a dirty window
|
||
************************************************************************/
|
||
int WannaSend(MyWindowPtr win)
|
||
{
|
||
Str255 title;
|
||
|
||
MyGetWTitle(GetMyWindowWindowPtr(win),title);
|
||
return(AlertStr(WANNA_SEND_ALRT,Stop,title));
|
||
}
|
||
|
||
/************************************************************************
|
||
* SendAttachments - send the files the user has attached to his message.
|
||
************************************************************************/
|
||
int SendAttachments(TransStream stream,MessHandle messH,long flags,PStr boundary,short tableID,short idBase)
|
||
{
|
||
FSSpec spec;
|
||
short index;
|
||
short err=noErr;
|
||
Boolean plainText = 0==(flags & FLAG_BX_TEXT);
|
||
Boolean canQP = 0!=(flags & FLAG_CAN_ENC);
|
||
Boolean isUU;
|
||
CInfoPBRec hfi;
|
||
Str31 name;
|
||
short aType;
|
||
|
||
aType = AttachOptNumber(flags);
|
||
isUU = aType+1==atmUU;
|
||
hfi.hFileInfo.ioNamePtr = name;
|
||
for (index=1;!err;index++)
|
||
{
|
||
if (err=GetIndAttachment(messH,index,&spec,nil))
|
||
if (err==1) break;
|
||
else
|
||
return(FileSystemError(BINHEX_OPEN,spec.name,err));
|
||
IsAlias(&spec,&spec);
|
||
if (FSpIsItAFolder(&spec))
|
||
err = SendAttachmentFolder(stream,messH,flags,canQP,plainText,tableID,boundary,&spec,0,idBase+index-1,&idBase,&hfi);
|
||
else
|
||
err = SendAnAttachment(stream,messH,flags,canQP,plainText,tableID,boundary,&spec,0,idBase+index-1);
|
||
}
|
||
if (index-1)
|
||
UpdateNumStat(kStatSentAttach,index-1);
|
||
if (err==1) err = noErr;
|
||
return(err);
|
||
}
|
||
|
||
/************************************************************************
|
||
* SendRelatedParts - send the files the user has put in his html
|
||
************************************************************************/
|
||
int SendRelatedParts(TransStream stream,MessHandle messH,long flags,StackHandle stack,PStr boundary)
|
||
{
|
||
FSSpec origSpec,spec;
|
||
short index;
|
||
short err=noErr;
|
||
|
||
for (index=(*stack)->elCount;!err && index--;)
|
||
{
|
||
if (err = StackItem(&origSpec,index,stack)) err = 1;
|
||
else
|
||
{
|
||
IsAlias(&origSpec,&spec);
|
||
ConvertPictPart(&origSpec,&spec);
|
||
if (err=SendAnAttachment(stream,messH,flags,true,true,0,boundary,&spec,1,(*stack)->elCount-index-1)) break;
|
||
}
|
||
}
|
||
if (err==1) err = noErr;
|
||
return(err);
|
||
}
|
||
|
||
/************************************************************************
|
||
* ConvertPictPart - convert any PICT HTML parts to something more universal
|
||
************************************************************************/
|
||
void ConvertPictPart(FSSpecPtr origSpec,FSSpecPtr spec)
|
||
{
|
||
static OSType exportType;
|
||
Str255 scratch;
|
||
Str31 token;
|
||
GraphicsExportComponent exCI = nil;
|
||
OSErr err;
|
||
|
||
if (PrefIsSet(PREF_NO_PICT_CONVERSION) || // User doesn't want conversion
|
||
exportType == -1 || // Exporter not found
|
||
#if TARGET_RT_MAC_CFM
|
||
!HaveQuickTime(0x0400) || !GraphicsExportDoExport || // Make sure we have version 4 or greater and QT library
|
||
#else
|
||
!HaveQuickTime(0x0400) ||
|
||
#endif
|
||
FileTypeOf(spec) != 'PICT')
|
||
return; // Don't convert this one
|
||
|
||
if (!exportType)
|
||
{
|
||
// Find best exporter
|
||
if (GetRString(scratch,EXPORT_PICT_LIST))
|
||
{
|
||
OSType thisType;
|
||
UPtr spot;
|
||
|
||
for(spot=scratch+1;PToken(scratch,token,&spot,",");)
|
||
{
|
||
if (*token==sizeof(thisType))
|
||
{
|
||
BMD(token+1,&thisType,sizeof(thisType));
|
||
if (exCI = OpenDefaultComponent(GraphicsExporterComponentType,thisType))
|
||
{
|
||
// Found one!
|
||
exportType = thisType;
|
||
break;
|
||
}
|
||
}
|
||
}
|
||
}
|
||
|
||
if (!exportType)
|
||
{
|
||
// Exporter not found
|
||
exportType = -1;
|
||
return;
|
||
}
|
||
}
|
||
else
|
||
exCI = OpenDefaultComponent(GraphicsExporterComponentType,exportType);
|
||
|
||
if (exCI)
|
||
{
|
||
// Do conversion
|
||
GraphicsImportComponent imCI;
|
||
|
||
if (!GetGraphicsImporterForFile(spec,&imCI))
|
||
{
|
||
FSSpec tempSpec;
|
||
|
||
tempSpec = *origSpec;
|
||
UniqueSpec(&tempSpec,31);
|
||
GraphicsExportSetOutputFile(exCI,&tempSpec);
|
||
GraphicsExportSetInputGraphicsImporter(exCI,imCI);
|
||
if (exportType == 'PNGf')
|
||
{
|
||
// Don't allow alpha channel. QuickTime PNG exporter messes up alpha channel with
|
||
// PICT vector images rendering them invisible when not viewing with QuickTime
|
||
GraphicsExportSetDepth (exCI,24);
|
||
}
|
||
err = GraphicsExportDoExport(exCI,nil);
|
||
CloseComponent(imCI);
|
||
CloseComponent(exCI);
|
||
if (!err)
|
||
{
|
||
// Replace old PICT file (or alias)
|
||
FSpDelete(origSpec);
|
||
FSpRename(&tempSpec,origSpec->name);
|
||
*spec = *origSpec;
|
||
}
|
||
}
|
||
else
|
||
CloseComponent(exCI);
|
||
}
|
||
}
|
||
|
||
/************************************************************************
|
||
* SendAttachmentFolder - send a folder full of files
|
||
************************************************************************/
|
||
int SendAttachmentFolder(TransStream stream,MessHandle messH,long flags,Boolean canQP,Boolean plainText,short tableID,PStr boundary,FSSpecPtr folderSpec,short multiID,short partID,short *partBase,CInfoPBRec *hfi)
|
||
{
|
||
short err = noErr;
|
||
short index;
|
||
FSSpec spec;
|
||
Str127 ourBoundary;
|
||
long dirId;
|
||
|
||
// start by sending a boundary for the outer multipart
|
||
if (err=SendBoundary(stream)) return err;
|
||
// and a content-id, why not?
|
||
if (err=SendCID(stream,messH,multiID,partID)) return(err);
|
||
|
||
// now, let's compose our boundary
|
||
NumToString(partID,spec.name);
|
||
BuildBoundary(nil,ourBoundary,spec.name);
|
||
/*
|
||
* send the multipart header
|
||
*/
|
||
if (err = ComposeRTrans(stream,MIME_MP_FMT,
|
||
InterestHeadStrn+hContentType,
|
||
MIME_MULTIPART,
|
||
MIME_X_FOLDER,
|
||
AttributeStrn+aBoundary,
|
||
ourBoundary,
|
||
NewLine))
|
||
return(err);
|
||
|
||
/*
|
||
* content-disposition
|
||
*/
|
||
if (!err) err = ComposeRTrans(stream,MIME_CD_FMT,
|
||
InterestHeadStrn+hContentDisposition,
|
||
ATTACHMENT,
|
||
AttributeStrn+aFilename,
|
||
folderSpec->name,
|
||
NewLine);
|
||
|
||
// header/body separator
|
||
if (err=SendPString(stream,NewLine)) return(err);
|
||
|
||
// get our dirID
|
||
spec.vRefNum = folderSpec->vRefNum;
|
||
spec.parID = dirId = SpecDirId(folderSpec);
|
||
if (!spec.parID) return fnfErr;
|
||
|
||
// setup our iterator
|
||
hfi->hFileInfo.ioNamePtr = spec.name;
|
||
hfi->hFileInfo.ioFDirIndex = 0;
|
||
index = 0;
|
||
|
||
// loop through the contents
|
||
while (!DirIterate(spec.vRefNum,spec.parID,hfi))
|
||
{
|
||
if (partBase) ++*partBase;
|
||
partID++;
|
||
if (HFIIsFolder(hfi))
|
||
{
|
||
index = hfi->hFileInfo.ioFDirIndex; // save the index
|
||
err = SendAttachmentFolder(stream,messH,flags,canQP,plainText,tableID,ourBoundary,&spec,multiID,partID,partBase,hfi);
|
||
hfi->hFileInfo.ioNamePtr = spec.name;
|
||
hfi->hFileInfo.ioFDirIndex = index; // restore the index
|
||
spec.parID = dirId;
|
||
}
|
||
else
|
||
err = SendAnAttachment(stream,messH,flags,canQP,plainText,tableID,ourBoundary,&spec,multiID,partID);
|
||
if (err) break;
|
||
}
|
||
|
||
// we need to send our final boundary
|
||
if (!err) err = SendTrans(stream,"--",2,ourBoundary+1,*ourBoundary,"--",2,NewLine+1,*NewLine,nil);
|
||
|
||
return err;
|
||
}
|
||
|
||
/************************************************************************
|
||
* SendAnAttachment - send a single file
|
||
************************************************************************/
|
||
int SendAnAttachment(TransStream stream,MessHandle messH,long flags,Boolean canQP,Boolean plainText,short tableID,PStr boundary,FSSpecPtr spec,short multiID,short partID)
|
||
{
|
||
short err=noErr;
|
||
Boolean isUU;
|
||
CInfoPBRec hfi;
|
||
Str255 s;
|
||
Str31 name;
|
||
short aType;
|
||
AttMap am;
|
||
uLong oldTicks;
|
||
Boolean flat = false;
|
||
FSSpec local;
|
||
Boolean noRFork;
|
||
|
||
aType = AttachOptNumber(flags);
|
||
isUU = aType+1==atmUU;
|
||
hfi.hFileInfo.ioNamePtr = name;
|
||
if (err=FSpGetHFileInfo(spec,&hfi)) return(FileSystemError(BINHEX_OPEN,spec->name,err));
|
||
ComposeRString(s,BINHEX_PROG_FMT,spec->name);
|
||
Progress(NoChange,NoChange,nil,nil,s);
|
||
noRFork = hfi.hFileInfo.ioFlRLgLen==0;
|
||
|
||
oldTicks = TickCount();
|
||
if (err=SendBoundary(stream)) return(err);
|
||
if (err=SendCID(stream,messH,multiID,partID)) return(err);
|
||
if (err=FindAttMap(spec,&am)) return(err);
|
||
|
||
if (am.mm.specialId=='flat')
|
||
{
|
||
local = *spec;
|
||
if (!FlattenAndSpool(&local))
|
||
{
|
||
spec = &local; // send the spooled copy
|
||
flat = true;
|
||
}
|
||
am.mm.specialId = nil; // special processing done
|
||
}
|
||
|
||
#ifdef TWO
|
||
if (plainText && am.mm.specialId && am.mm.specialId != ' ' || am.mm.specialId=='MiME')
|
||
err = SendSpecial(stream,spec,&am);
|
||
else
|
||
#endif
|
||
err = kSpecialSendDidntPanOut;
|
||
if (err != kSpecialSendDidntPanOut) ;
|
||
#ifdef TWO
|
||
else if (plainText && IsMailbox(spec) && hfi.hFileInfo.ioFlLgLen)
|
||
err = SendDigest(stream,spec);
|
||
#endif
|
||
else if (plainText && (am.isPostScript&&!canQP || EqualStrRes(am.mm.mimetype,MIME_TEXT) && am.isBasic))
|
||
err = SendPlain(stream,spec,flags,tableID,&am);
|
||
else if (!isUU && plainText && (am.isBasic || noRFork))
|
||
err = SendDataFork(stream,spec,flags,tableID,&am);
|
||
else
|
||
{
|
||
switch(aType+1)
|
||
{
|
||
case atmDouble: err = SendDouble(stream,spec,flags,tableID,&am); break;
|
||
case atmSingle: err = SendSingle(stream,spec,True,&am); break;
|
||
#ifdef TWO
|
||
case atmUU: err = SendUU(stream,spec,&am); break;
|
||
#endif
|
||
default: err = SendBinHex(stream,spec,&am); break;
|
||
}
|
||
}
|
||
// if (!err) {Progress(100,NoBar,nil,nil,nil);PopProgress(False);}
|
||
{
|
||
long rate = (600*(hfi.hFileInfo.ioFlLgLen+hfi.hFileInfo.ioFlRLgLen))/((TickCount()-oldTicks+1)*1024);
|
||
ComposeLogS(LOG_TPUT,nil,"\p%p: %d %d.%d KBps",spec->name,aType,rate/10,rate%10);
|
||
}
|
||
|
||
if (flat) FSpDelete(spec);
|
||
return(err);
|
||
}
|
||
|
||
/************************************************************************
|
||
* SendCID - send a content-id
|
||
************************************************************************/
|
||
OSErr SendCID(TransStream stream,MessHandle messH,long part,short n)
|
||
{
|
||
Str255 mid, cid;
|
||
OSErr err=noErr;
|
||
|
||
if (*CompGetMID(messH,mid))
|
||
{
|
||
// compose
|
||
BuildContentID(cid,mid,part,n);
|
||
// send
|
||
err = ComposeRTrans(stream,CID_SEND_FMT,InterestHeadStrn+hContentId,cid,NewLine);
|
||
}
|
||
return(err);
|
||
}
|
||
|
||
/************************************************************************
|
||
* BuildContentID - build a content-id, without <>'s or header or newline
|
||
************************************************************************/
|
||
PStr BuildContentID(PStr into,PStr mid,long part,short i)
|
||
{
|
||
return(ComposeRString(into,CID_ONLY_FMT,mid,part,i));
|
||
}
|
||
|
||
#ifdef TWO
|
||
|
||
/**********************************************************************
|
||
* SendDigest - send a mailbox, as a digest
|
||
**********************************************************************/
|
||
OSErr SendDigest(TransStream stream, FSSpecPtr spec)
|
||
{
|
||
TOCHandle tocH = TOCBySpec(spec);
|
||
Str255 boundary;
|
||
Str63 date;
|
||
short i;
|
||
MyWindowPtr win;
|
||
Boolean newWin;
|
||
|
||
if (!tocH) return(1);
|
||
|
||
/*
|
||
* build the boundary
|
||
*/
|
||
BuildBoundary(nil,boundary,"\pd");
|
||
|
||
sErr = ComposeRTrans(stream,MIME_MP_FMT,
|
||
InterestHeadStrn+hContentType,
|
||
MIME_MULTIPART,
|
||
MIME_DIGEST,
|
||
AttributeStrn+aBoundary,
|
||
boundary,
|
||
NewLine);
|
||
if (!sErr) sErr = ComposeRTrans(stream,MIME_CD_FMT,
|
||
InterestHeadStrn+hContentDisposition,
|
||
ATTACHMENT,
|
||
AttributeStrn+aFilename,spec->name,
|
||
NewLine);
|
||
if (!sErr && *R822Date(date,AFSpGetMod(spec)-ZoneSecs()))
|
||
sErr = ComposeRTrans(stream,MIME_CT_ANNOTATE, AttributeStrn+aModDate,date,NewLine);
|
||
if (!sErr) sErr=SendPString(stream,NewLine);
|
||
|
||
if (!sErr) for (i=0;i<(*tocH)->count;i++)
|
||
{
|
||
UHandle tSig, tRSig, tHSig;
|
||
|
||
tSig = eSignature;
|
||
tRSig = RichSignature;
|
||
tHSig = HTMLSignature;
|
||
|
||
eSignature = nil;
|
||
RichSignature = nil;
|
||
HTMLSignature = nil;
|
||
|
||
/*
|
||
* send a boundary
|
||
*/
|
||
if (sErr=SendBoundary(stream)) break;
|
||
|
||
win = GetAMessageLo(tocH, i, nil, nil, false, &newWin);
|
||
if(win)
|
||
{
|
||
WindowPtr winWP = GetMyWindowWindowPtr(win);
|
||
|
||
sErr = TransmitMessageForSpool(stream, Win2MessH(win));
|
||
if(newWin)
|
||
CloseMyWindow(winWP);
|
||
else
|
||
NotUsingWindow(winWP);
|
||
}
|
||
else sErr = mFulErr;
|
||
|
||
BSCLOSE(stream,nil); /* unfortunate, but gotta do it because of how SendBoundary works */
|
||
|
||
eSignature = tSig;
|
||
RichSignature = tRSig;
|
||
HTMLSignature = tHSig;
|
||
|
||
}
|
||
|
||
done:
|
||
FlushTOCs(True,False);
|
||
|
||
/*
|
||
* and the terminal boundary
|
||
*/
|
||
if (!sErr)
|
||
{
|
||
PCat(boundary,"\p--");
|
||
sErr = SendBoundary(stream);
|
||
}
|
||
return(sErr);
|
||
}
|
||
|
||
/************************************************************************
|
||
* SendSpecial - send a special attachment type
|
||
************************************************************************/
|
||
OSErr SendSpecial(TransStream stream,FSSpecPtr spec,AttMapPtr amp)
|
||
{
|
||
OSErr err;
|
||
|
||
switch (amp->mm.specialId)
|
||
{
|
||
case 'AURL':
|
||
err = SendAnonFTP(stream,spec);
|
||
break;
|
||
case 'MiME':
|
||
err = SendRawMIME(stream,spec);
|
||
break;
|
||
default:
|
||
WarnUser(INVALID_MAP,0);
|
||
err = 1;
|
||
break;
|
||
}
|
||
return(err);
|
||
}
|
||
#endif
|
||
|
||
/************************************************************************
|
||
* GetFlatten - Copy the flatten table into a pointer
|
||
************************************************************************/
|
||
UPtr GetFlatten(void)
|
||
{
|
||
Handle flatH;
|
||
UPtr flatten;
|
||
|
||
flatten = NuPtr(256);
|
||
flatH = GetResource_('taBL',ktFlatten);
|
||
if (flatH) BMD(*flatH,flatten,256);
|
||
else ZapPtr(flatten);
|
||
|
||
return(flatten);
|
||
}
|
||
|
||
#ifdef TWO
|
||
/************************************************************************
|
||
* SendAnonFTP - send an 'AURL' doc as an anonymous ftp thingie
|
||
************************************************************************/
|
||
OSErr SendAnonFTP(TransStream stream,FSSpecPtr spec)
|
||
{
|
||
OSErr err;
|
||
Handle text;
|
||
Str127 type, ftp, host, dir, name, token;
|
||
Str255 data;
|
||
UPtr spot;
|
||
short size;
|
||
|
||
if (err=Snarf(spec,&text,254))
|
||
FileSystemError(BINHEX_READ,spec->name,err);
|
||
else
|
||
{
|
||
size = GetHandleSize_(text);
|
||
MakePStr(data,*text,size);
|
||
|
||
/*
|
||
* parse the string
|
||
*/
|
||
spot = data+1;
|
||
if (PToken(data,type,&spot," ") &&
|
||
PToken(data,ftp,&spot,":"))
|
||
{
|
||
while (*spot=='/') spot++;
|
||
if (PToken(data,host,&spot,"/"))
|
||
{
|
||
*name = *dir = 0;
|
||
while (PToken(data,token,&spot,"/"))
|
||
{
|
||
if (dir[*dir] != '/') PCatC(dir,'/');
|
||
PCat(dir,token);
|
||
PCopy(name,token);
|
||
}
|
||
if (*name)
|
||
{
|
||
*dir -= 1+*name;
|
||
|
||
/*
|
||
* hey; that all parsed
|
||
*/
|
||
if (EqualStrRes(type,ANARCHIE_GET) || EqualStrRes(type,ANARCHIE_TXT))
|
||
if (EqualStrRes(ftp,ANARCHIE_FTP) && !PPtrFindSub("\p@",host+1,*host))
|
||
{
|
||
/*
|
||
* and it is even something we can handle. Hurrah!
|
||
*/
|
||
err = ComposeRTrans(stream,MIME_TEXTPLAIN,
|
||
InterestHeadStrn+hContentType,
|
||
MIME_MESSAGE,EXTERNAL_BODY,"",NewLine);
|
||
if (!err)
|
||
err = ComposeRTrans(stream,NQ_ANNOTATE,
|
||
AttributeStrn+aAccessType,
|
||
GetRString(data,ANON_FTP),
|
||
NewLine);
|
||
if (!err)
|
||
err = ComposeRTrans(stream,MIME_CT_ANNOTATE,
|
||
AttributeStrn+aSite,
|
||
host,
|
||
NewLine);
|
||
if (*dir && !err)
|
||
err = ComposeRTrans(stream,MIME_CT_ANNOTATE,
|
||
AttributeStrn+aDirectory,
|
||
dir,
|
||
NewLine);
|
||
if (!err)
|
||
err = ComposeRTrans(stream,MIME_CT_ANNOTATE,
|
||
AttributeStrn+aName,
|
||
name,
|
||
NewLine);
|
||
if (!err)
|
||
err = ComposeRTrans(stream,MIME_CT_ANNOTATE,
|
||
AttributeStrn+aMode,
|
||
GetRString(data,EqualStrRes(type,ANARCHIE_TXT)?ASCII:IMAGE),
|
||
NewLine);
|
||
|
||
if (!err) err = SendPString(stream,NewLine);
|
||
if (!err)
|
||
err = ComposeRTrans(stream,MIME_MP_FMT,
|
||
InterestHeadStrn+hContentType,
|
||
MIME_APPLICATION,MIME_OCTET_STREAM,
|
||
AttributeStrn+aName,
|
||
name,
|
||
NewLine);
|
||
if (!err)
|
||
err = ComposeRTrans(stream,MIME_CD_FMT,
|
||
InterestHeadStrn+hContentDisposition,
|
||
ATTACHMENT,
|
||
AttributeStrn+aFilename,name,
|
||
NewLine);
|
||
return(err);
|
||
}
|
||
}
|
||
}
|
||
}
|
||
return(-1);
|
||
}
|
||
return(err);
|
||
}
|
||
/************************************************************************
|
||
* SendRawMIME - send a raw MIME document
|
||
************************************************************************/
|
||
OSErr SendRawMIME(TransStream stream,FSSpecPtr spec)
|
||
{
|
||
long size = FSpDFSize(spec);
|
||
Handle buffer = nil;
|
||
long bSize;
|
||
long count;
|
||
long sendCount;
|
||
OSErr err = noErr;
|
||
short refN;
|
||
DecoderFunc *encoder = UUPCOut ? nil : PeriodEncoder;
|
||
Boolean needNL = false; // we do not need to add a newline
|
||
|
||
bSize = MIN(size,GetRLong(BUFFER_SIZE));
|
||
//Debugger();
|
||
|
||
if (buffer=NewIOBHandle(size/4,size))
|
||
{
|
||
MoveHHi(buffer);
|
||
HLock(buffer);
|
||
if (!(err = FSpOpenDF(spec,fsRdWrPerm,&refN)))
|
||
{
|
||
// sniff the end of the file for crlf
|
||
GetEOF(refN,&count);
|
||
if (count<2) needNL = true;
|
||
else
|
||
{
|
||
SetFPos(refN,fsFromLEOF,-2);
|
||
count = 2;
|
||
ARead(refN,&count,*buffer);
|
||
needNL = (*buffer)[0]!='\015' || (*buffer)[1]!='\012';
|
||
SetFPos(refN,fsFromStart,0);
|
||
}
|
||
|
||
// send the file
|
||
while (!err && size>0)
|
||
{
|
||
count = MIN(size,bSize);
|
||
if (!(err=ARead(refN,&count,*buffer)))
|
||
{
|
||
if (NewLine[0]==1 && NewLine[1]=='\015')
|
||
sendCount = RemoveChar('\012',*buffer,count);
|
||
else if (NewLine[0]==1 && NewLine[1]=='\012')
|
||
sendCount = RemoveChar('\015',*buffer,count);
|
||
else
|
||
sendCount = count;
|
||
err = BufferSend(stream,encoder,*buffer,sendCount,True);
|
||
}
|
||
else FileSystemError(BINHEX_READ,spec->name,err);
|
||
size -= count;
|
||
}
|
||
// if the file didn't end with a newline, add one
|
||
if (!err && needNL) err = BufferSend(stream,encoder,NewLine+1,*NewLine,True);
|
||
|
||
// send any remainder
|
||
if (!err) err = BufferSend(stream,nil,nil,0,False);
|
||
BufferSendRelease(stream);
|
||
FSClose(refN);
|
||
}
|
||
ZapHandle(buffer);
|
||
}
|
||
else WarnUser(BINHEX_READ, err = MemError());
|
||
return(err);
|
||
}
|
||
#endif
|
||
|
||
/************************************************************************
|
||
* PeriodEncoder - encode periods
|
||
************************************************************************/
|
||
OSErr PeriodEncoder(CallType callType,DecoderPBPtr pb)
|
||
{
|
||
static short nlState;
|
||
|
||
if (pb)
|
||
{
|
||
switch (callType)
|
||
{
|
||
case kDecodeInit:
|
||
nlState = *NewLine;
|
||
break;
|
||
|
||
case kDecodeDone:
|
||
break;
|
||
|
||
case kDecodeDispose:
|
||
break;
|
||
|
||
case kDecodeData:
|
||
if (pb->inlen)
|
||
pb->outlen = StuffPeriods(pb->input,pb->inlen,pb->output,NewLine,&nlState);
|
||
else
|
||
pb->outlen = 0;
|
||
break;
|
||
}
|
||
}
|
||
return(noErr);
|
||
}
|
||
|
||
/************************************************************************
|
||
* StuffPeriods - byte-stuff periods for SMTP
|
||
************************************************************************/
|
||
long StuffPeriods(UPtr in, long inLen, UPtr out, PStr newLine, short *nlStatePtr)
|
||
{
|
||
short nlState = *nlStatePtr;
|
||
UPtr end = in + inLen;
|
||
short newLineLen = *newLine;
|
||
UPtr origOut = out;
|
||
|
||
for (end=in+inLen;in<end;in++)
|
||
{
|
||
if (*in=='.' && nlState==newLineLen) // found period we need to stuff
|
||
{
|
||
*out++ = '.';
|
||
nlState = 0;
|
||
}
|
||
else
|
||
{
|
||
if (nlState==newLineLen) nlState = 0; // start over again
|
||
|
||
if (*in==newLine[nlState+1]) nlState++; // found one of the newline chars
|
||
else nlState = 0; // start over again
|
||
}
|
||
*out++ = *in;
|
||
}
|
||
|
||
*nlStatePtr = nlState;
|
||
return(out-origOut);
|
||
}
|
||
|
||
/************************************************************************
|
||
* IsPostScript - is a file a PostScript file?
|
||
************************************************************************/
|
||
Boolean IsPostScript(FSSpecPtr spec)
|
||
{
|
||
short refN;
|
||
Str31 psMagic;
|
||
Str31 fileMagic;
|
||
long count;
|
||
Boolean result = False;
|
||
|
||
if (!FSpOpenDF(spec,fsRdPerm,&refN))
|
||
{
|
||
GetRString(psMagic,PS_MAGIC);
|
||
count = *psMagic;
|
||
if (!ARead(refN,&count,fileMagic+1))
|
||
{
|
||
*fileMagic = *psMagic;
|
||
result = StringSame(fileMagic,psMagic);
|
||
}
|
||
MyFSClose(refN);
|
||
}
|
||
return(result);
|
||
}
|
||
|
||
/************************************************************************
|
||
* SendPlain - send a plain text file
|
||
************************************************************************/
|
||
short SendPlain(TransStream stream,FSSpec *spec,long flags,short tableId,AttMapPtr amp)
|
||
{
|
||
int err;
|
||
DecoderFunc *encoder = nil;
|
||
UHandle taste = nil;
|
||
Str31 scratch;
|
||
FInfo info;
|
||
|
||
/*
|
||
* send header
|
||
*/
|
||
if (amp->isPostScript)
|
||
{
|
||
if ((flags&FLAG_CAN_ENC))
|
||
{
|
||
encoder = B64Encoder;
|
||
flags |= FLAG_ENCBOD;
|
||
}
|
||
if (err=MIMEFileHeader(stream,amp,POSTSCRIPT,AFSpGetMod(spec))) goto done;
|
||
if (err = ComposeRTrans(stream,MIME_V_FMT,InterestHeadStrn+hContentEncoding,encoder ? MIME_BASE64 : MIME_BINARY,NewLine))
|
||
goto done;
|
||
DontTranslate = True;
|
||
flags &= ~FLAG_WRAP_OUT;
|
||
}
|
||
else
|
||
{
|
||
flags &= ~FLAG_ENCBOD; /* we may not need to encode this; we'll find out later */
|
||
Snarf(spec,&taste,GetRLong(TEXT_QP_TASTE));
|
||
if (err = SendContentType(stream,taste,0,nil,0,tableId,&flags,nil,ATT_MAP_NAME(amp),nil,amp->mm.subtype)) goto done;
|
||
ZapHandle(taste);
|
||
encoder = flags&FLAG_ENCBOD ? QPEncoder : nil;
|
||
if (err = ComposeRTrans(stream,MIME_CD_FMT,
|
||
InterestHeadStrn+hContentDisposition,
|
||
ATTACHMENT,
|
||
AttributeStrn+aFilename,
|
||
ATT_MAP_NAME(amp),
|
||
NewLine))
|
||
goto done;
|
||
if (*R822Date(scratch,AFSpGetMod(spec)-ZoneSecs()) && (err = ComposeRTrans(stream,MIME_CT_ANNOTATE,
|
||
AttributeStrn+aModDate,scratch,NewLine)))
|
||
goto done;
|
||
}
|
||
FSpGetFInfo(spec,&info);
|
||
if (!err && !amp->suppressXMac) err = ComposeRTrans(stream,MIME_CT_ANNOTATE,
|
||
AttributeStrn+aMacType,Long2Hex(scratch,info.fdType),
|
||
NewLine);
|
||
if (!err && !amp->suppressXMac) err = ComposeRTrans(stream,MIME_CT_ANNOTATE,
|
||
AttributeStrn+aMacCreator,Long2Hex(scratch,info.fdCreator),
|
||
NewLine);
|
||
|
||
if (err = SendPString(stream,NewLine)) goto done;
|
||
|
||
err = SendTextFile(stream,spec,flags,encoder);
|
||
|
||
done:
|
||
DontTranslate = False;
|
||
BufferSendRelease(stream);
|
||
return(err);
|
||
}
|
||
|
||
/**********************************************************************
|
||
* SendTextFile - send text from a file
|
||
**********************************************************************/
|
||
OSErr SendTextFile(TransStream stream,FSSpecPtr spec,long flags,DecoderFunc *encoder)
|
||
{
|
||
short refN=0;
|
||
UHandle dataBuffer=nil;
|
||
long dataSize;
|
||
int err;
|
||
long fileSize,sendSize,readSize;
|
||
Boolean partial = False;
|
||
|
||
/*
|
||
* allocate the buffers
|
||
*/
|
||
dataSize = GetRLong(BUFFER_SIZE);
|
||
if (!(dataBuffer=NuHTempOK(dataSize)))
|
||
{WarnUser(MEM_ERR,err=MemError()); goto done;}
|
||
|
||
/*
|
||
* open it
|
||
*/
|
||
if (err = FSpOpenDF(spec,fsRdPerm,&refN))
|
||
{FileSystemError(BINHEX_OPEN,spec->name,err); goto done;}
|
||
if (err = GetEOF(refN,&fileSize))
|
||
{FileSystemError(BINHEX_OPEN,spec->name,err); goto done;}
|
||
|
||
/*
|
||
* send it
|
||
*/
|
||
for (;fileSize;fileSize-=readSize)
|
||
{
|
||
readSize=MIN(dataSize,fileSize);
|
||
sendSize = readSize;
|
||
if (err=ARead(refN,&sendSize,LDRef(dataBuffer)))
|
||
{FileSystemError(BINHEX_READ,spec->name,err); goto done;}
|
||
UL(dataBuffer);
|
||
if (err = SendBodyLines(stream,dataBuffer,sendSize,0,flags,False,nil,0,partial,encoder))
|
||
goto done;
|
||
partial = (*dataBuffer)[sendSize-1] != '\015';
|
||
}
|
||
if (!err) err=BufferSend(stream,encoder,nil,0,True);
|
||
if (!err && partial) SendPString(stream,NewLine);
|
||
|
||
done:
|
||
DontTranslate = False;
|
||
BufferSendRelease(stream);
|
||
if (refN) MyFSClose(refN);
|
||
if (dataBuffer) ZapHandle(dataBuffer);
|
||
return(err);
|
||
}
|
||
|
||
/************************************************************************
|
||
* BuildDateHeader - build an RFC 822 date header
|
||
************************************************************************/
|
||
void BuildDateHeader(UPtr buffer,long seconds)
|
||
{
|
||
Str63 date;
|
||
if (*R822Date(date,seconds))
|
||
ComposeRString(buffer,DATE_HEADER,HeaderStrn+DATE_HEAD,date);
|
||
else *buffer = 0;
|
||
return;
|
||
}
|
||
|
||
/************************************************************************
|
||
* BuildDateHeader - build an RFC 822 date header
|
||
************************************************************************/
|
||
PStr R822Date(PStr date,long seconds)
|
||
{
|
||
DateTimeRec dtr;
|
||
long delta = ZoneSecs();
|
||
Boolean negative;
|
||
|
||
if (delta==-1) {*date=0;return date;}
|
||
if (seconds)
|
||
SecondsToDate(seconds+delta,&dtr);
|
||
else
|
||
GetTime(&dtr);
|
||
if (negative=delta<0) delta *= -1;
|
||
delta /= 60; /* we want minutes */
|
||
return(ComposeRString(date,R822_DATE_FMT,
|
||
WEEKDAY_STRN+dtr.dayOfWeek,
|
||
dtr.day,
|
||
MONTH_STRN+dtr.month,
|
||
dtr.year,
|
||
dtr.hour/10, dtr.hour%10,
|
||
dtr.minute/10, dtr.minute%10,
|
||
dtr.second/10, dtr.second%10,
|
||
negative ? '-' : '+',
|
||
delta/600,(delta%600)/60,(delta%60)/10,delta%10));
|
||
}
|
||
|
||
/************************************************************************
|
||
* SaveB4Send - grab an outgoing message, saving if necessary
|
||
************************************************************************/
|
||
MessHandle SaveB4Send(TOCHandle tocH,short sumNum)
|
||
{
|
||
short which;
|
||
MessHandle messH = (MessHandle)(*tocH)->sums[sumNum].messH;
|
||
|
||
if (messH && (*messH)->win->isDirty)
|
||
{
|
||
which = WannaSend((*messH)->win);
|
||
if (which==CANCEL_ITEM || which==WANNA_SAVE_CANCEL)
|
||
return (nil);
|
||
else if (which == WANNA_SAVE_SAVE && !SaveComp((*messH)->win))
|
||
return(nil);
|
||
else if (which == WANNA_SAVE_DISCARD)
|
||
{
|
||
(*messH)->win->isDirty = False;
|
||
CloseMyWindow(GetMyWindowWindowPtr((*messH)->win));
|
||
messH = nil;
|
||
}
|
||
}
|
||
if (!messH)
|
||
{
|
||
MyWindowPtr winResult;
|
||
|
||
MyThreadBeginCritical(); // Make sure OpenComp doesn't switch threads
|
||
winResult = OpenComp(tocH,sumNum,nil,nil,False,False);
|
||
MyThreadEndCritical();
|
||
if (!winResult) return(nil);
|
||
messH = (MessHandle)(*tocH)->sums[sumNum].messH;
|
||
}
|
||
return(messH);
|
||
}
|
||
|
||
|
||
/************************************************************************
|
||
* BuildBoundary - build a boundary line for a message
|
||
************************************************************************/
|
||
void BuildBoundary(MessHandle messH,PStr boundary,PStr middle)
|
||
{
|
||
#pragma unused(messH)
|
||
ComposeRString(boundary,MIME_BOUND1_FMT,GMTDateTime(),middle,MIME_BOUND2);
|
||
}
|
||
|
||
/************************************************************************
|
||
* SendContentType - deduce and send the appropriate content-type
|
||
* (and CTE) for two blocks of text (body and signature, typically)
|
||
* text1 - one block of text (may be nil)
|
||
* text2 - second block of text (may be nil)
|
||
* tableID - xlate table id
|
||
* flags - message flags
|
||
************************************************************************/
|
||
OSErr SendContentType(TransStream stream,Handle text1, long offset1, Handle text2, long offset2, short tableID,long *flags,long *opts,PStr name,emsMIMEHandle *tlMIME,PStr subtype)
|
||
{
|
||
short etid = EffectiveTID(tableID);
|
||
Str127 scratch;
|
||
Str63 flowed;
|
||
#ifdef ETL
|
||
Str63 s2;
|
||
#endif
|
||
Boolean anyfunny;
|
||
Boolean any2022;
|
||
short err;
|
||
short encId;
|
||
Boolean strip = 0!=(opts && (*opts & OPT_STRIP));
|
||
Boolean rich = !strip && 0!=(flags && (*flags & FLAG_RICH));
|
||
Boolean html = !strip && 0!=(opts && (*opts & OPT_HTML));
|
||
short computedSubType = html?HTMLTagsStrn+htmlTag:(rich?MIME_RICHTEXT:MIME_PLAIN);
|
||
|
||
if (rich || html) *flags &= ~FLAG_WRAP_OUT;
|
||
|
||
anyfunny = !text1 || AnyFunny(text1,offset1) || text2 && AnyFunny(text2,offset2);
|
||
any2022 = !PrefIsSet(PREF_NO_2022) && (text1 && Any2022(text1,offset1) || text2 && Any2022(text2,offset2));
|
||
|
||
/*
|
||
* figure out proper charset
|
||
*/
|
||
if (anyfunny) NameCharset(scratch,etid,tlMIME);
|
||
else if (any2022) NameCharset(scratch,kt2022,tlMIME);
|
||
else NameCharset(scratch,ktMacUS,tlMIME);
|
||
|
||
if (0!=(*flags&FLAG_WRAP_OUT) && UseFlowOut)
|
||
{
|
||
if (tlMIME)
|
||
AddTLMIME(*tlMIME,TLMIME_PARAM,GetRString(flowed,AttributeStrn+aFormat),GetRString(s2,FORMAT_FLOWED));
|
||
ComposeRString(flowed,MIME_CT_ANNOTATE,AttributeStrn+aFormat,GetRString(s2,FORMAT_FLOWED),"");
|
||
PCat(scratch,flowed);
|
||
}
|
||
|
||
/*
|
||
* send the content type
|
||
*/
|
||
if (!name)
|
||
{
|
||
if (subtype)
|
||
err = ComposeRTrans(stream,MIME_TEXTNOTPLAIN,
|
||
InterestHeadStrn+hContentType,
|
||
MIME_TEXT,
|
||
subtype,
|
||
scratch,
|
||
NewLine);
|
||
else
|
||
err = ComposeRTrans(stream,MIME_TEXTPLAIN,
|
||
InterestHeadStrn+hContentType,
|
||
MIME_TEXT,
|
||
computedSubType,
|
||
scratch,
|
||
NewLine);
|
||
#ifdef ETL
|
||
if (tlMIME)
|
||
{
|
||
AddTLMIME(*tlMIME,TLMIME_TYPE,GetRString(scratch,MIME_TEXT),nil);
|
||
AddTLMIME(*tlMIME,TLMIME_SUBTYPE,subtype?subtype:GetRString(scratch,computedSubType),nil);
|
||
}
|
||
#endif
|
||
}
|
||
else
|
||
{
|
||
PCat(scratch,NewLine);
|
||
if (subtype)
|
||
err = ComposeRTrans(stream,MIME_TEXT_SUBTYPE_FMT,
|
||
InterestHeadStrn+hContentType,
|
||
MIME_TEXT,
|
||
subtype,
|
||
AttributeStrn+aName,
|
||
name,
|
||
scratch);
|
||
else
|
||
err = ComposeRTrans(stream,MIME_MP_FMT,
|
||
InterestHeadStrn+hContentType,
|
||
MIME_TEXT,
|
||
computedSubType,
|
||
AttributeStrn+aName,
|
||
name,
|
||
scratch);
|
||
#ifdef ETL
|
||
if (tlMIME)
|
||
{
|
||
AddTLMIME(*tlMIME,TLMIME_TYPE,GetRString(scratch,MIME_TEXT),nil);
|
||
AddTLMIME(*tlMIME,TLMIME_SUBTYPE,subtype?subtype:GetRString(scratch,computedSubType),nil);
|
||
}
|
||
#endif
|
||
}
|
||
if (err) return(err);
|
||
|
||
/*
|
||
* content-transfer-encoding, if any
|
||
*/
|
||
if (0==(*flags&FLAG_ENCBOD)) /* set manually? */
|
||
*flags = DecideEncoding(text1,text2,anyfunny,etid,*flags); /* determine automatically */
|
||
|
||
if (0!=(*flags&FLAG_ENCBOD))
|
||
{
|
||
if ((*flags&FLAG_CAN_ENC) && (UUPCOut || (Ehlo && !(*Ehlo)->mime8bit) || !PrefIsSet(PREF_ALLOW_8BITMIME)))
|
||
encId = MIME_QP;
|
||
else
|
||
{
|
||
encId = MIME_8BIT;
|
||
*flags &= ~FLAG_ENCBOD;
|
||
}
|
||
if (err = ComposeRTrans(stream,MIME_V_FMT,
|
||
InterestHeadStrn+hContentEncoding,
|
||
encId,
|
||
NewLine))
|
||
return(err);
|
||
#ifdef ETL
|
||
if (tlMIME)
|
||
{
|
||
AddTLMIME(*tlMIME,TLMIME_TYPE,GetRString(scratch,MIME_TEXT),nil);
|
||
AddTLMIME(*tlMIME,TLMIME_PARAM,GetRString(scratch,MIME_CTE),GetRString(s2,encId));
|
||
}
|
||
#endif
|
||
}
|
||
return(noErr);
|
||
}
|
||
|
||
/************************************************************************
|
||
* DecideEncoding - decide which encoding (if any) to use
|
||
************************************************************************/
|
||
long DecideEncoding(Handle text1, Handle text2,Boolean anyfunny,short etid,long flags)
|
||
{
|
||
if (etid==ktMacUS) anyfunny = False;
|
||
|
||
if (anyfunny)
|
||
flags |= FLAG_ENCBOD; /* encode funny chars in QP */
|
||
else if (0==(flags&FLAG_WRAP_OUT) && !(flags&FLAG_RICH))
|
||
{
|
||
if (!text1 || LongerThan(text1,GetRLong(MAX_SMTP_LINE)) ||
|
||
text2 && LongerThan(text2,GetRLong(MAX_SMTP_LINE)))
|
||
flags |= FLAG_ENCBOD;
|
||
}
|
||
else if (0==(flags&FLAG_ENCBOD) && (flags&FLAG_RICH))
|
||
{
|
||
if (!text1 || LongerWordThan(text1,GetRLong(ENRICHED_MAX_WORD)) ||
|
||
text2 && LongerWordThan(text1,GetRLong(ENRICHED_MAX_WORD)))
|
||
flags |= FLAG_ENCBOD;
|
||
}
|
||
return(flags);
|
||
}
|
||
|
||
/************************************************************************
|
||
* SevenBitTable - is the table in question full of only 7-bit chars?
|
||
************************************************************************/
|
||
Boolean SevenBitTable(short tableID)
|
||
{
|
||
Handle table;
|
||
UPtr spot,end;
|
||
|
||
if (!tableID || !(table = GetResource_('taBL',tableID))) return(False);
|
||
|
||
for (spot=*table+127,end=*table+256;spot<end;spot++) if (*spot>126) return(False);
|
||
|
||
return(True);
|
||
}
|
||
|
||
/************************************************************************
|
||
* AnyFunny - does a block of text contain funny chars?
|
||
************************************************************************/
|
||
Boolean AnyFunny(Handle text,long offset)
|
||
{
|
||
UPtr spot,end;
|
||
Str255 line;
|
||
|
||
if (!text || !*text) return(True);
|
||
|
||
// check for high bits
|
||
if (Flatten)
|
||
{
|
||
for (spot=*text+offset,end=spot+GetHandleSize_(text)-offset; spot<end; spot++)
|
||
if (Flatten[*spot]>126) return(True);
|
||
}
|
||
else
|
||
{
|
||
for (spot=*text+offset,end=spot+GetHandleSize_(text)-offset; spot<end; spot++)
|
||
if (*spot>126) return(True);
|
||
}
|
||
|
||
// check for uucp envelopes
|
||
for (spot=*text+offset;spot<end;spot++)
|
||
{
|
||
if (*spot=='\015') break;
|
||
}
|
||
if (spot<end)
|
||
{
|
||
MakePStr(line,*text+offset,spot-*text-offset);
|
||
if (*line && IsFromLine(line+1)) return true;
|
||
}
|
||
|
||
// all quiet on the western front
|
||
return(False);
|
||
}
|
||
|
||
/************************************************************************
|
||
* Any2022 - does a block of text contain 2022?
|
||
************************************************************************/
|
||
Boolean Any2022(Handle text,long offset)
|
||
{
|
||
UPtr spot,end;
|
||
|
||
if (!text || !*text) return(True);
|
||
|
||
for (spot=*text+offset,end=spot+GetHandleSize_(text)-offset; spot<end; spot++)
|
||
if (spot[0]==escChar && spot[1]=='$') return(True);
|
||
|
||
return(False);
|
||
}
|
||
|
||
/************************************************************************
|
||
* EffectiveTID - what is the effective table id?
|
||
************************************************************************/
|
||
short EffectiveTID(short tid)
|
||
{
|
||
Str31 pTable;
|
||
|
||
if (!NewTables) return(TransOutTablID());
|
||
if (tid==NO_TABLE) return(0);
|
||
if (tid==DEFAULT_TABLE)
|
||
return(*GetPref(pTable,PREF_OUT_XLATE) ?
|
||
GetPrefLong(PREF_OUT_XLATE) : TransOutTablID());
|
||
else return(tid);
|
||
}
|
||
|
||
/************************************************************************
|
||
* TransOutTablID - return the translit table name to use for high-bit chars
|
||
* when not using the new table support. Pretty hacky, I'm afraid.
|
||
************************************************************************/
|
||
short TransOutTablID(void)
|
||
{
|
||
Str255 name;
|
||
GetPref(name,PREF_SEND_CSET);
|
||
if (EqualStrRes(name,MIME_ISO_LATIN1)) return TRANS_OUT_TABL_8859_1;
|
||
if (EqualStrRes(name,MIME_ISO_LATIN15)) return ktMacISO15;
|
||
if (EqualStrRes(name,MIME_WIN_1252)) return ktMacWindows;
|
||
if (EqualStrRes(name,MIME_MAC)) return ktIdendity;
|
||
return TRANS_OUT_TABL_8859_1; // Oh well, pref is junk...
|
||
}
|
||
|
||
/************************************************************************
|
||
* TransOutTablName - return the translit table name to use for high-bit chars
|
||
* when not using the new table support. Pretty hacky, I'm afraid.
|
||
************************************************************************/
|
||
PStr TransOutTablName(PStr name)
|
||
{
|
||
GetPref(name,PREF_SEND_CSET);
|
||
if (EqualStrRes(name,MIME_ISO_LATIN1)) return name;
|
||
if (EqualStrRes(name,MIME_ISO_LATIN15)) return name;
|
||
if (EqualStrRes(name,MIME_WIN_1252)) return name;
|
||
if (EqualStrRes(name,MIME_MAC)) return name;
|
||
return GetRString(name,MIME_ISO_LATIN1); // Oh well, pref is junk...
|
||
}
|
||
|
||
|
||
/************************************************************************
|
||
* NameCharset - build a charset= parameter
|
||
************************************************************************/
|
||
PStr NameCharset(PStr charset,short tid,emsMIMEHandle *tlMIME)
|
||
{
|
||
Str63 scratch;
|
||
#ifdef ETL
|
||
Str63 header;
|
||
#endif
|
||
|
||
if (*SimpleNameCharset(scratch,tid))
|
||
{
|
||
ComposeRString(charset,MIME_CSET,scratch);
|
||
#ifdef ETL
|
||
if (tlMIME) AddTLMIME(*tlMIME,TLMIME_PARAM,GetRString(header,MIME_CHARSET),scratch);
|
||
#endif
|
||
}
|
||
else
|
||
*charset = 0;
|
||
|
||
return(charset);
|
||
}
|
||
|
||
/**********************************************************************
|
||
* SimpleNameCharset - return just the name of a charset
|
||
**********************************************************************/
|
||
PStr SimpleNameCharset(PStr name,short tid)
|
||
{
|
||
Handle res;
|
||
short id;
|
||
ResType type;
|
||
|
||
if (!tid)
|
||
GetRString(name,MIME_MAC);
|
||
else if (tid==ktMacUS)
|
||
GetRString(name,MIME_USASCII);
|
||
else if (tid==TransOutTablID())
|
||
TransOutTablName(name);
|
||
else if (tid==TRANS_OUT_TABL_8859_1 || tid==ktMacISO || tid==ktISOMac || tid==TRANS_IN_TABL)
|
||
GetRString(name,MIME_ISO_LATIN1);
|
||
else if (tid==kt2022)
|
||
GetRString(name,ISO_2022_JP);
|
||
else
|
||
{
|
||
if (!GetTableCName(tid-1,name))
|
||
{
|
||
res = GetResource_('taBL',tid);
|
||
if (res)
|
||
{
|
||
GetResInfo(res,&id,&type,name);
|
||
}
|
||
else
|
||
*name = 0;
|
||
}
|
||
}
|
||
return(name);
|
||
}
|
||
|
||
/************************************************************************
|
||
* LongerThan - is there a line longer than some number of chars?
|
||
************************************************************************/
|
||
Boolean LongerThan(Handle text, short len)
|
||
{
|
||
UPtr spot,end,nl;
|
||
|
||
spot = *text;
|
||
end = spot + GetHandleSize_(text);
|
||
nl = spot-1;
|
||
|
||
for (;spot<end;spot++)
|
||
{
|
||
if (*spot=='\015')
|
||
{
|
||
if (spot-nl-1>len) return(True);
|
||
nl = spot;
|
||
}
|
||
}
|
||
return(False);
|
||
}
|
||
|
||
/************************************************************************
|
||
* LongerWordThan - is there a "word" longer than some number of chars?
|
||
************************************************************************/
|
||
Boolean LongerWordThan(Handle text, short len)
|
||
{
|
||
UPtr spot,end,nl;
|
||
|
||
spot = *text;
|
||
end = spot + GetHandleSize_(text);
|
||
nl = spot-1;
|
||
|
||
for (;spot<end;spot++)
|
||
{
|
||
if (*spot=='\015' || *spot==' ')
|
||
{
|
||
if (spot-nl-1>len) return(True);
|
||
nl = spot;
|
||
}
|
||
}
|
||
return(False);
|
||
}
|
||
|
||
/************************************************************************
|
||
* Next1342Word - parse a word from a 1342 stream
|
||
************************************************************************/
|
||
void Next1342Word(UPtr *startP,UPtr end,Token1342Ptr current,PStr delim,Boolean *wasQuote,Boolean *encQuote)
|
||
{
|
||
Str63 word;
|
||
short wordLim = 48;
|
||
Enum1342 wordType;
|
||
Byte c;
|
||
UPtr source = *startP;
|
||
Boolean justSpace;
|
||
Boolean newWasQuote;
|
||
UPtr qSpot;
|
||
|
||
/*
|
||
* are we off the end?
|
||
*/
|
||
if (source>=end)
|
||
{
|
||
wordType = k1342End;
|
||
*word = 0;
|
||
}
|
||
else
|
||
{
|
||
c = *source++;
|
||
*word = 1;
|
||
word[1] = c;
|
||
if (*wasQuote || c=='"') /* collect a quote */
|
||
{
|
||
if (!*wasQuote)
|
||
{ /* search to end of quote to see if quote contains any high bits */
|
||
for (qSpot=source;qSpot<end;qSpot++)
|
||
if (qSpot[0]=='"' && qSpot[-1]!='\\') break;
|
||
*encQuote = AnyHighBits(source,qSpot-source);
|
||
}
|
||
newWasQuote = True;
|
||
while (source<end && *word<wordLim)
|
||
{
|
||
PCatC(word,*source);
|
||
if (*source>=0x80) wordLim -= 2; /* allow space for encoding */
|
||
if (*source++ == '"' && word[*word-1]!='\\')
|
||
{
|
||
newWasQuote = False;
|
||
break; /* closing " */
|
||
}
|
||
}
|
||
wordType = *encQuote ? k1342Word : k1342Plain;
|
||
*wasQuote = newWasQuote;
|
||
}
|
||
else if (strchr(delim+1,c)) /* collect a string of delimiters */
|
||
{
|
||
justSpace = c==' ';
|
||
while (source<end && *word<wordLim)
|
||
if (*source!='"' && strchr(delim+1,*source))
|
||
{
|
||
PCatC(word,*source);
|
||
if (*source!=' ') justSpace = False;
|
||
source++;
|
||
}
|
||
else break;
|
||
wordType = justSpace ? k1342LWSP : k1342Plain;
|
||
}
|
||
else /* collect a regular word */
|
||
{
|
||
UPtr whichDelim;
|
||
UPtr lastSP = nil;
|
||
short oldWordLim;
|
||
short oldWordSize;
|
||
|
||
while (source<end && *word<wordLim)
|
||
if (whichDelim=strchr(delim+1,*source))
|
||
{
|
||
if (*whichDelim==' ')
|
||
{
|
||
if (!AnyHighBits(word+1,*word)) break;
|
||
// We've seen some stuff we need to encode, and now we've
|
||
// seen a space. Remember where we saw it. If we overflow
|
||
// our buffer, then we'll truncate to before here so we always
|
||
// have integral words in our encoding. With luck, this will
|
||
// allow us to make less stupid choices about encoding words by
|
||
// sometimes including spaces in encoded words rather than encoding
|
||
// space runs
|
||
lastSP = source;
|
||
oldWordLim = wordLim;
|
||
oldWordSize = *word;
|
||
PCatC(word,*source++);
|
||
}
|
||
else
|
||
break;
|
||
}
|
||
else
|
||
{
|
||
if (*source>=0x80) wordLim -= 2; /* allow space for encoding */
|
||
PCatC(word,*source);
|
||
source++;
|
||
}
|
||
|
||
// did we end with chars left? If not, and if we have a prior space,
|
||
// back up to that prior space
|
||
if (*word >= wordLim && lastSP)
|
||
{
|
||
source = lastSP;
|
||
wordLim = oldWordLim;
|
||
*word = oldWordSize;
|
||
}
|
||
|
||
wordType = AnyHighBits(word+1,*word) ? k1342Word : k1342Plain;
|
||
// trim trailing spaces from encoded words
|
||
if (wordType == k1342Word)
|
||
while (*word && word[*word]==' ')
|
||
{
|
||
--*word;
|
||
--source;
|
||
}
|
||
}
|
||
}
|
||
|
||
PCopy(current->word,word); /* copy word and type into current buffer */
|
||
current->wordType = wordType;
|
||
*startP = source; /* mark new position in string */
|
||
}
|
||
|
||
/************************************************************************
|
||
* Encode1342 - encode a header line ala RFC 1342 (1522, actually)
|
||
************************************************************************/
|
||
Handle Encode1342(UPtr source,long len,short lineLimit,short *charsOnLine,PStr nl,short tid)
|
||
{
|
||
Token1342 tokens[3];
|
||
Token1342Ptr prev, curr, next;
|
||
Str31 dl1342;
|
||
Boolean continueQuote = False;
|
||
Boolean encQuote = False;
|
||
UPtr spot = source;
|
||
short line;
|
||
Handle encoded = NuHandle(0);
|
||
Boolean wrapped;
|
||
Byte c;
|
||
|
||
if (!encoded) goto fail;
|
||
|
||
/*
|
||
* grab delimiter list
|
||
*/
|
||
GetRString(dl1342,RFC1342_DELIMS);
|
||
|
||
/*
|
||
* prime initial line length
|
||
*/
|
||
line = charsOnLine ? *charsOnLine : 0;
|
||
|
||
/*
|
||
* initialize token buffer
|
||
*/
|
||
tokens[0].wordType = k1342End;
|
||
*tokens[0].word = 0;
|
||
|
||
/*
|
||
* read first token
|
||
*/
|
||
Next1342Word(&spot,source+len,tokens+1,dl1342,&continueQuote,&encQuote);
|
||
|
||
/*
|
||
* main loop
|
||
*/
|
||
for (curr = tokens+1;curr->wordType!=k1342End;curr = next)
|
||
{
|
||
next = RingNext(curr,tokens,3);
|
||
prev = RingNext(next,tokens,3);
|
||
Next1342Word(&spot,source+len,next,dl1342,&continueQuote,&encQuote);
|
||
|
||
/*
|
||
* whitespace between two encoded words gets elided. Therefore, if we
|
||
* are looking at whitespace and both the previous and next words are
|
||
* encoded, we must encode the current word.
|
||
*
|
||
* if either the previous or next words are not encoded, then we don't
|
||
* need to do anything special
|
||
*/
|
||
if (curr->wordType==k1342LWSP && prev->wordType==k1342Word && next->wordType==k1342Word)
|
||
curr->wordType = k1342Word;
|
||
|
||
/*
|
||
* if we are encoding, get with it
|
||
*/
|
||
if (curr->wordType == k1342Word) Encode1342String(curr->word,tid);
|
||
|
||
/*
|
||
* worry about line wrapping
|
||
*/
|
||
wrapped = (prev->wordType!=k1342Plain || curr->wordType!=k1342Plain) &&
|
||
(lineLimit && line+*curr->word>=lineLimit);
|
||
if (wrapped)
|
||
{
|
||
if (PtrPlusHand_(nl+1,encoded,*nl)) goto fail;
|
||
if (PtrPlusHand_(" ",encoded,1)) goto fail;
|
||
line = 1;
|
||
}
|
||
|
||
/*
|
||
* if we are outputting an encoded word and the previous thing was an
|
||
* encoded word, or if the previous thing was a regular word that did
|
||
* NOT end with a space or a ')', then we must prepend a space to the
|
||
* encoded word
|
||
*/
|
||
c = prev->word[*prev->word];
|
||
if (!wrapped && curr->wordType==k1342Word &&
|
||
(prev->wordType==k1342Word ||
|
||
prev->wordType==k1342Plain && c!=' ' && c!=')'))
|
||
{
|
||
BMD(curr->word+1,curr->word+2,*curr->word);
|
||
++*curr->word;
|
||
curr->word[1] = ' ';
|
||
}
|
||
|
||
/*
|
||
* if we are outputting an encoded word and the next thing is a
|
||
* plain word, then we must append a space to the encoded word
|
||
*/
|
||
c = next->word[1];
|
||
if (curr->wordType==k1342Word && next->wordType==k1342Plain)
|
||
PCatC(curr->word,' ');
|
||
|
||
/*
|
||
* stick the word on the end
|
||
*/
|
||
if (PtrPlusHand_(curr->word+1,encoded,*curr->word)) goto fail;
|
||
line += *curr->word;
|
||
}
|
||
|
||
if (charsOnLine) *charsOnLine = line;
|
||
return(encoded);
|
||
|
||
fail:
|
||
ZapHandle(encoded);
|
||
return(nil);
|
||
}
|
||
|
||
/************************************************************************
|
||
* Encode1342String - encode a string in 1342-speak
|
||
************************************************************************/
|
||
void Encode1342String(PStr s,short tid)
|
||
{
|
||
Str255 encoded;
|
||
Str63 name;
|
||
UPtr from, to, end;
|
||
Byte c;
|
||
|
||
/*
|
||
* first, we translit to ISO-latin1
|
||
*/
|
||
if (tid) TransLitRes(s+1,*s,tid);
|
||
|
||
/*
|
||
* now, we encode
|
||
*/
|
||
to = encoded+1;
|
||
end = s + *s + 1;
|
||
for (from=s+1;from<end;from++)
|
||
{
|
||
c = *from;
|
||
if ('a'<=c&&c<='z' || 'A'<=c&&c<='Z' || '0'<=c&&c<='9') *to++ = c;
|
||
else if (c==' ')
|
||
*to++='_';
|
||
else
|
||
{
|
||
*to++ = '=';
|
||
Bytes2Hex(&c,1,to);
|
||
to += 2;
|
||
}
|
||
if (to>encoded+sizeof(encoded)-20) return; /* OVERFLOW */
|
||
}
|
||
|
||
*encoded = to-encoded-1;
|
||
ComposeRString(s,RFC1342_FMT,SimpleNameCharset(name,tid),encoded);
|
||
}
|
||
|
||
#pragma segment SMTPUtil
|
||
/************************************************************************
|
||
* SendPString - send a pascal string
|
||
************************************************************************/
|
||
OSErr SendPString(TransStream stream,PStr string)
|
||
{
|
||
return(SendTrans(stream,string+1,*string,nil));
|
||
}
|
||
|
||
/************************************************************************
|
||
* TimeStamp - put a time stamp on a message
|
||
************************************************************************/
|
||
void TimeStamp(TOCHandle tocH,short sumNum,uLong when,long delta)
|
||
{
|
||
PtrTimeStamp(LDRef(tocH)->sums+sumNum,when,delta);
|
||
UL(tocH);
|
||
#ifdef NEVER
|
||
CalcSumLengths(tocH,sumNum);
|
||
#endif
|
||
InvalSum(tocH,sumNum);
|
||
TOCSetDirty(tocH,true);
|
||
}
|
||
|
||
/************************************************************************
|
||
* PtrTimeStamp - timestamp, but into a sum directly
|
||
************************************************************************/
|
||
void PtrTimeStamp(MSumPtr sum,uLong when,long delta)
|
||
{
|
||
sum->seconds = when;
|
||
sum->origZone = delta/60;
|
||
}
|
||
|
||
PStr FormatZone(PStr string,long delta)
|
||
{
|
||
Boolean neg = delta<0;
|
||
|
||
if (neg) delta *= -1;
|
||
delta /= 60; /* minutes*/
|
||
ComposeString(string,"\p %c%d%d%d%d",neg?'-':'+',
|
||
delta/600,(delta%600)/60,(delta%60)/10,delta%10);
|
||
return(string);
|
||
}
|
||
|
||
|
||
|
||
/************************************************************************
|
||
* BufferSend - send a buffer of (possibly encoded) data
|
||
* encoder - function to call for encoding
|
||
* data - data to encode/send (or nil to send remaining data and close)
|
||
* dataLen - length of data to encode/send
|
||
************************************************************************/
|
||
OSErr BufferSend(TransStream stream, DecoderFunc *encoder, UPtr data, long dataLen, Boolean text)
|
||
{
|
||
short err = noErr;
|
||
static long used;
|
||
long consumed;
|
||
UPtr spot,end;
|
||
long bSize;
|
||
long progBytes;
|
||
|
||
if (EncoderGlobalsOldEncoder && EncoderGlobalsOldEncoder!=encoder)
|
||
{
|
||
BufferSendRelease(stream);
|
||
EncoderGlobalsOldEncoder = encoder;
|
||
}
|
||
|
||
/*
|
||
* are we being fed data?
|
||
*/
|
||
if (data)
|
||
{
|
||
/*
|
||
* do we need to initialize?
|
||
*/
|
||
if (!EncoderGlobalsBuffers[0])
|
||
{
|
||
bSize = 3*GetRLong(BUFFER_SIZE);
|
||
while (!(EncoderGlobalsBuffers[0] = NuHTempOK(bSize)) && bSize > 256) bSize /= 2;
|
||
if (!EncoderGlobalsBuffers[0])
|
||
{
|
||
WarnUser(MEM_ERR,err=MemError());
|
||
return(err);
|
||
}
|
||
#ifdef DEBUG
|
||
if (!BUG5)
|
||
#endif
|
||
if (AsyncSendTrans) EncoderGlobalsBuffers[1] = NuHTempOK(bSize);
|
||
EncoderGlobalsBuffer = EncoderGlobalsBuffers[0];
|
||
if (encoder) err = (*encoder)(kDecodeInit,&EncoderGlobalsPb);
|
||
EncoderGlobalsPb.text = text;
|
||
if (err) {BufferSend(stream,encoder,nil,0,0); return(err);}
|
||
used = 0;
|
||
}
|
||
|
||
bSize = GetHandleSize_(EncoderGlobalsBuffer);
|
||
|
||
if (!DontTranslate && Flatten)
|
||
for (spot=data,end=data+dataLen;spot<end; spot++) *spot = Flatten[*spot];
|
||
|
||
if (!DontTranslate && TransOut)
|
||
for (spot=data,end=data+dataLen; spot<end; spot++) *spot = TransOut[*spot];
|
||
|
||
while (dataLen)
|
||
{
|
||
progBytes = consumed = 0;
|
||
|
||
/*
|
||
* encode?
|
||
*/
|
||
if (encoder)
|
||
{
|
||
if ((!used || bSize-used > 4*dataLen))
|
||
{
|
||
EncoderGlobalsPb.output = LDRef(EncoderGlobalsBuffer)+used;
|
||
consumed = EncoderGlobalsPb.inlen = MIN((bSize-used)/4,dataLen);
|
||
EncoderGlobalsPb.input = data;
|
||
err = (*encoder)(kDecodeData,&EncoderGlobalsPb);
|
||
UL(EncoderGlobalsBuffer);
|
||
if (err) return(err);
|
||
used += EncoderGlobalsPb.outlen;
|
||
progBytes = EncoderGlobalsPb.outlen;
|
||
}
|
||
}
|
||
/*
|
||
* no, just copy
|
||
*/
|
||
else
|
||
{
|
||
consumed = MIN(bSize-used,dataLen);
|
||
BMD(data,*EncoderGlobalsBuffer+used,consumed);
|
||
used += consumed;
|
||
}
|
||
|
||
/*
|
||
* send
|
||
*/
|
||
if (consumed < dataLen)
|
||
{
|
||
if (AsyncSendTrans && EncoderGlobalsBuffers[1])
|
||
{
|
||
err = AsyncSendTrans(stream,LDRef(EncoderGlobalsBuffer),used);
|
||
EncoderGlobalsBuffer = EncoderGlobalsBuffer == EncoderGlobalsBuffers[0] ? EncoderGlobalsBuffers[1] : EncoderGlobalsBuffers[0];
|
||
}
|
||
else
|
||
{
|
||
err = SendTrans(stream,LDRef(EncoderGlobalsBuffer),used,nil);
|
||
UL(EncoderGlobalsBuffer);
|
||
}
|
||
if (err) return(err);
|
||
used = 0;
|
||
}
|
||
|
||
dataLen -= consumed;
|
||
data += consumed;
|
||
if (progBytes) ByteProgress(nil,-progBytes,0);
|
||
}
|
||
}
|
||
|
||
/*
|
||
* no data; clear out encoder
|
||
*/
|
||
else
|
||
{
|
||
if (AsyncSendTrans) err = AsyncSendTrans(stream,nil,-1);
|
||
if (!err && used && !dataLen && EncoderGlobalsBuffer) err = SendTrans(stream,LDRef(EncoderGlobalsBuffer),used,nil);
|
||
|
||
if (encoder && EncoderGlobalsPb.refCon)
|
||
{
|
||
if (!err)
|
||
{
|
||
EncoderGlobalsPb.output = LDRef(EncoderGlobalsBuffer);
|
||
err = (*encoder)(kDecodeDone,&EncoderGlobalsPb);
|
||
if (!err && EncoderGlobalsPb.outlen && !dataLen)
|
||
err = SendTrans(stream,*EncoderGlobalsBuffer,EncoderGlobalsPb.outlen,nil);
|
||
}
|
||
(*encoder)(kDecodeDispose,&EncoderGlobalsPb);
|
||
}
|
||
WriteZero(&EncoderGlobalsPb,sizeof(EncoderGlobalsPb));
|
||
used = 0;
|
||
ZapHandle(EncoderGlobalsBuffers[0]);
|
||
ZapHandle(EncoderGlobalsBuffers[1]);
|
||
EncoderGlobalsBuffer = nil;
|
||
}
|
||
|
||
return(err);
|
||
}
|
||
|
||
/************************************************************************
|
||
* GetIndAttachment - get a particular attacment
|
||
************************************************************************/
|
||
OSErr GetIndAttachment(MessHandle messH,short index,FSSpecPtr spec,HSPtr where)
|
||
{
|
||
OSErr err=1;
|
||
Handle text=nil;
|
||
HeadSpec hs;
|
||
|
||
if (CompHeadFind(messH,ATTACH_HEAD,&hs))
|
||
{
|
||
if (!(err = PETEGetRawText(PETE,TheBody,&text)))
|
||
err = GetIndAttachmentLo(text,index,spec,where,&hs);
|
||
}
|
||
return(err);
|
||
}
|
||
|
||
/************************************************************************
|
||
* GetIndAttachmentLo - get a particular attacment
|
||
************************************************************************/
|
||
OSErr GetIndAttachmentLo(Handle text,short index,FSSpecPtr spec,HSPtr where,HeadSpec *hs)
|
||
{
|
||
short colons[4];
|
||
short onColon;
|
||
Str31 name;
|
||
Str31 volName;
|
||
long id;
|
||
int onChar;
|
||
OSErr err;
|
||
|
||
onColon = 0;
|
||
for (onChar=hs->value;onChar<hs->stop;onChar++)
|
||
if ((*text)[onChar] == ':')
|
||
{
|
||
colons[onColon] = onChar;
|
||
if (++onColon==sizeof(colons)/sizeof(short))
|
||
{
|
||
index--;
|
||
onColon = 0;
|
||
if (!index)
|
||
{
|
||
BMD((*text)+colons[0]+1,volName+1,colons[1]-colons[0]);
|
||
*volName = colons[1]-colons[0];
|
||
id = Atoi(LDRef(text)+colons[1]+1);
|
||
BMD((*text)+colons[2]+1,name+1,colons[3]-colons[2]-1);
|
||
if (where)
|
||
{
|
||
where->start = where->value = colons[0];
|
||
where->stop = colons[3]+1;
|
||
}
|
||
*name = colons[3]-colons[2]-1;
|
||
if (err = FSMakeFSSpec(GetMyVR(volName),id,name,spec))
|
||
{
|
||
// This file probably wasn't found. Go ahead and
|
||
// build spec manually. May need name later on.
|
||
spec->vRefNum = GetMyVR(volName);
|
||
spec->parID = id;
|
||
PStrCopy(spec->name,name,sizeof(spec->name));
|
||
}
|
||
hs->value = onChar+1;
|
||
return err;
|
||
}
|
||
}
|
||
}
|
||
hs->value = onChar;
|
||
return(1); /* no more files */
|
||
}
|
||
|
||
/************************************************************************
|
||
* PriorityHeader: Build a priority header
|
||
************************************************************************/
|
||
UPtr PriorityHeader(UPtr buffer,Byte priority)
|
||
{
|
||
return(ComposeRString(buffer,PRIORITY_FMT,HEADER_STRN+PRIORITY_HEAD,priority,PRIOR_STRN+priority));
|
||
}
|
||
|
||
/************************************************************************
|
||
* GetReply - get a reply to an SMTP command
|
||
************************************************************************/
|
||
int GetReplyLo(TransStream stream, UPtr buffer, int size, AccuPtr bufAcc, Boolean verbose,Boolean isEhlo)
|
||
{
|
||
long rSize;
|
||
Str127 scratch;
|
||
char *cp;
|
||
short err;
|
||
Str255 tempBuffer;
|
||
|
||
// if a buffer was not passed in...
|
||
if (!buffer)
|
||
{
|
||
buffer = tempBuffer;
|
||
size = 255;
|
||
}
|
||
|
||
#ifdef TWO
|
||
if (PrefIsSet(PREF_POP_SEND))
|
||
{
|
||
rSize = size;
|
||
err = POPCmdGetReply(stream,-1,"",buffer,&rSize);
|
||
if (err) return(602);
|
||
if (*buffer=='+') BMD("200 ",buffer,4);
|
||
else BMD("550 ",buffer,4);
|
||
cp = buffer;
|
||
}
|
||
else
|
||
#endif
|
||
do
|
||
{
|
||
Boolean partialBuffer;
|
||
|
||
rSize = size;
|
||
if (bufAcc) bufAcc->offset = 0;
|
||
if (CommandPeriod || (sErr=RecvLine(stream,buffer,&rSize))) return(602); /* error receiving */
|
||
if (bufAcc) AccuAddPtr(bufAcc,buffer,rSize);
|
||
partialBuffer = rSize && buffer[rSize-1]!='\r';
|
||
|
||
// if we've been given an accumulator,
|
||
// accumulate the whole response if this one is incomplete
|
||
if (bufAcc && partialBuffer)
|
||
{
|
||
while(buffer[rSize-1]!='\r')
|
||
{
|
||
rSize = size;
|
||
sErr = RecvLine(stream,buffer,&rSize);
|
||
if (CommandPeriod || sErr) return 602;
|
||
AccuAddPtr(bufAcc,buffer,rSize);
|
||
}
|
||
|
||
// pretend that what we got was just that first bufferful
|
||
rSize = min(size,bufAcc->offset);
|
||
BMD(*bufAcc->data,buffer,rSize);
|
||
}
|
||
else if (partialBuffer)
|
||
{
|
||
// Ick - not all of the reply will fit in the buffer, and we
|
||
// weren't given an accumulator to keep it in. Throw stuff away until
|
||
// we find the end of the line
|
||
Str63 dumpBuffer;
|
||
long dumpSize;
|
||
|
||
do
|
||
{
|
||
dumpSize = sizeof(dumpBuffer);
|
||
sErr = RecvLine(stream,dumpBuffer,&dumpSize);
|
||
if (CommandPeriod || sErr) return 602;
|
||
CarefulLog(LOG_PROTO,DISCARD_LOG_FMT,dumpBuffer,dumpSize);
|
||
}
|
||
while (dumpBuffer[dumpSize-1]!='\r');
|
||
}
|
||
|
||
if (verbose)
|
||
{
|
||
*scratch = MIN(127,rSize);
|
||
strncpy(scratch+1,buffer,*scratch);
|
||
ProgressMessage(kpMessage,scratch);
|
||
}
|
||
for (cp=buffer;cp<buffer+rSize && (*cp < ' ' || *cp>'~');cp++);
|
||
if (isEhlo && cp[0]=='2' && cp[1]=='5' && cp[2]=='0')
|
||
EhloLine(buffer,rSize);
|
||
rSize -= cp-buffer;
|
||
}
|
||
while (rSize<3 ||
|
||
!isdigit(cp[0])||!isdigit(cp[1])||!isdigit(cp[2]) ||
|
||
rSize>3 && cp[3]=='-');
|
||
cp[rSize] = 0;
|
||
err = Atoi(cp);
|
||
if (verbose && err>399 && err<600) SMTPCmdError(nil,nil,buffer);
|
||
if (err==505 || err==530) SetPref(PREF_SMTP_GAVE_530,YesStr);
|
||
if (err==452) err = 552; // Goddam stupid SMTP spec gave a 4xx series response
|
||
// to a permanent error code
|
||
return(err);
|
||
}
|
||
|
||
/************************************************************************
|
||
* FlattenAndSpool - flatten and spool a movie
|
||
* Changes the filespec passed to it!
|
||
************************************************************************/
|
||
OSErr FlattenAndSpool(FSSpecPtr spec)
|
||
{
|
||
FSSpec tempSpec;
|
||
OSErr err = NewTempSpec(0,0,spec->name,&tempSpec);
|
||
|
||
if (!err)
|
||
{
|
||
if (err=FlattenQTMovie(spec,&tempSpec)) FSpDelete(&tempSpec);
|
||
else
|
||
{
|
||
*spec = tempSpec;
|
||
FSpKillRFork(spec);
|
||
}
|
||
}
|
||
return(err);
|
||
}
|
||
|
||
/**********************************************************************
|
||
* FlattenQTMovie - put movie in data fork of new file
|
||
**********************************************************************/
|
||
OSErr FlattenQTMovie(FSSpecPtr inSpec,FSSpecPtr outSpec)
|
||
{
|
||
short movieResFile;
|
||
Movie theMovie,tempMovie;
|
||
OSErr err = noErr;
|
||
|
||
if (!HaveQuickTime(0x0100))
|
||
return cantOpenHandler; // Don't have QuickTime
|
||
|
||
if (!QTMoviesInited)
|
||
{
|
||
if (!EnterMovies()) // Need to do this once
|
||
QTMoviesInited = true;
|
||
err = GetMoviesError();
|
||
}
|
||
|
||
if (QTMoviesInited && !(err = OpenMovieFile(inSpec,&movieResFile,fsRdPerm)))
|
||
{
|
||
short movieResID = 0; /* want first movie */
|
||
Boolean wasChanged;
|
||
|
||
err = NewMovieFromFile (&theMovie,movieResFile,&movieResID,nil,0,&wasChanged);
|
||
CloseMovieFile(movieResFile);
|
||
|
||
if (!err)
|
||
{
|
||
tempMovie = FlattenMovieData(theMovie,flattenAddMovieToDataFork,outSpec,FileCreatorOf(inSpec),smSystemScript,createMovieFileDeleteCurFile);
|
||
err = GetMoviesError();
|
||
if (tempMovie) DisposeMovie(tempMovie);
|
||
DisposeMovie(theMovie);
|
||
}
|
||
}
|
||
|
||
return err;
|
||
} |