home *** CD-ROM | disk | FTP | other *** search
- {$A+,B-,D-,E-,F+,I-,L-,N-,O+,R-,S-,V-}
-
- {***********************************************}
- {* OOBPLUS.PAS 1.0 *}
- {* Copyright (c) Steve Sneed 1991 *}
- {* All Rights Reserved *}
- {* *}
- {* Provided to TurboPower Software for their *}
- {* use or distribution with their products *}
- {***********************************************}
-
- unit OOBPlus; {CompuServe B+ protocol objects}
-
- {*** PLEASE SEE OOBPLUS.DOC BEFORE USING THIS UNIT! ***}
-
- {$I APDEFINE.INC}
-
- {The following define is used to specify status updates at more frequent
- intervals than normal. This makes the status display more informative and
- causes the "Time to go" field to just about tick like a clock on downloads,
- but it can have a negative effect on thruput at higher bps rates and can
- cause the CPS field to "jitter" somewhat, especially on uploads.}
-
- {.$DEFINE ShowRates}
-
-
- {$IFNDEF UseOpro}
- {$IFDEF SupportGIF}
- !!! The options selected are incompatible with this unit !!!
- {$ENDIF}
- {$ENDIF}
-
- interface
-
- uses
- DOS,
- OpString,
- OpCrt,
- ApMisc,
- ApTimer,
- ApPort,
- ApInt14,
- ApUart,
- OOCom,
- OOAbsPcl;
-
- const
- UnitVers = '1.2j';
- UnitDate = '02-Nov-92';
-
- const
- CounterOn : Boolean = False;
- CPSCount : LongInt = 0;
-
- var
- CPSTimer : EventTimer;
-
- const
- {consts needed here for status, continued from ApMisc}
- ecResync = 9980;
- ecWaitACK = 9981;
- ecDropout = 9982;
- ecHostCan = 9983;
- ecFileIO = 9984;
- ecTryResume = 9985;
- ecHostResume= 9986;
- ecResumeOK = 9987;
- ecResumeBad = 9988;
- ecOverwrite = 9989;
- ecUnPacket = 9990;
-
- BP_Timeout_Max = 15; {max allowed timeout per-char}
- BP_Error_Max = 10; {max sequential errors}
- BP_Buffer_Max = 1032; {largest data block available}
- BP_Abort_Max = 3; {number of abort requests req'd to trigger Override}
- BP_SendAhead_Max = 2; {max number of packets we can send ahead}
-
- {minimum <ESC><'I'> (and GIF support interrogation) response strings}
- ESCI_Response : String[80] = '#OZ3,OzCIS1,AC,CA,SSxx,GF,PB,DT';
-
- {see the GIF87a or 89a spec for explanation of these codes}
- GIFReplyEGA = '#89a;1;0,320,200,4,0;0,640,200,2,2;0,640,350,4,2';
- GIFReplyCGA = '#89a;1;0,320,200,2,0;0,640,200,1,0';
- GIFReplyHGC = '#89a;1;0,720,350,1,0';
- GIFReplyNONE = '';
- GIFReply : String[60] = GIFReplyEGA;
-
- type
- {used by GetResumeProc for resume request handling}
- ResumeResultType = (xfrResume, xfrOverwrite, xfrRename, xfrAbort);
-
- BufferType = Array[0..BP_Buffer_Max] of Byte; {a buffer of data}
- SABuffType = {windowing buffer:}
- record
- Seq : Integer; {this sequence number}
- Num : Integer; {this packet's data size}
- Buf : BufferType; {this packet's data}
- end;
- SPackets = Array[0..BP_SendAhead_Max] of SABuffType;
-
- QuoteArray = Array[0..7] of Byte; {for quoting params sets}
-
- const
- DQFull : {all chars in ranges $00..$1F and $80..$9F}
- QuoteArray = ($FF, $FF, $FF, $FF, $FF, $FF, $FF, $FF);
- DQDefault : {ETX ENQ DLE XON XOFF NAK}
- QuoteArray = ($14, $00, $D4, $00, $00, $00, $00, $00);
- DQExtend : {same as DQDefault plus XON & XOFF w/ high bit set}
- QuoteArray = ($14, $00, $D4, $00, $00, $00, $50, $00);
- DQClassic : {Classic B set, all chars in range $00..$1F}
- QuoteArray = ($FF, $FF, $FF, $FF, $00, $00, $00, $00);
-
- type
- ParamsRecord = {xfer params record:}
- record
- WinSend, {send window size}
- WinRecv, {recv window size}
- BlkSize, {block size (* 128)}
- ChkType : Byte; {check type, chksum or CRC}
- QuoteSet : QuoteArray; {chars to quote}
- DROpt, {DL Recovery option}
- UROpt, {UL Recovery option}
- FIOpt : Byte; {File Info option}
- end;
-
- {protocol direction options}
- DirectionType = (Upload, Download, Unknown);
-
- BPProtocolPtr = ^BPProtocol;
- GetResumeProc = function(BPP : BPProtocolPtr) : ResumeResultType;
- ChkAbortProc = function : Boolean;
-
- BPProtocol = {abstract BPlus object:}
- object(AbstractProtocol)
- Ch : Integer; {curr char sent/recd}
- Quoted : Boolean; {true if last ch recd was quoted}
- QuoteTable: Array[0..255] of Byte; {our active quoting table}
- Checksum : Word; {may hold CRC}
- Direction : DirectionType; {upload or download}
- DefResume : ResumeResultType; {default resume handling}
- GetResume : GetResumeProc; {determine how to handle resume}
- ChkAbort : ChkAbortProc; {see if user wants an abort}
-
- HisParams : ParamsRecord; {host's parameters}
- OurParams : ParamsRecord; {our parameters}
-
- AbortCount: Integer; {# of abort requests so far}
- ResumeFlag: Boolean; {true if resuming an aborted dl}
- ResumeOK : Boolean; {true if resume was successful}
- Aborting : Boolean; {true if processing abort}
- FatalAbort: Boolean; {true if OverrideAbort}
- ShowStatus: Boolean; {False only for GIF}
- PacketRecd: Boolean; {true if packet recd in SendPacket}
- BPlus : Boolean; {true if in full B+ mode}
- ClassicB : Boolean; {true for "original" B proto}
- UseSQuote : Boolean; {true if using special quote set}
- LastXferOK: Boolean; {true if last xfer completed OK}
- SQuoteSet : QuoteArray; {user's specified quote set}
-
- RSize : Integer; {size of last recd buffer}
- BuffeRSize: Integer; {current allowed recv size}
- RBuffer : BufferType; {receive buffer}
- SBuffer : SPackets; {sending buffers}
- SeqNum : Integer; {current xmit sequence number}
- Next2ACK : Integer; {packet pending ACK}
- Next2Fill : Integer; {packet to load for send}
- SAMax : Integer; {highest current sendahead cnt}
- SAWaiting : Integer; {# of packets outstanding ACKs}
- SAErrors : Integer; {keep track of SendAhead errors}
-
- R_Raw : LongInt; {vars for status display}
- R_Packets : LongInt;
- S_Raw : LongInt;
- S_Packets : LongInt;
- {$IFDEF ShowRates}
- R_Counter : LongInt;
- S_Counter : LongInt;
- {$ENDIF}
-
- constructor Init(AP : AbstractPortPtr);
- destructor Done; virtual;
-
- {...public methods called by terminal handlers}
- procedure bpHandleENQ;
- {-handle an <ENQ> from host}
- procedure bpHandleESCI;
- {-handle <ESC><'I'> (VT52 terminal capabilities inquiry) from host}
- function bpDLESeen : Boolean; virtual;
- {-called when <DLE> seen from host, starts protocol}
-
- {...other publics}
- procedure bpSetResumeProc(RP : GetResumeProc);
- {-set our ResumeProc for this instance}
- procedure bpSetChkAbortProc(CAP : ChkAbortProc);
- {-set our ChkAbortProc for this instance}
- procedure bpSendACK;
- {-send acknowledgement of receipt for good packet}
-
- {...private methods}
- procedure UpdateStatus(W : Word);
- procedure UpdateQuoteTable(QS : QuoteArray);
- procedure QuoteThis(Value : Integer);
- procedure apResetProtocol; virtual;
- procedure apUpdateBlockCheck(CurByte : Byte); virtual;
- function CheckAbort : Boolean;
- procedure SendByte(C : Char);
- procedure SendQuotedByte(I : Integer);
- procedure SendNAK;
- procedure SendENQ;
- function IncSequence(Value : Integer) : Integer;
- function ReadByte : Boolean;
- function ReadQuotedByte : Boolean;
- procedure SendFailure(Reason : String);
- function ReadPacket(LeadInSeen, FromSend : Boolean) : Boolean;
- procedure SendData(BNum : Integer);
- function IncSA(Value : Integer) : Integer;
- function ReSync : Integer;
- function GetACK : Boolean;
- function SAFlush : Boolean;
- function SendPacket(Size : Integer) : Boolean;
- function SendTransport : Boolean;
- procedure ProcessTransportParams(SendXPortInfo : Boolean);
- procedure bpInitVars;
- end;
-
- BPProtoFTPPtr = ^BPProtoFTP;
- BPProtoFTP =
- object(BPProtocol)
- constructor Init(AP : AbstractPortPtr;
- DefaultResume : ResumeResultType);
- destructor Done; virtual;
-
- {...public/virtual methods}
- function bpDLESeen : Boolean; virtual;
- procedure apPrepareReading; virtual;
- procedure apFinishReading; virtual;
- procedure apFinishWriting; virtual;
- procedure apPrepareWriting; virtual;
-
- {...private methods}
- procedure SendFile;
- procedure RecvFile;
- end;
-
- BPProtoGIFPtr = ^BPProtoGIF;
- BPProtoGIF =
- object(BPProtocol)
- constructor Init(AP : AbstractPortPtr);
- destructor Done; virtual;
-
- {...public/virtual methods}
- function bpDLESeen : Boolean; virtual;
- function bpGetGIFDataBlock(var P;
- var PSize : Word;
- var IsLast : Boolean) : Boolean;
- end;
-
- function bpStatusStr(Code : Word) : String;
- {-provides override functionality for ApMisc's StatusStr function}
-
- procedure NoUserStatus(AP : AbstractProtocolPtr; First, Last : Boolean);
- {-empty status display proc, forced to be used during GIF services}
-
- implementation
-
- const
- {byte/int type char consts}
- ETX = 3;
- ENQ = 5;
- DLE = 16;
- NAK = 21;
-
- {default ParamsRecord values}
- DefDR : Byte = 1; {can handle Download Resume}
- DefBS : Byte = 8; {default to 128 * DefBS (1024) byte packets}
- DefWS = 1; {can handle send ahead}
- DefWR = 2; {can receive up to 2 packets ahead}
- DefCM = 1; {can handle CRC blockchecking}
- DefDQ = 1; {can handle special quoting including non-quoted NUL}
- DefUR = 0; {can NOT handle Upload Recovery (not supported by CIS)}
- DefFI = 1; {can handle File Info packet}
- DefXP = 0; {FTP/GIF does not use TransportLayer}
-
-
- function Long2Str(L : LongInt) : String;
- var
- S : String;
- begin
- Str(L,S);
- Long2Str := S;
- end;
-
- function bpStatusStr(Code : Word) : String;
- var
- S : String;
- begin
- case Code mod 10000 of
- ecResync:
- S := 'Resyncing with host';
- ecWaitACK:
- S := 'Received Wait-ACK from host';
- ecDropout:
- S := 'Dropout';
- ecHostCan:
- S := 'Host requested cancel';
- ecFileIO:
- S := 'Disk IO error';
- ecTryResume:
- S := 'Processing Resume: calculating CRC';
- ecHostResume:
- S := 'Host calculating CRC';
- ecResumeOK:
- S := 'Resuming original download';
- ecResumeBad:
- S := 'CRC check failed, rewinding';
- ecOverwrite:
- S := 'Overwriting original file';
- ecUnPacket:
- S := 'Unknown packet type received';
- else
- S := ApMisc.StatusStr(Code);
- end;
- bpStatusStr := S;
- end;
-
- procedure NoUserStatus(AP : AbstractProtocolPtr; First, Last : Boolean);
- begin
- {do-nothing status procedure}
- end;
-
- constructor BPProtocol.Init(AP : AbstractPortPtr);
- begin
- if NOT AbstractProtocol.InitCustom(AP,DefProtocolOptions) then Fail;
- ProtType := UserProt1;
- ProtocolTypeString[ProtType] := 'Classic B';
- CheckType := bcChecksum1;
- SetShowStatusProc(NoUserStatus);
- UseSQuote := False;
- SQuoteSet := DQDefault;
- DefResume := xfrRename; {rename for safety}
- @GetResume := NIL;
- @ChkAbort := NIL;
- Direction := Unknown;
- case (AP^.Pr^.CurBaud div 10) of
- 0..30 :
- DefBS := 1;
- 31..120 :
- DefBS := 4;
- else
- DefBS := 8;
- end;
- end;
-
- destructor BPProtocol.Done;
- begin
- AbstractProtocol.Done;
- end;
-
- procedure BPProtocol.bpSetResumeProc(RP : GetResumeProc);
- {-set our function to get type of resume handling needed}
- begin
- GetResume := RP;
- end;
-
- procedure BPProtocol.bpSetChkAbortProc(CAP : ChkAbortProc);
- {-set our function to see if an abort was requested by the user}
- begin
- ChkAbort := CAP;
- end;
-
- procedure BPProtocol.UpdateStatus(W : Word);
- {-simplified UserStatus call}
- begin
- if W = 0 then
- AsyncStatus := 0
- else
- APort^.GotError(W);
- UserStatus(@Self,False,False);
- end;
-
- procedure BPProtocol.UpdateQuoteTable(QS : QuoteArray);
- {-update our QuoteTable to match the QS quotearray}
- var
- I,J,K : Integer;
- B,C : Byte;
- begin
- K := 0;
- C := $40;
-
- for I := 0 to 7 do begin
- if I = 4 then begin
- K := 128;
- C := $60;
- end;
- B := QS[I];
-
- for J := 0 to 7 do begin
- if (B and $80) <> 0 then
- QuoteTable[K] := C;
- B := B shl 1;
- Inc(C);
- Inc(K);
- end;
- end;
- end;
-
- procedure BPProtocol.QuoteThis(Value : Integer);
- {-quote <value> char, or reset quotetable if Value = -1}
- var
- I,J : Integer;
- begin
- if Value in [$00..$1F,$80..$9F] then begin
- if Value > $1F then begin
- I := 4;
- Value := Value and $1F;
- end
- else I := 0;
-
- I := I + Value div 8;
- J := Value mod 8;
- SQuoteSet[i] := SQuoteSet[i] or ($80 shr J);
- UseSQuote := True;
- end
- else if Value = -1 then begin
- UseSQuote := False;
- SQuoteSet := DQDefault;
- end;
- end;
-
- procedure BPProtocol.apResetProtocol;
- {-init important session-dependant protocol vars}
- begin
- SeqNum := 0;
- SAMax := 1;
- SAErrors := 0;
- BuffeRSize := 512;
- AbortCount := 0;
- FatalAbort := False;
- LastXferOK := False;
- BPlus := False;
- ClassicB := True;
- CheckType := bcChecksum1;
- FillChar(QuoteTable,SizeOf(QuoteTable),0);
- FillChar(OurParams,SizeOf(OurParams),0);
- OurParams.BlkSize := 4;
- OurParams.QuoteSet := DQDefault;
- UpdateQuoteTable(DQDefault);
-
- BytesTransferred := 0;
- ElapsedTics := 0;
- SrcFileLen := 0;
- SrcFileDate := 0;
- PathName := '';
- end;
-
- procedure BPProtocol.bpHandleENQ;
- {-called when the terminal handler receives an <ENQ>}
- begin
- Aborting := False;
- apResetProtocol;
- APort^.PutString(#16'++'#16'0');
- end;
-
- procedure BPProtocol.bpHandleESCI;
- {-called by terminal handler when <ESC><'I'> seen at port}
- var
- S : String;
- T : String[5];
- XRes,YRes,P : Integer;
- begin
- S := ESCI_Response;
- {make sure tailer is in place for later}
- if Pos(',+',S) = 0 then
- S := S + ',+';
- {if 'SSxx' part of string, insert our screen size values}
- P := Pos('SSxx',S);
- if P > 0 then begin
- XRes := (Lo(WindMax) - Lo(WindMin));
- YRes := (Hi(WindMax) - Hi(WindMin));
- S[p+2] := Chr(YRes+31);
- S[p+3] := Chr(XRes+31);
- end;
- {build the string's checksum and append it to the string}
- XRes := 0;
- for P := 1 to Length(S) do
- Inc(XRes,Ord(S[p]));
- Str(XRes,T);
- S := S + T;
- {send the response}
- (*
- for P := 1 to Length(S) do
- {!!} WriteSes(S[p]);
- *)
- APort^.PutString(S+^M);
- end;
-
- procedure BPProtocol.apUpdateBlockCheck(CurByte : Byte);
- {-update the CRC/checksum to reflect the new byte}
-
- function UpdCrc(CurByte : Byte; CurCrc : Word) : Word;
- {-due to an oddity in the CIS handling of CRC's, we use this special
- version of UpdateCrc rather than the one in APMISC. This function
- requires the CRC lookup table in APMISC.}
- begin
- UpdCrc := CrcTable[((CurCrc shr 8) xor CurByte) and $ff] xor
- (CurCrc shl 8);
- end;
-
- begin
- if GetCheckType = bcCrc16 then
- Checksum := UpdCRC(CurByte,Checksum)
- else begin {use classic B's odd checksum method}
- Checksum := Checksum shl 1;
- if Checksum > 255 then
- Checksum := (Checksum and $00FF) + 1;
- Checksum := Checksum + CurByte;
- if Checksum > 255 then
- Checksum := (Checksum and $00FF) + 1;
- end;
- end;
-
- function BPProtocol.CheckAbort : Boolean;
- begin
- if @ChkAbort <> NIL then
- CheckAbort := ChkAbort
- else
- CheckAbort := (KeyPressed) and (ReadKey = #27);
- end;
-
- procedure BPProtocol.SendByte(C : Char);
- begin
- with APort^ do begin
- while NOT TransReady do ;
- PutChar(C);
- end;
- Inc(S_Raw);
- if CounterOn then
- Inc(CPSCount);
-
- {$IFDEF ShowRates}
- Inc(S_Counter);
- S_Counter := S_Counter mod (APort^.Pr^.CurBaud div 10);
- if S_Counter = 0 then
- UpdateStatus(0);
- {$ENDIF}
- end;
-
- procedure BPProtocol.SendQuotedByte(I : Integer);
- begin
- I := I and $FF;
- if QuoteTable[i] <> 0 then begin
- SendByte(cDLE);
- SendByte(Chr(QuoteTable[i]));
- end
- else SendByte(Chr(i));
- end;
-
- procedure BPProtocol.bpSendACK;
- begin
- SendByte(cDLE);
- SendByte(Chr(SeqNum + Ord('0')));
- end;
-
- procedure BPProtocol.SendNAK;
- begin
- SendByte(cNAK);
- end;
-
- procedure BPProtocol.SendENQ;
- begin
- SendByte(cENQ);
- end;
-
- function BPProtocol.ReadByte : Boolean;
- var
- ET : EventTimer;
- C : Char;
- begin
- ReadByte := False;
- {set timeout based on the Aborting state}
- if Aborting then
- NewTimer(ET,Secs2Tics(10))
- else
- NewTimer(ET,Secs2Tics(30));
-
- {wait for char, checking for OverrideAbort request}
- while NOT APort^.CharReady do begin
- if TimerExpired(ET) then
- exit;
- if (Aborting) and (CheckAbort) then begin
- inc(AbortCount);
- if AbortCount >= BP_Abort_Max then begin
- FatalAbort := True;
- exit;
- end;
- end;
- end;
-
- APort^.GetChar(C);
- ReadByte := (AsyncStatus = ecOK);
- Ch := Ord(C);
- inc(R_Raw);
- if CounterOn then
- Inc(CPSCount);
-
- {$IFDEF ShowRates}
- Inc(R_Counter);
- R_Counter := R_Counter mod (APort^.Pr^.CurBaud div 10);
- if R_Counter = 0 then
- UpdateStatus(0);
- {$ENDIF}
- end;
-
- function BPProtocol.ReadQuotedByte : Boolean;
- {receive a byte, unquoting if needed}
- begin
- ReadQuotedByte := False;
- Quoted := False;
-
- if NOT ReadByte then exit;
- if Char(Ch) = cDLE then begin
- if NOT ReadByte then exit;
- if Ch < $60 then
- Ch := Ch and $1F
- else
- Ch := (Ch and $1F) or $80;
- Quoted := True;
- end;
- ReadQuotedByte := True;
- end;
-
- function BPProtocol.IncSequence(Value : Integer) : Integer;
- {-increment a Sequence Number var}
- begin
- IncSequence := (Succ(Value) mod 10);
- end;
-
- function BPProtocol.ReadPacket(LeadInSeen, FromSend : Boolean) : Boolean;
- const
- R_Get_DLE = 0;
- R_Get_B = 1;
- R_Get_Seq = 2;
- R_Get_Data = 3;
- R_Get_Check = 4;
- R_Send_ACK = 5;
- R_Timed_Out = 6;
- R_Error = 7;
- R_Success = 8;
- var
- State : Byte;
- NextSeq, Idx, PacketNum : Integer;
- NewChk : Word;
- OldChk : Word absolute BlockCheck;
- NAKSent : Boolean;
- begin
- ReadPacket := True; {assume success for now}
- if PacketRecd then begin
- PacketRecd := False;
- exit;
- end;
-
- {set initial values}
- NAKsent := False;
- NextSeq := IncSequence(SeqNum);
- BlockErrors := 0;
- if LeadInSeen then
- State := R_Get_Seq
- else
- State := R_Get_DLE;
-
- while True do
- case State of
- R_Get_DLE :
- begin
- if NOT(Aborting) and (CheckAbort) then begin
- UpdateStatus(epNonFatal + ecCancelRequested);
- SendFailure('AAborted by user');
- Aborting := True;
- ReadPacket := False;
- exit;
- end;
- if not ReadByte then
- State := R_Timed_Out
- else if Ch = DLE then {saw DLE}
- State := R_Get_B
- else if Ch = ENQ then {respond to ENQ}
- State := R_Send_ACK;
- end;
-
- R_Get_B :
- begin
- if not ReadByte then
- State := R_Timed_Out
- else if Ch = Ord('B') then {saw B}
- State := R_Get_Seq
- else if Ch = Ord(';') then begin {saw "wait-acknowledge"}
- UpdateStatus(epNonFatal + ecWaitACK);
- State := R_Get_DLE;
- end
- else if State = ENQ then {respond to ENQ}
- State := R_Send_ACK
- else
- State := R_Get_DLE;
- end;
-
- R_Get_Seq :
- begin
- if ResumeFlag then begin {if resuming, reset vars for accuracy}
- NewTimer(Timer,Secs2Tics(SecsPerDay));
- R_Raw := 2;
- end;
- if not ReadByte then
- State := R_Timed_Out
- else if Ch = ENQ then
- State := R_Send_ACK
- else begin
- if (GetCheckType = bcCrc16) then
- Checksum := $FFFF
- else
- Checksum := 0;
- apUpdateBlockCheck(Byte(Ch));
- PacketNum := Ch - ord('0');
- Idx := 0;
- State := R_Get_Data;
- end;
- end;
-
- R_Get_Data :
- begin
- if not ReadQuotedByte then
- State := R_Timed_Out
- else begin
- apUpdateBlockCheck(Byte(Ch));
- if (Ch = ETX) and NOT(Quoted) then
- State := R_Get_Check
- else begin
- RBuffer[Idx] := Byte(Ch);
- Inc(Idx);
- end;
- end;
- end;
-
- R_Get_Check :
- begin
- if not ReadQuotedByte then
- State := R_Timed_Out
- else begin
- if (GetCheckType = bcCrc16) then begin
- apUpdateBlockCheck(Byte(Ch));
- if not ReadQuotedByte then
- NewChk := Checksum xor $FF
- else begin
- NewChk := 0;
- apUpdateBlockCheck(Byte(Ch));
- end;
- end
- else NewChk := Ch;
-
- if NewChk <> Checksum then begin {checksum/CRC error}
- UpdateStatus(epNonFatal + ecBlockCheckError);
- State := R_Error;
- end
- else if RBuffer[0] = Ord('F') then {*always* accept a}
- State := R_Success {failure packet!}
- else if PacketNum = SeqNum then begin {dupe packet}
- UpdateStatus(epNonFatal + ecDuplicateBlock);
- State := R_Send_ACK;
- end
- else if PacketNum <> NextSeq then begin {out-of-sequence packet}
- UpdateStatus(epNonFatal + ecSequenceError);
- State := R_Get_DLE;
- end
- else
- State := R_Success;
- end;
- end;
-
- R_Timed_Out :
- begin
- UpdateStatus(epNonFatal + ecTimeout);
- State := R_Error;
- end;
-
- R_Error :
- begin
- Inc(TotalErrors);
- Inc(BlockErrors);
- if (BlockErrors > BP_Error_Max) or (FromSend) or (FatalAbort) then begin
- ReadPacket := False;
- exit;
- end;
- if NOT(NAKSent) or NOT(BPlus) then begin
- NAKSent := True;
- SendNAK;
- end;
- State := R_Get_DLE;
- end;
-
- R_Send_ACK :
- begin
- if NOT Aborting then
- bpSendACK;
- State := R_Get_DLE;
- end;
-
- R_Success :
- begin
- if NOT Aborting then
- SeqNum := PacketNum;
- ResumeFlag := False;
- RSize := Idx;
- Inc(R_Packets);
- Exit;
- end;
- end;
- end;
-
- procedure BPProtocol.SendData(BNum : Integer);
- var
- I : Integer;
- begin
- with SBuffer[BNum] do begin
- if (BPlus) and (GetCheckType = bcCrc16) then
- Checksum := $FFFF
- else
- Checksum := 0;
-
- SendByte(cDLE);
- SendByte('B');
-
- SendByte(Chr(Seq+Ord('0')));
- apUpdateBlockCheck(Byte(Seq+Ord('0')));
-
- for i := 0 to Num do begin
- SendQuotedByte(Buf[i]);
- apUpdateBlockCheck(Buf[i]);
- end;
-
- SendByte(cETX);
- apUpdateBlockCheck(ETX);
-
- if (BPlus) and (GetCheckType = bcCrc16) then
- SendQuotedByte(Hi(Checksum));
- SendQuotedByte(Lo(Checksum));
- end;
- end;
-
- function BPProtocol.IncSA(Value : Integer) : Integer;
- begin
- if Value = SAMax then
- IncSA := 0
- else
- IncSA := Value + 1;
- end;
-
- function BPProtocol.ReSync : Integer;
- const
- GetDLE1 = 1;
- GetDigit1 = 2;
- GetDLE2 = 3;
- GetDigit2 = 4;
- var
- State : Integer;
- Digit1 : Integer;
- begin
- UpdateStatus(epNonFatal + ecResync);
- ReSync := -1;
- SendByte(cENQ); {send <ENQ><ENQ>}
- SendByte(cENQ);
- State := GetDLE1;
-
- while True do
- case State of
- GetDLE1 :
- begin
- if not ReadByte then
- exit;
- if Ch = DLE then
- State := GetDigit1;
- end;
-
- GetDigit1 :
- begin
- if not ReadByte then
- exit;
- if Ch = Ord('B') then begin
- ReSync := Ch;
- exit;
- end
- else if (Ch >= Ord('0')) and (Ch <= Ord('9')) then begin
- Digit1 := Ch;
- State := GetDLE2;
- end;
- end;
-
- GetDLE2 :
- begin
- if not ReadByte then
- exit;
- if Ch = DLE then
- State := GetDigit2;
- end;
-
- GetDigit2 :
- begin
- if not ReadByte then
- exit;
- if (Ch >= Ord('0')) and (Ch <= Ord('9')) then begin
- if Digit1 = Ch then begin
- ReSync := Ch;
- exit;
- end
- else if Ch = Ord('B') then begin
- ReSync := Ord('B');
- exit;
- end
- else begin
- Digit1 := Ch;
- State := GetDLE2;
- end;
- end
- else State := GetDLE2;
- end;
- end;
- end;
-
- function BPProtocol.GetACK : Boolean;
- const
- S_Get_DLE = 0;
- S_Get_Num = 1;
- S_Have_ACK = 2;
- S_Get_Packet = 3;
- S_Skip_Packet = 4;
- S_Timed_Out = 5;
- S_Error = 6;
- S_Send_NAK = 7;
- S_Send_ENQ = 8;
- S_Send_Data = 9;
- var
- State : Byte;
- PacketNum, Idx : Integer;
- SAIdx : Integer;
- SentENQ : Boolean;
- begin
- GetACK := True;
- PacketRecd := False;
- SentENQ := False;
- BlockErrors := 0;
- State := S_Get_DLE;
-
- while True do
- case State of
- S_Get_DLE :
- begin
- if NOT(Aborting) and (CheckAbort) then begin {user wants out}
- UpdateStatus(epNonFatal + ecCancelRequested);
- SendFailure('AAborted by user');
- GetACK := False;
- exit;
- end;
- if not ReadByte then
- State := S_Timed_Out
- else begin
- case Ch of
- DLE : State := S_Get_Num; {potential ACK}
- NAK : State := S_Send_ENQ; {packet error}
- ETX : State := S_Send_NAK; {sequence problem}
- end;
- end;
- end;
-
- S_Get_Num :
- begin
- if not ReadByte then
- State := S_Timed_Out
- else begin
- if (Ch >= Ord('0')) and (Ch <= Ord('9')) then
- State := S_Have_ACK {we have an ACK, check it}
- else if Ch = Ord('B') then begin {incomming data packet,}
- if Aborting then {handle as needed}
- State := S_Skip_Packet
- else
- State := S_Get_Packet;
- end
- else if Ch = NAK then {sequence problem}
- State := S_Send_ENQ
- else if Ch = Ord(';') then begin
- UpdateStatus(epNonFatal + ecWaitACK); {show the user}
- State := S_Get_DLE;
- end
- else
- State := S_Get_DLE;
- end;
- end;
-
- S_Get_Packet :
- begin
- if ReadPacket(True, True) then begin {read the packet}
- PacketRecd := True;
- if RBuffer[0] = Ord('F') then begin {ACK any failure packet}
- bpSendACK; {and drop out}
- GetACK := False;
- exit;
- end;
- Next2ACK := IncSA(Next2ACK); {keep our SendAhead straight}
- Dec(SAWaiting);
- GetACK := True;
- exit;
- end
- else State := S_Get_DLE; {read failed, keep looking for ACK}
- end;
-
- S_Skip_Packet:
- begin
- if not ReadByte then {read bytes til an ETX arrives}
- State := S_Timed_Out
- else if Ch = ETX then begin
- if not ReadQuotedByte then {read & skip checksum}
- State := S_Timed_Out
- else if GetCheckType <> bcCrc16 then
- State := S_Get_DLE
- else if not ReadQuotedByte then {read & skip 2nd byte of CRC}
- State := S_Timed_Out
- else
- State := S_Get_DLE;
- end;
- end;
-
- S_Have_ACK :
- begin
- PacketNum := Ch - Ord('0');
- if SBuffer[Next2ACK].Seq = PacketNum then begin {the one!}
- Next2ACK := IncSA(Next2ACK);
- Dec(SAWaiting);
- if SAErrors > 0 then
- Dec(SAErrors);
- GetACK := True;
- exit;
- end
- else if (SBuffer[IncSA(Next2ACK)].Seq = PacketNum) and
- (SAWaiting = 2) then begin {must have missed an ACK}
- UpdateStatus(epNonFatal + ecSequenceError);
- Dec(SAWaiting,2);
- Next2ACK := IncSA(Next2ACK); {inc twice to skip the miss}
- Next2ACK := IncSA(Next2ACK);
- if SAErrors > 0 then
- Dec(SAErrors);
- GetACK := True;
- exit;
- end
- else if SBuffer[Next2ACK].Seq = IncSequence(PacketNum) then begin
- if SentENQ then
- State := S_Send_Data {remote missed first packet}
- else
- State := S_Get_DLE; {duplicate ACK}
- end
- else begin
- if Aborting then
- State := S_Get_DLE
- else
- State := S_Timed_Out;
- end;
- SentENQ := False;
- end;
-
- S_Timed_Out :
- begin
- UpdateStatus(epNonFatal + ecTimeout);
- State := S_Send_ENQ;
- end;
-
- S_Send_NAK :
- begin
- Inc(BlockErrors);
- Inc(TotalErrors);
- if (BlockErrors > BP_Error_Max) or (FatalAbort) then begin
- GetACK := False;
- exit;
- end;
- SendNAK;
- State := S_Get_DLE;
- end;
-
- S_Send_ENQ :
- begin
- Inc(BlockErrors);
- Inc(TotalErrors);
- if (BlockErrors > BP_Error_Max) or (FatalAbort) then begin
- GetACK := False;
- exit;
- end;
- Ch := ReSync; {try to resync with host}
- if Ch = -1 then
- State := S_Get_DLE
- else if Ch = Ord('B') then begin
- if Aborting then
- State := S_Skip_Packet
- else
- State := S_Get_Packet;
- end
- else
- State := S_Have_ACK;
- SentENQ := True;
- end;
-
- S_Send_Data :
- begin
- inc(SAErrors,3);
- if SAErrors >= 12 then {if too many SA errors, cease SendAhead}
- SAMax := 1;
- SAIdx := Next2ACK; {flush all pending packets to send}
- for Idx := 1 to SAWaiting do begin
- SendData(SAIdx);
- SAIdx := IncSA(SAIdx);
- end;
- SentENQ := False;
- State := S_Get_DLE;
- end;
- end;
- end;
-
- function BPProtocol.SAFlush : Boolean;
- {-get ACKs on outstanding packets after last packet sent}
- begin
- SAFlush := False;
- while SAWaiting > 0 do
- if not GetACK then
- exit;
- SAFlush := True;
- end;
-
- function BPProtocol.SendPacket(Size : Integer) : Boolean;
- {-send a packet of data}
- begin
- SendPacket := False;
- while SAWaiting >= SAMax do {allow for SendAhead dropout}
- if not GetACK then
- exit;
-
- SeqNum := IncSequence(SeqNum);
- SBuffer[Next2Fill].Seq := SeqNum;
- SBuffer[Next2Fill].Num := Size;
- SendData(Next2Fill);
- Next2Fill := IncSA(Next2Fill);
- Inc(SAWaiting);
- Inc(S_Packets);
- SendPacket := True;
- end;
-
- procedure BPProtocol.SendFailure(Reason : String);
- {-send a failure packet}
- begin
- Next2ACK := 0;
- Next2Fill := 0;
- SAWaiting := 0;
- Aborting := True;
-
- with SBuffer[0] do begin
- Buf[0] := Ord('F');
- Move(Reason[1],Buf[1],Length(Reason));
- end;
- if SendPacket(Length(Reason)) then
- if SAFlush then ;
- end;
-
- function BPProtocol.SendTransport : Boolean;
- {-send our transport settings}
- begin
- ClassicB := False; {if we're here, it's at least QuickB}
- ProtocolTypeString[ProtType] := 'Quick B';
-
- with SBuffer[Next2Fill] do begin
- Buf[0] := Ord('+');
- Buf[1] := DefWS;
- Buf[2] := DefWR;
- Buf[3] := DefBS;
- Buf[4] := DefCM;
- Buf[5] := DefDQ;
- Buf[6] := DefXP;
- Move(OurParams.QuoteSet,Buf[7],8);
- Buf[15] := DefDR;
- Buf[16] := DefUR;
- Buf[17] := DefFI;
- end;
- SendTransport := (SendPacket(17)) and (SAFlush);
- end;
-
- procedure BPProtocol.ProcessTransportParams(SendXPortInfo : Boolean);
- {-process received "+" packet, send our params if not a host}
- var
- QSP : Boolean;
- begin
- if UseSQuote then
- OurParams.QuoteSet := SQuoteSet
- else
- OurParams.QuoteSet := DQDefault;
- FillChar(RBuffer[RSize+1],SizeOf(RBuffer)-RSize,0);
- Move(RBuffer[1],HisParams.WinSend,4);
- Move(RBuffer[7],HisParams.QuoteSet,11);
-
- QSP := (RSize >= 14);
- UpdateQuoteTable(DQFull); {send '+' packet under FULL quoting}
-
- if SendXPortInfo then {don't send + packet if we're a host}
- if NOT SendTransport then exit;
-
- {make a minimal set of parameters to work from}
- if HisParams.WinSend < DefWR
- then OurParams.WinSend := HisParams.WinSend
- else OurParams.WinSend := DefWR;
- if OurParams.WinSend <> 0 then {if > 0, we can use all windows}
- SAMax := BP_SendAhead_Max;
-
- if HisParams.WinRecv < DefWS
- then OurParams.WinRecv := HisParams.WinRecv
- else OurParams.WinRecv := DefWS;
-
- if HisParams.BlkSize < DefBS
- then OurParams.BlkSize := HisParams.BlkSize
- else OurParams.BlkSize := DefBS;
- if OurParams.BlkSize = 0 then
- OurParams.BlkSize := 4; {default is 512-byte packets}
- BuffeRSize := (OurParams.BlkSize * 128);
-
- if HisParams.ChkType < DefCM
- then OurParams.ChkType := HisParams.ChkType
- else OurParams.ChkType := DefCM;
- if OurParams.ChkType > 0 then {if = 1, we need CRC blockchecking}
- CheckType := bcCrc16;
-
- if HisParams.DROpt < DefDR
- then OurParams.DROpt := HisParams.DROpt
- else OurParams.DROpt := DefDR;
-
- if HisParams.UROpt < DefUR
- then OurParams.UROpt := HisParams.UROpt
- else OurParams.UROpt := DefUR;
-
- if HisParams.FIOpt < DefFI
- then OurParams.FIOpt := HisParams.FIOpt
- else OurParams.FIOpt := DefFI;
-
- FillChar(QuoteTable,SizeOf(QuoteTable),0); {clear the Quote Table}
- UpdateQuoteTable(OurParams.QuoteSet); {set our quoting}
- if QSP then {if host sent a set,}
- UpdateQuoteTable(HisParams.QuoteSet); {add his as well}
- BPlus := True; {now using full B+}
- ProtocolTypeString[ProtType] := 'B Plus';
- end;
-
- procedure BPProtocol.bpInitVars;
- {-init vars that need resetting each time a DLE is seen}
- begin
- FillChar(SBuffer,SizeOf(SBuffer),0);
- Next2ACK := 0;
- Next2Fill := 0;
- SAWaiting := 0;
- SAMax := 1;
- AbortCount := 0;
- R_Packets := 0;
- R_Raw := 0;
- S_Packets := 0;
- S_Raw := 0;
- {$IFDEF ShowRates}
- R_Counter := 0;
- S_Counter := 0;
- {$ENDIF}
- TotalErrors := 0;
- ShowStatus := True;
- FatalAbort := False;
- PacketRecd := False;
- ResumeFlag := False;
- Direction := Unknown;
- end;
-
- function BPProtocol.bpDLESeen : Boolean;
- {-called by terminal handler when <DLE> seen. *MUST* be overridden!}
- begin
- RunError(211);
- end;
-
- {==== BPProtoFTP Methods ===================================================}
-
- constructor BPProtoFTP.Init(AP : AbstractPortPtr;
- DefaultResume : ResumeResultType);
- begin
- if NOT BPProtocol.Init(AP) then Fail;
- DefResume := DefaultResume;
- end;
-
- destructor BPProtoFTP.Done;
- begin
- BPProtocol.Done;
- end;
-
- procedure BPProtoFTP.apPrepareReading;
- {-open file to be sent to host}
- var
- Result : Integer;
- begin
- {preset display vars}
- SrcFileLen := 0;
- BytesRemaining := 0;
- BytesTransferred := 0;
-
- {open the file}
- Assign(WorkFile,PathName);
- Reset(WorkFile,1);
- Result := IOResult;
- if Result <> 0 then
- UpdateStatus(epNonFatal+Result);
-
- {set our open flag}
- FileOpen := (Result = 0);
-
- {reset display vars}
- if FileOpen then begin
- SrcFileLen := FileSize(WorkFile);
- BytesRemaining := SrcFileLen;
- BytesTransferred := 0;
- {reset timer for greater accuracy}
- NewTimer(Timer,TicsPerDay);
- {reset packet counts to reflect only data}
- S_Packets := 0;
- R_Packets := 0;
- end;
- end;
-
- procedure BPProtoFTP.apFinishReading;
- {-close file sent to host}
- begin
- {close workfile}
- if FileOpen then
- Close(WorkFile);
- if IOResult = 0 then ;
- FileOpen := False;
- end;
-
- procedure BPProtoFTP.SendFile;
- {-upload a file to the host}
- var
- N : Integer;
- Result : Integer;
- BS : Integer;
- begin
- LastXferOK := False;
- Direction := Upload;
- BS := OurParams.BlkSize * 128;
-
- {open the file if we can}
- apPrepareReading;
- if not FileOpen then begin
- SendFailure('MFile not found');
- exit;
- end;
-
- {display current status}
- UserStatus(@Self,False,False);
-
- {loop sending packets til error or complete}
- repeat
- with SBuffer[Next2Fill] do begin
- Buf[0] := Ord('N'); {"Next Packet" type packet}
- BlockRead(WorkFile,Buf[1],BS,N);
- Result := IOResult;
- if Result <> 0 then N := -1;
- end;
- if N > 0 then begin
- if NOT SendPacket(N) then begin
- AsyncStatus := epNonFatal + ecHostCan;
- UserStatus(@Self,False,False);
- apFinishReading;
- exit;
- end;
- {update display vars}
- Dec(BytesRemaining,N);
- Inc(BytesTransferred,N);
- UserStatus(@Self,False,False);
- end;
- until N <= 0;
- {close the file}
- apFinishReading;
-
- {if we had an error, display it and go home}
- if N < 0 then begin
- UpdateStatus(epNonFatal+Result);
- SendFailure('EFile read failure');
- exit;
- end;
-
- {send "Transfer Complete" packet}
- with SBuffer[Next2Fill] do begin
- Buf[0] := Ord('T');
- Buf[1] := Ord('C');
- if (SendPacket(2)) and (SAFlush) then
- LastXferOK := True;
- end;
- AsyncStatus := ecOK;
- end;
-
- procedure BPProtoFTP.apFinishWriting;
- {-close file recd from host}
- begin
- if FileOpen then
- Close(WorkFile);
- if IOResult = 0 then ;
- FileOpen := False;
- end;
-
- procedure BPProtoFTP.apPrepareWriting;
- {-opens a file to receive, handles resume/overwrite request}
- label
- ExitPoint;
- var
- Dir : DirStr;
- Name : NameStr;
- Ext : ExtStr;
- Result : Word;
- Res : ResumeResultType;
- OvrW : Boolean;
- ET : EventTimer;
- I : Integer;
- S,T : PathStr;
- F : LongInt;
- begin
- {Does the file exist already?}
- ResumeFlag := False;
- ResumeOK := False;
- FileOpen := False;
- OvrW := False;
- Assign(WorkFile, PathName);
- Reset(WorkFile, 1);
- Result := IOResult;
-
- {Exit on errors other than FileNotFound}
- if (Result <> 0) and (Result <> 2) then begin
- APort^.GotError(epFatal+Result);
- goto ExitPoint;
- end;
-
- {if file exists process potential resume}
- if (Result = 0) then begin
- if (@GetResume <> NIL) then
- Res := GetResume(@Self)
- else
- Res := DefResume;
- case Res of
- xfrAbort:
- begin
- APort^.GotError(epNonFatal+ecFileAlreadyExists);
- goto ExitPoint;
- end;
- xfrResume:
- ResumeFlag := True;
- xfrRename:
- APort^.GotError(epNonFatal + ecFileRenamed);
- xfrOverwrite:
- begin
- APort^.GotError(epNonFatal + ecOverwrite);
- OvrW := True;
- end;
- end;
- end;
-
- if ResumeFlag then begin
- {calculate CRC on existing file's contents}
- UpdateStatus(epNonFatal + ecTryResume);
- NewTimer(ET,Secs2Tics(SecsPerDay));
- F := FileSize(WorkFile);
- with SBuffer[Next2Fill] do begin
- Seek(WorkFile,0);
- Checksum := $FFFF;
- repeat
- BlockRead(WorkFile,Buf[0],512,Result);
- for I := 0 to (Result - 1) do
- apUpdateBlockCheck(Buf[i]);
- if ElapsedTimeInSecs(ET) >= 10 then begin {Send WACK so host knows}
- NewTimer(ET,Secs2Tics(SecsPerDay)); {we're busy}
- SendByte(cDLE);
- SendByte(';');
- UpdateStatus(epNonFatal + ecTryResume); {notify user}
- end;
- until (Result = 0) or (IOResult <> 0);
-
- {send the host a "Tr" packet with our info}
- FillChar(Buf,SizeOf(Buf),0);
- Buf[0] := Ord('T');
- Buf[1] := Ord('r');
- {send filesize and CRC}
- S := Long2Str(F) + ' ' + Long2Str(Checksum) + ' ';
- Move(S[1],Buf[2],Length(S));
-
- if NOT(SendPacket(Length(S)+1)) then
- goto ExitPoint;
- if NOT(SAFlush) then
- goto ExitPoint; {fatal error!}
-
- UpdateStatus(epNonFatal + ecHostResume); {notify user}
- BytesTransferred := F; {make calculations correct}
- Seek(WorkFile,F); {position ourselves}
- AsyncStatus := IOResult;
- if AsyncStatus <> 0 then begin {whoops!}
- Inc(AsyncStatus,epNonFatal);
- goto ExitPoint;
- end;
- ResumeOK := True;
- FileOpen := True;
- exit;
- end;
- end
-
- else begin
- Close(WorkFile);
- if IOResult = 0 then ;
-
- {Change the file name if needed}
- if (Result = 0) and NOT(ResumeFlag) and NOT(OvrW) then begin
- FSplit(Pathname, Dir, Name, Ext);
- Name[1] := '$';
- Pathname := Dir + Name + Ext;
- APort^.GotError(epNonFatal+ecFileRenamed);
- end;
-
- {Give status a chance to show that the file was renamed}
- UserStatus(@Self, False, False);
- AsyncStatus := ecOk;
-
- {Ok to rewrite file now}
- Assign(WorkFile, Pathname);
- Rewrite(WorkFile, 1);
- Result := IOResult;
- if Result <> 0 then begin
- APort^.GotError(epFatal+Result);
- goto ExitPoint;
- end;
- FileOpen := True;
- bpSendACK; {acknowledge the T packet}
- exit;
- end;
-
- ExitPoint:
- Close(WorkFile);
- if IOResult <> 0 then ;
- end;
-
- procedure BPProtoFTP.RecvFile;
- {-receive a file downloaded from the host}
- var
- Dir : DirStr;
- Name : NameStr;
- Ext : ExtStr;
- I : Integer;
- S : String[40];
- begin
- Direction := Download;
- LastXferOK := False;
- BytesRemaining := 0;
- BytesTransferred := 0;
- SrcFileLen := 0;
- apPrepareWriting;
- if (ResumeFlag) and NOT(ResumeOK) then {we failed}
- exit;
- if AsyncStatus <> ecOK then begin {notify host}
- SendFailure('CCannot create file');
- exit;
- end;
- NewTimer(Timer,Secs2Tics(SecsPerDay));
-
- while True do begin
- if ReadPacket(False,False) then begin
- ElapsedTics := ElapsedTime(Timer);
- case Chr(RBuffer[0]) of
- 'F': {"Failure" packet means we outta here}
- begin
- UpdateStatus(epNonFatal + ecHostCan);
- bpSendACK;
- apFinishWriting;
- exit;
- end;
-
- 'N': {"Next data" packet, write it to file}
- begin
- Inc(BytesTransferred, RSize-1); {update our data recd var}
- BytesRemaining := SrcFileLen-BytesTransferred;
- BlockWrite(WorkFile, RBuffer[1], RSize-1, I);
- ElapsedTics := ElapsedTime(Timer); {allow for write time}
- if (I <> RSize-1) or (IOResult <> 0) then begin
- apFinishWriting;
- UpdateStatus(epNonFatal + ecFileIO);
- SendFailure('EWrite failure');
- exit;
- end;
- UserStatus(@Self,False,False);
- bpSendACK;
- end;
-
- 'T': {A transfer control packet, process per second byte}
- begin
- case Chr(RBuffer[1]) of
- 'C': {"Transfer Complete" packet}
- begin
- UpdateStatus(epNonFatal + ecEndFile);
- apFinishWriting;
- bpSendACK;
- AsyncStatus := ecOK;
- LastXferOK := True;
- exit;
- end;
-
- 'I': {"Transfer Info" packet; we only use FileSize field here}
- begin
- bpSendACK;
- I := 4; {skip data type and compression flags}
- S := '';
- while (I <= RSize-1) and (Chr(RBuffer[i]) in ['0'..'9']) do begin
- S := S + Chr(RBuffer[i]);
- Inc(I);
- end;
- Val(S,BytesRemaining,I);
- if I <> 0 then
- BytesRemaining := 0;
- SrcFileLen := BytesRemaining;
- R_Packets := 0; {reset packet counts to reflect data}
- S_Packets := 0;
- NewTimer(Timer,Secs2Tics(SecsPerDay)); {reset timer for accuracy}
- end;
-
- 'f': {"Host Failed Resume"; rewrite the file}
- begin
- Close(WorkFile);
- {if we default to Rename, rename the file}
- if DefResume = xfrRename then begin
- FSplit(Pathname, Dir, Name, Ext);
- Name[1] := '$';
- Pathname := Dir + Name + Ext;
- Assign(WorkFile, PathName);
- APort^.GotError(epNonFatal+ecFileRenamed);
- end;
- {otherwise just overwrite}
- Rewrite(WorkFile,1);
- if IOResult <> 0 then begin
- FileOpen := False;
- UpdateStatus(epNonFatal + ecFileIO);
- SendFailure('CCannot create file');
- exit;
- end;
- BytesTransferred := 0;
- UpdateStatus(epNonFatal + ecResumeBad);
- ResumeFlag := False;
- R_Packets := 0; {reset packet counts to reflect data}
- S_Packets := 0;
- bpSendACK;
- NewTimer(Timer,Secs2Tics(SecsPerDay)); {reset timer for accuracy}
- end;
-
- else {I dunno, boss!}
- begin
- UpdateStatus(epNonFatal + ecUnexpectedChar);
- SendFailure('NInvalid T Packet');
- apFinishWriting;
- exit;
- end;
- end;
- end;
- end;
- end
- else begin {got a packet type we don't understand}
- UpdateStatus(epNonFatal+ecUnPacket);
- apFinishWriting;
- exit;
- end;
- end;
- end;
-
- function BPProtoFTP.bpDLESeen : Boolean;
- {-main handler called from terminal loop when <DLE> seen from host}
- label
- Skip;
- var
- I : Integer;
- Upl : Boolean;
- begin
- bpDLESeen := False;
- if Aborting then exit;
- bpInitVars;
-
- {<DLE> already seen, try to get 'B'}
- if NOT(ReadByte) or ((Ch and $7F) <> Ord('B')) then exit;
-
- UserStatus(@Self,True,False);
-
- {<DLE><'B'> seen, begin protocol processing}
- if ReadPacket(True,False) then begin
- case Chr(RBuffer[0]) of
- '+': {'+' packet: request for XPort params}
- with APort^ do begin
- ProcessTransportParams(True);
- UserStatus(@Self,False,True);
- exit;
- end;
-
- 'T': {'T' packet: Trigger FTP services}
- begin
- {Draw the initial status screen}
- {verify direction}
- if NOT(Chr(RBuffer[1]) in ['D','U']) then begin
- UpdateStatus(epNonfatal + ecUnexpectedChar);
- SendFailure('NUnimplemented Transfer Function');
- UserStatus(@Self,False,True);
- exit;
- end;
- {verify file type}
- if NOT(Chr(RBuffer[2]) in ['A','B','I']) then begin
- UpdateStatus(epNonfatal + ecUnexpectedChar);
- SendFailure('NUnimplemented File Type');
- UserStatus(@Self,False,True);
- exit;
- end;
- {retrieve pathname}
- PathName := '';
- I := 2;
- while (RBuffer[i] <> 0) and (I < RSize-1) do begin
- Inc(I);
- PathName := PathName + Upcase(Chr(RBuffer[i]));
- end;
-
- if Chr(RBuffer[2]) = 'A' then
- SetEfficiencyParms(10,1) {empirically-developed values}
- else
- SetEfficiencyParms(45,1);
-
- Upl := (Chr(RBuffer[1]) = 'U');
- if Upl then
- SendFile
- else
- RecvFile;
-
- UserStatus(@Self,False,True);
- bpDLESeen := True;
- exit;
- end;
-
- else
- begin {else an unsupported packet type}
- UserStatus(@Self,True,False);
- UpdateStatus(epNonFatal + ecUnexpectedChar);
- SendFailure('NUnknown packet type');
- UserStatus(@Self,False,True);
- exit;
- end;
- end;
- end;
- end;
-
- {=== BPProtoGIF Methods ====================================================}
-
- constructor BPProtoGIF.Init(AP : AbstractPortPtr);
- begin
- if NOT BPProtocol.Init(AP) then Fail;
- end;
-
- destructor BPProtoGIF.Done;
- begin
- BPProtocol.Done;
- end;
-
- function BPProtoGIF.bpDLESeen : Boolean;
- {-called by terminal handler when <DLE> seen at port}
- begin
- bpDLESeen := False;
- if Aborting then exit;
- bpInitVars;
- {make sure we don't display status info}
- SetShowStatusProc(NoUserStatus);
-
- {<DLE> already seen, try to get 'B'}
- if NOT(ReadByte) or (Ch <> Ord('B')) then exit;
-
- {<DLE><'B'> seen, handle '+' packet (others handled from within
- your GIF decoder - see the documentation)}
- if ReadPacket(True,False) then begin
- if Chr(RBuffer[0]) = '+' then begin
- ProcessTransportParams(True);
- bpDLESeen := True;
- end
- else begin
- SendFailure('NUnknown packet type');
- end;
- end;
- end;
-
- function BPProtoGIF.bpGetGIFDataBlock(var P;
- var PSize : Word;
- var IsLast : Boolean) : Boolean;
- {-get next packet of GIF data into P}
- begin
- if ReadPacket(False,False) then begin
- bpGetGIFDataBlock := True;
- IsLast := ((Chr(RBuffer[0]) = 'T') and (Chr(RBuffer[1]) = 'C')) or
- (Chr(RBuffer[0]) = 'F');
- Move(RBuffer[1],P,RSize-1);
- PSize := RSize-1;
- end
- else
- bpGetGIFDataBlock := False;
- end;
-
- end.
-