{$IFDEF MSDOS}
{$O+,F+}
{$ENDIF}
{$X+,V-,I-}

Unit Protocol;

{*********************************************************}
{*                    PROTOCOL.PAS                       *}
{*                                                       *}
{*  Copyright (c) Konstantin Klyagin, 1995-98,           *}
{*                exspecially for Tornado BBS System     *}
{*                                                       *}
{*********************************************************}

Interface

Uses
{$IFNDEF WIN32}
  DOS,
  ApAbsPcl,
  ApSame,
  ApXModem,
  ApYModem,
  ApZModem,
  ApCom,
  Iface,
{$ELSE}
  Classes,
  AdProtcl,
  SysUtils,
  Windows,
  ooMisc,
  Forms,
  WApro,
{$ENDIF}

{$IFDEF MSDOS}
  ApPort,
{$ENDIF}

{$IFDEF OS2}
  Os2Base,
{$ENDIF}
  Resource,
  TimeTask,
  tModem,
  Log,
  Users,
  Parse,
  mFind,
  FilesBBS,
  tMisc,
  MainComm,
  OpCrt,
  TGlob,
  Objects,
  Parser;

Type
  tTransferState = (tsUploadMsg, tsNormal, tsPrivate);

Var
  UpLoadToUser : String [36];

Const
  TransferTime : LongInt = 0;
  TransferSize : LongInt = 0;

Function AcceptFile ({$IFNDEF WIN32} P: ProtocolRecPtr {$ELSE} FName: String {$ENDIF}): Boolean;
Procedure Transfer (FileName: String; TransferMode: TransferModeType; State: tTransferState);
Procedure LogFile ({$IFNDEF WIN32} Prot: ProtocolRecPtr; LogFileStatus: LogFileType {$ELSE} LogFileStatus: Word {$ENDIF});
Function Next2Transfer ({$IFNDEF WIN32} P: ProtocolRecPtr; {$ENDIF} Var FName: PathStr): Boolean;

{$IFNDEF WIN32}
Procedure StatusP (Prot: ProtocolRecPtr; Starting, Ending: Boolean);
Function Status2Str (Code: Word): String;
Procedure Errorz (P: Pointer; Var StatusCode: Word);
{$ELSE}
Procedure ProtocolMsg (S: String);
{$ENDIF}

Procedure DownLoad;
Procedure ClearCL;
Procedure ClearSent;

Implementation

{$IFDEF WIN32}
Uses
  tor32u;
{$ENDIF}

Const
  CLDirName : PathStr = '';

Var
  trState                               : tTransferState;
  Index2Transfer, CurrentFileIndex      : Word;
  LogString                             : String [100];
  xFile                                 : PathStr;
{$IFNDEF WIN32}
  Resuming, Informed                    : Boolean;
{$ENDIF}

Const
  lfName  : String = '';
  lfState : tTransferState = tsPrivate;

{$IFDEF WIN32}

Procedure ProtocolMsg (S: String);
Begin
  MainForm. Memo2. Lines. Add (S);
End;

{$ENDIF}

Procedure UploadDone;
Var
  TC               : Boolean;
  dto, AddTime, i  : LongInt;
  AddSize          : LongInt;
  Ft               : Text;
  S, S1            : String;
  P                : PString;
{$IFDEF WIN32}
  DT               : DateTime;
  F1               : THandle;
{$ELSE}
  DT               : DOS. DateTime;
  F                : File;
{$ENDIF}

Begin
  If InShell Then
  Begin
    UpFiles^. DeleteAll;
    TransferTime := 0;
    TransferSize := 0;
    Exit;
  End;

  AddTime := Round (TransferTime/60)*Cnf. UploadTimePlus;
  If (AddTime >= 1) And (trState = tsNormal) Then
  Begin
    Inc (R. TotalTime, AddTime * 60);
{    Message (lang (laUploadPlus) + ' ' + Long2Str (AddTime) + ' ' + lang (laMinutes));}
    ComWriteLn (lang (laUploadPlus) + ' ' + Long2Str (AddTime) + ' ' + lang (laMinutes), eoMacro + eoCodes);
  End;

  AddSize := TransferSize * Cnf. UploadSizePlus;
  If (AddSize >= 1) And (trState = tsNormal) Then
  Begin
    Inc (R. DailySize, AddSize);
    ComWriteLn (lang (laUploadPlusKB) + ' ' + Long2Str (AddSize) + ' ' + lang (laKB), eoMacro + eoCodes);
{    Message (lang (laUploadPlus) + ' ' + Long2Str (AddSize) + ' ' + lang (laMinutes));}
  End;
  If ((AddTime >= 1) Or (AddSize >= 1)) And (trState = tsNormal) Then
    Message ('');

  If (UpFiles^. Count > 0) and (lfName = '') Then
  If lfState <> tsPrivate Then
  For i := 0 To UpFiles^. Count - 1 Do
  Begin
    ComWrite (lang (laULDescription) + JustFileName (GetStr (UpFiles^. At (i))) + ': ', eoMacro + eoCodes);

    If Cnf. DLCount And (Trim (Cnf. DLCountMask) <> '')
    Then S1 := ZeroMask (Cnf. DLCountMask) + ' '
    Else S1 := '';

    TC := TimeCount;
    TimeCount := False;
    S := '';
    ComReadLn (S, 60, ofAllowEmpty);
    S := S1 + S;

  {$IFDEF WIN32}
    If Application. Terminated Then Exit;
  {$ENDIF}

  {$IFNDEF WIN32}
    Assign (F, GetStr (UpFiles^. At (i)));
    SetFAttr (F, Archive);
    ReSet (F);
  {$ELSE}
    Try FileSetAttr (GetStr (UpFiles. At (i)), faArchive);
    Except
    End;
  {$ENDIF}

    If Cnf. AjustULDate Then
    Begin
      GetDateTime (DT);
      PackTime (DT, dto);
    {$IFDEF WIN32}
      Try
        F1 := FileOpen (GetStr (UpFiles. At (i)), fmOpenRead or fmShareDenyNone);
        If F1 > 0 Then
        Begin
          FileSetDate (F1, dto);
          FileClose (F1);
        End;
      Except
      End;
    {$ELSE}
      SetFTime (F, dto);
      Close (F);
    {$ENDIF}
    End;

    AddDescription (FileArea. ULPath + 'files.bbs',
    UpString (JustFileName (GetStr (UpFiles^. At (i)))), S);

    ExecScript ('afterupl ' + JustFileName (GetStr (UpFiles^. At (i))));
    TimeCount := TC;
  End Else
  Begin
    Assign (Ft, Cnf. SaveTagPath + HexL (Crc32Str (UpLoadToUser)) + '.lst');
    If FileExists (Cnf. SaveTagPath + HexL (Crc32Str (UpLoadToUser)) + '.lst') Then Append (Ft) Else ReWrite (Ft);

    If IOResult <> 0 Then
    Begin
      UpFiles^. DeleteAll;
      TransferTime := 0;
      TransferSize := 0;
      Exit;
    End;

    WriteLn (Ft, '; ' + UpLoadToUser);

    For i := 0 To UpFiles^. Count - 1 Do
    WriteLn (Ft, '*' + R. Name + '|' + GetStr (UpFiles^. At (i)));

    Close (Ft);
  End;

  If (UpFiles^. Count > 0) And (trState = tsNormal) Then
  Begin
    Assign (Ft, Cnf. FlagsDir + FileFlag + '.' + LeftPadCh (Long2Str (BbsLine), '0', 3));
    If FileExists (Cnf. FlagsDir + FileFlag + '.' +
       LeftPadCh (Long2Str (BbsLine), '0', 3))
    Then Append (Ft) Else ReWrite (Ft);

    For i := 0 To UpFiles^. Count-1 Do
    Begin
      P := UpFiles^. At (i);
      WriteLn (Ft, GetStr (P));
    End;

    Close (Ft);
  End;

  UpFiles^. DeleteAll;
  TransferTime := 0;
  TransferSize := 0;
End;

{$IFNDEF WIN32}
Function Status2Str (Code: Word): String;
Begin
  Case Code mod 10000 of
    ecOk: Status2Str := 'Ok';

    {DOS errors}
    ecFileNotFound: Status2Str := sm (sstatFileNotFound);
    ecPathNotFound: Status2Str := sm (sstatPathNotFound);
    ecTooManyFiles: Status2Str := sm (sstatTooManyFiles);
    ecAccessDenied: Status2Str := sm (sstatAccessDenied);
    ecInvalidHandle: Status2Str := sm (sstatInvalidHandle);
    ecOutOfMemory: Status2Str := sm (sstatOutOfMemory);
    ecInvalidDrive: Status2Str := sm (sstatInvalidDrive);
    ecNoMoreFiles: Status2Str := sm (sstatNoMoreFiles);

    {Turbo Pascal I/O errors}
    ecDiskRead: Status2Str := sm (sstatDiskRead);
    ecDiskFull: Status2Str := sm (sstatDiskFull);
    ecNotAssigned: Status2Str := sm (sstatNotAssigned);
    ecNotOpen: Status2Str := sm (sstatNotOpen);
    ecNotOpenInput: Status2Str := sm (sstatNotOpenInput);
    ecNotOpenOutput: Status2Str := sm (sstatNotOpenOutput);
    ecInvalidFormat: Status2Str := sm (sstatInvalidFormat);

    {DOS critical errors}
    ecWriteProtected: Status2Str := sm (sstatWriteProtected);
    ecUnknownUnit: Status2Str := sm (sstatUnknownUnit);
    ecDriveNotReady: Status2Str := sm (sstatDriveNotReady);
    ecUnknownCommand: Status2Str := sm (sstatUnknownCommand);
    ecCrcError: Status2Str := sm (sstatCrcError);
    ecBadStructLen: Status2Str := sm (sstatBadStructLen);
    ecSeekError: Status2Str := sm (sstatSeekError);
    ecUnknownMedia: Status2Str := sm (sstatUnknownMedia);
    ecSectorNotFound: Status2Str := sm (sstatSectorNotFound);
    ecOutOfPaper: Status2Str := sm (sstatOutOfPaper);
    ecDeviceWrite: Status2Str := sm (sstatDeviceWrite);
    ecDeviceRead: Status2Str := sm (sstatDeviceRead);
    ecHardwareFailure: Status2Str := sm (sstatHardwareFailure);

    {APUART port errors}
    ecNoMorePorts: Status2Str := sm (sstatNoMorePorts);
    ecOverrunError: Status2Str := sm (sstatOverrunError);
    ecParityError: Status2Str := sm (sstatParityError);
    ecFramingError: Status2Str := sm (sstatFramingError);

    {APINT14 port errors}
    ecTransmitFailed: Status2Str := sm (sstatTransmitFailed);
    ecUartError: Status2Str := sm (sstatUartError);

    {APCOM/OOCOM errors/status}
    ecBlockIncomplete: Status2Str := sm (sstatBlockIncomplete);
    ecBufferIsFull: Status2Str := sm (sstatBufferIsFull);
    ecBufferIsEmpty: Status2Str := sm (sstatBufferIsEmpty);
    ecTimeout: Status2Str := sm (sstatTimeout);
    ecStringIncomplete: Status2Str := sm (sstatStringIncomplete);
    ecStringOverrun: Status2Str := sm (sstatStringOverrun);
    ecUserAbort: Status2Str := sm (sstatUserAbort);

    {APMODEM/OOMODEM errors}
    ecTableFull: Status2Str := sm (sstatTableFull);
    ecNullCommand: Status2Str := sm (sstatNullCommand);

    {Tracing/EventFile file errors}
    ecEventFileError: Status2Str := sm (sstatEventFileError);
    ecTraceFileError: Status2Str := sm (sstatTraceFileError);

    {APCOM/OOCOM port errors}
    ecBadPortNumber: Status2Str := sm (sstatBadPortNumber);
    ecOutofRange: Status2Str := sm (sstatOutofRange);
    ecPortNotOpen: Status2Str := sm (sstatPortNotOpen);
    ecInvalidBaudRate: Status2Str := sm (sstatInvalidBaudRate);
    ecInvalidArgument: Status2Str := sm (sstatInvalidArgument);
    ecNoDevice: Status2Str := sm (sstatNoDevice);
    ecNotaUart: Status2Str := sm (sstatNotaUart);
    ecInvalidParity: Status2Str := sm (sstatInvalidParity);
    ecBadFileList: Status2Str := sm (sstatBadFileList);
    ecNotBuffered: Status2Str := sm (sstatNotBuffered);
    ecNotSupported: Status2Str := sm (sstatNotSupported);

    {Device layers error codes}
    ecNoFossil: Status2Str := sm (sstatNoFossil);
    ecDigiFailure: Status2Str := sm (sstatDigiFailure);

    {APXMODEM/OOABSPCL status codes}
    ecInitFail: Status2Str := sm (sstatInitFail);
    ecInitCancel: Status2Str := sm (sstatInitCancel);
    ecCancelRequested: Status2Str := sm (sstatCancelRequested);
    ecError: Status2Str := sm (sstatError);
    ecDuplicateBlock: Status2Str := sm (sstatDuplicateBlock);
    ecSequenceError: Status2Str := sm (sstatSequenceError);
    ecDirNotFound: Status2Str := sm (sstatDirNotFound);
    ecNoMatchingFiles: Status2Str := sm (sstatNoMatchingFiles);
    ecLongPacket: Status2Str := sm (sstatLongPacket);
    ecEndFile: Status2Str := sm (sstatEndFile);
    ecHandshakeInProgress: Status2Str := sm (sstatHandshakeInProgress);
    ecFileRenamed: Status2Str := sm (sstatFileRenamed);
    ecFileAlreadyExists: Status2Str := sm (sstatFileAlreadyExists);
    ecInvalidFilesize: Status2Str := sm (sstatInvalidFilesize);
    ecInvalidDateTime: Status2Str := sm (sstatInvalidDateTime);
    ecUnexpectedChar: Status2Str := sm (sstatUnexpectedChar);
    ecBlockCheckError: Status2Str := sm (sstatBlockCheckError);
    ecNoSearchMask: Status2Str := sm (sstatNoSearchMask);
    ecNoFilename: Status2Str := sm (sstatNoFilename);
    ecAsciiReceiveInProgress: Status2Str := sm (sstatAsciiReceiveInProgress);
    ecFileRejected: Status2Str := sm (sstatFileRejected);
    ecTooManyErrors: Status2Str := sm (sstatTooManyErrors);

    {APZMODEM/OOZMODEM status codes}
    ecGotCrcE: Status2Str := sm (sstatGotCrcE);
    ecGotCrcW: Status2Str := sm (sstatGotCrcW);
    ecGotCrcQ: Status2Str := sm (sstatGotCrcQ);
    ecGotCrcG: Status2Str := sm (sstatGotCrcG);
    ecGarbage: Status2Str := sm (sstatGarbage);
    ecSkipFile: Status2Str := sm (sstatSkipFile);
    ecBadPosition: Status2Str := sm (sstatBadPosition);
    ecFileDoesntExist: Status2Str := sm (sstatFileDoesntExist);
    ecCantWriteFile: Status2Str := sm (sstatCantWriteFile);
    ecFailedToHandshake: Status2Str := sm (sstatFailedToHandshake);
    ecNoFilesToReceive: Status2Str := sm (sstatNoFilesToReceive);

    {APMODEM/OOMODEM status codes}
    ecUnknownModemResult: Status2Str :=  sm (sstatUnknownModemResult);
    ecConnect: Status2Str := 'CONNECT';
    ecRing: Status2Str := 'RING';
    ecNoCarrier: Status2Str := 'NO CARRIER';
    ecNoDialTone: Status2Str := 'NO DIALTONE';
    ecBusy: Status2Str := 'BUSY';
    ecNoAnswer: Status2Str := 'NO ANSWER';
    ecRinging: Status2Str := 'RINGING';
    ecVoice: Status2Str := 'VOICE';

    Else Status2Str := sm (sstatUnknownErrorCode) + Long2Str (Code);

  End;
End;

Procedure BackGroundProc (Prot: ProtocolRecPtr);
Begin
  TimeSlice;
  Clock2;
End;

Procedure StatusP (Prot: ProtocolRecPtr; Starting, Ending: Boolean);
Begin
  If Starting Then
  Begin
    ProtocolMsg (sm (smtStart));
    Resuming := False;
    Informed := False;
  End;

  If (Not Informed) and (GetFileName (Prot) <> '') Then
  Begin
    If TrMode = Transmit Then
      ProtocolMsg (sm (smStartSend) + Trim (UpString (JustFileName
                  (GetPathName (Prot)))) + ' (' + Long2Str
                  (gFileSize (GetPathName (Prot))) + sm (smBytes) + ')')
    Else
      ProtocolMsg (sm (smStartRecv) + Trim (UpString (JustFileName
                  (GetPathName (Prot)))) + ' (' + Long2Str
                  (GetFileSize (Prot)) + sm (smBytes) + ')');

    Informed := True;
  End;

  If (Not Resuming) and (GetInitialFilePos (Prot) > 0) Then
  Begin
    ProtocolMsg (sm (smpResuming) + ' ' + Long2Str (GetInitialFilePos (Prot)));
    Resuming := True;
  End;

  Clock2;

  If Not (Starting or Ending) Then
  Begin
    UpdateProtBox (Prot);
    Exit;
  End;

  AsyncStatus := AsyncStatus mod 10000;
  If AsyncStatus <> ecOk Then ProtocolMsg (Status2Str (AsyncStatus));
End;

{$ENDIF}

Function AcceptFile
{$IFNDEF WIN32} (P: ProtocolRecPtr)
{$ELSE} (fName: String)
{$ENDIF}: Boolean;

{$IFNDEF WIN32}
Var
  fName : String;
{$ENDIF}
Var
  Acc : Boolean;

Begin
{$IFNDEF WIN32}
  fName := GetPathName (P);
{$ENDIF}

  Acc :=
    Not IsDevice (JustFileName (fName))
    And (Not FileExists (fName) Or (FileExists (fName) And ((gFileAttr (fName) And Hidden) <> 0) Or Cnf. ULOverWrite))
    And Not ((AddBackSlash (JustPathName (fName)) = Cnf. DoorInfoDir)
         And (UpString (JustExtension (fName)) <> 'REP') And (trState = tsNormal))
    And Not ((AddBackSlash (JustPathName (fName)) = Cnf. SaveTagPath) And (UpString (JustExtension (fName)) = 'LST'));
  if Not Acc Then LogWrite ('#','File ' + fName + ' not accepted');
  AcceptFile := Acc;
End;

Procedure ClearCL;
Var
  S : String;

Begin
  If (Reps^. Count > 0) And DirExists (CLDirName) Then
  Begin
    While Reps^. Count > 0 Do
    Begin
      S := GetStr (Reps^. At (0));

      If R. Protocol in ['1'..'8'] Then
      Begin
        tDeleteFile (Copy (S, Pos (#255, S)+1, 255));
        S := Cnf. DoorInfoDir + JustFileName (Copy (S, 1, Pos (#255, S)-1));
      End;

      tDeleteFile (S);
      DisposeStr (Reps^. At (0));
      Reps^. AtDelete (0);
    End;
    RmDir (CLDirName);
    If IOResult <> 0 Then;
  End;
End;

Procedure ClearSent;
Var
  i       : LongInt;

Begin
  i := 0;

  While F2Transfer^. Count > 0 Do
  Begin
    If i > F2Transfer^. Count-1 Then Break;

    If (PTagFileRec (F2Transfer ^. At (i))^. PathName = '!') Or
       (PTagFileRec (F2Transfer^. At (i))^. AreaNum = 0) Then
    Begin
      DisposeTagFile (F2Transfer ^. At (i));
      F2Transfer ^. AtDelete (i);
      i := 0;
      Continue;
    End;

    Inc (i);
  End;

  ClearCL;
  UpdateUserMacro;
End;

Procedure DownLoadOk (FSize, i: LongInt);
Var
  F1, F2        : String;
  j             : LongInt;

Begin
  Inc (TotalBytesTrans, FSize);
  Inc (SessionDL, Round (FSize/1024));
  Dec (SizeOfAll, FSize);

  If (i <> -1) And (i+1 <= F2Transfer^. Count) Then
  Begin
    With PTagFileRec (F2Transfer ^. At (i))^ Do
    Begin
      If R. FileGroup <> GroupNum Then SetFileGroup (GroupNum);
      If R. FileArea <> AreaNum Then SetFileArea (AreaNum);
    End;

    F1 := PTagFileRec (F2Transfer ^. At (i))^. PathName;

    If PTagFileRec (F2Transfer ^. At (i))^. FromName <> '' Then
    Begin
      If AddBackSlash (UpString (JustPathName (F1))) =
         AddBackSlash (UpString (Cnf. PrivUploadsDir))
      Then tDeleteFile (F1);
    End Else
      If Cnf. DLCount Then
        IncFilesBBSCounter (JustFileName (F1), FileArea. FileList,
        Cnf. DLCountMask, FileArea. FListFormat = fCDList);

    If (FileArea. CopyLocal) And Not Local Then
    For j := 0 To Reps^. Count-1 Do
    Begin
      F2 := ExtractWord (1, GetStr (Reps^. At (j)), [#255]);
      If F1 = F2 Then
      Begin
        F2 := Cnf. DoorInfoDir + JustFileName (F1);
        tDeleteFile (F2);
        Break;
      End;
    End;

    If i < F2Transfer^. Count Then PTagFileRec (F2Transfer^. At (i))^. PathName := '!';

    If Not PTagFileRec (F2Transfer^. At (i))^. Free Then
    Begin
      Inc (R. DownLoads);
      Inc (Sys. DownLoads);
      Inc (R. DownLoadsK, Round (FSize/1024));
      Inc (R. TodayK, Round (FSize/1024));
    End;
  End;
End;

Procedure UpLoadOk (FName: PathStr; FSize, Elapsed: LongInt);
Begin
  If (trState in [tsNormal, tsPrivate]) And Not InShell Then
  Begin
    If trState = tsNormal Then
    Begin
      Inc (R. UpLoads);
      Inc (Sys. UpLoads);
      Inc (R. UpLoadsK, Round (FSize/1024));
      Inc (SessionUL, Round (FSize/1024));
      Inc (TransferTime, Round (Elapsed/18));
      Inc (TransferSize, Round (FSize/1024));
    End;

    UpFiles^. InsLine (FName);
  End;
End;

Function Next2Transfer ({$IFNDEF WIN32} P: ProtocolRecPtr; {$ENDIF} Var FName: PathStr): Boolean;
Var
  i     : LongInt;
  S     : PathStr;

Label
  Loop;

Begin
  Next2Transfer := False;
  FName := '';

  Loop:
  If Index2Transfer-1 < F2Transfer^. Count Then
  Begin
    CurrentFileIndex := Index2Transfer-1;
    FName := PTagFileRec (F2Transfer^. At (Index2Transfer-1))^. PathName;
    Next2Transfer := True;
    Inc (Index2Transfer);

    If FName = '!' Then
    Begin
      Next2Transfer := Index2Transfer-1 < F2Transfer^. Count;
      GoTo Loop;
    End;

    For i := 0 To Reps^. Count-1 Do
    Begin
      S := GetStr (Reps^. At (i));
      S := Copy (S, 1, Pos (#255, S)-1);
      If S = FName Then
      Begin
        tRenameFile (Copy (GetStr (Reps^. At (i)), Length (S)+2, 255),
        Cnf. DoorInfoDir + JustFileName (FName));
        FName := Cnf. DoorInfoDir + JustFileName (FName);
        Break;
      End;
    End;

    SetTitle ('downloading file ' + NiceFileName (FName, 30));
  End;
End;

Procedure LogFile ({$IFNDEF WIN32} Prot: ProtocolRecPtr; LogFileStatus: LogFileType {$ELSE} LogFileStatus: Word {$ENDIF});
Var
  CPSrate, IPos,
  FSize, ETics  : LongInt;
  PName, PType  : String;
{$IFNDEF WIN32}
  Fil           : File;
{$ENDIF}

Begin
  If (Prot = nil) Or Local Then
  Begin
    ETics := 0;
    FSize := gFileSize (xFile);
    IPos := 0;
    PName := xFile;
    PType := 'LOCAL';
  End Else
  Begin
    ETics := GetElapsedTics (Prot);
    FSize := GetFileSize (Prot);
    IPos := GetInitialFilePos (Prot);
    PName := GetPathName (Prot);
    PType := {$IFNDEF WIN32} ProtocolTypeString [GetProtocol (Prot)]
             {$ELSE} ProtTypeStr [Prot. ProtocolType]
             {$ENDIF};
  End;

  If trState = tsUpLoadMsg Then
  Begin
    tRenameFile (PName, AddBackSlash (Cnf. DoorInfoDir) + 'msgtmp.');
    Exit;
  End;

  Case LogFileStatus Of

    lfReceiveOk :
    Begin
      LogString := Copy (PType, 1, 1) + 'R: ' + sm (smFile) +
      Trim (NiceFileName (PName, 40));

      If IPos <> 0 Then LogString := LogString + ' (from ' + Long2Str (IPos) + ')';
      LogString := LogString + ' ' + NiceFileSize (FSize);

      UpLoadOk (PName, FSize, ETics);
    {$IFNDEF WIN32}
      Assign (Fil, GetPathName (Prot));
      SetFAttr (Fil, 0);
    {$ELSE}
      DOSerror := FileSetAttr (Prot. FileName, 0);
    {$ENDIF}
    End;

    lfReceiveFail :
    Begin
      LogString := Copy (PType, 1, 1) + 'R: ' + sm (smFile) +
      Trim (NiceFileName (PName, 40));
      If IPos <> 0 Then LogString := LogString + ' (' +
      sm (smpFrom) + ' ' + Long2Str (IPos) + ')';

      LogString := LogString + sm (smReceiveFail) + ' ' +
      sm (smpAtPos) + ' ' + Long2Str (GetBytesTransferred (Prot));

    {$IFNDEF WIN32}
      Assign (Fil, GetPathName (Prot));
      SetFAttr (Fil, Hidden);
    {$ELSE}
      DOSerror := FileSetAttr (Prot. FileName, faHidden);
    {$ENDIF}
    End;

    lfReceiveSkip :
      LogString := Copy (PType, 1, 1) + 'R: ' + sm (smFile) +
      Trim (NiceFileName (PName, 40)) + sm (smReceiveSkip);

    lfTransmitOk :
    Begin
      LogString := Copy (PType, 1, 1) + 'S: ' + sm (smFile) +
      Trim (NiceFileName (PName, 40));

      If IPos <> 0 Then LogString := LogString + ' (' +
      sm (smpFrom) + ' ' + Long2Str (IPos) + ')';

      LogString := LogString + ' ' + NiceFileSize (FSize);
      DownLoadOk (FSize, CurrentFileIndex);
    End;

    lfTransmitFail :
    Begin
      LogString := Copy (PType, 1, 1) + 'S: ' + sm (smFile) +
      Trim (NiceFileName (PName, 40));

      If IPos <> 0 Then
        LogString := LogString + ' (' + sm (smpFrom) + ' ' +
        Long2Str (IPos) + ')';

      LogString := LogString + sm (smTransmitFail) + ' ' +
      sm (smpAtPos) + ' ' + Long2Str (GetBytesTransferred (Prot));
    End;

    lfTransmitSkip :
      LogString := Copy (PType, 1, 1) + 'S: ' + sm (smFile) +
      Trim (NiceFileName (PName, 40)) + sm (smTransmitSkip);

  End;

  If LogFileStatus in [lfReceiveOk, lfTransmitOk] Then
  Begin
    If ETics <> 0
    Then CPSrate := Round ((FSize-IPos)/(ETics/18.2))
    Else CPSrate := Round (GetConnectSpeed/10);

    LogString := LogString + ' ' + Long2Str (CPSrate) + ' CPS Ok';
  End;

  If (LogFileStatus in [lfReceiveOk..lfReceiveSkip]) Or
     (LogFileStatus in [lfTransmitOk..lfTransmitSkip]) Then
  Begin
    If Prot <> nil Then ProtocolMsg (LogString);
    LogWrite ('*', LogString);
  End;

  If (LogFileStatus in [lfReceiveOk, lfTransmitOk]) And (ETics <> 0) Then
  If R. AvgCPS = 0
  Then R. AvgCPS := Round ((FSize-IPos)/(ETics/18.2))
  Else R. AvgCPS := Round ((R. AvgCPS+Round ((FSize-IPos)/(ETics/18.2)))/2);

{$IFNDEF WIN32}
  If Prot <> nil Then
  Begin
    ApAbsPcl. AvgCPS := R. AvgCPS;
    UpdateProtBox (Prot);
    Prot^. PData^. InitFilePos := 0;
    Prot^. PData^. BytesTransferred := 0;
  End;
{$ENDIF}
End;

Procedure Transfer (FileName: String; TransferMode: TransferModeType; State: tTransferState);
Var
  i             : Integer;
  S, S1, CurDir,
  ULDir, FName  : String;
  oFileArea,
  oSizeOfAll    : LongInt;
  tR            : tUser;
  List          : Text;
  KWord         : String [40];
  SRec          : mSearchRec;
  DirCreated, B : Boolean;
{$IFNDEF WIN32}
  F             : File;
  SB            : Boolean;
{$ENDIF}

Function CheckFreeSpace : Boolean;
Var
  oDir  : PathStr;
  dFree : LongInt;

Begin
  CheckFreeSpace := True;
  If Cnf. UploadSpace = 0 Then Exit;

  GetDir (0, oDir);
  SmartChDir (JustPathname (ULDir));
  If IOResult <> 0 Then;
  dFree := DiskFree (0);
  If dFree < 0 Then dFree := 2147483647;
  If Round (dFree/1024) < Cnf. UpLoadSpace Then
  Begin
    CheckFreeSpace := False;
    Message (lang (laUpLoadSpace));
  End;

  SmartChDir (oDir);
End;

Procedure ShowTransferHeader;
Begin
  UpdateUserMacro;

  Cls;
  ComWriteLn (lang (laDownLoadHead1), eoMacro + eoCodes);
  ComWriteLn (lang (laDownLoadHead2), eoMacro + eoCodes);

  If Not Local And Not AutoDL Then
  Begin
    ComWriteLn (lang (laDownLoadHead3), eoMacro + eoCodes);
    ComWriteLn (lang (laDownLoadHead4), eoMacro + eoCodes);
  End;
End;

Procedure ShutDown;
Begin
  ClearCL;
  If (FileName <> '') And (TransferMode = Transmit) Then
  Begin
    SizeOfAll := oSizeOfAll;
    UpdateUserMacro;
  End;
End;

Function LocalDownLoad: Boolean;

Procedure While1;
Begin
  ComWrite (lang (laCopyFile), eoCodes + eoMacro);
  ComWrite (Trim (NiceFileName (xFile, 30)), 0);
  ComWrite (lang (laCopyTo), eoCodes+eoMacro);
  ComWrite (S + '...', 0);

  If Not tCopyFile (xFile, AddBackSlash (S) + JustFileName (xFile), True) Then
  Begin
    ComWriteLn (lang (laCopyError), eoMacro + eoCodes);
    LogFile ({$IFNDEF WIN32} nil, {$ENDIF} lfTransmitFail);
  End Else
  Begin
    ComWriteLn (lang (laCopyOk), eoMacro + eoCodes);
    LogFile ({$IFNDEF WIN32} nil, {$ENDIF} lfTransmitOk);
  End;

  FileName := '';
{$IFDEF WIN32}
  MainForm. Console1. Paint;
{$ENDIF}
End;

Begin
  SetTitle ('local mode download');
  LocalDownLoad := False;
  S := '';
  ComWriteLn ('|' + lang (laLocalDownLoad), eoMacro + eoCodes);
  ComWrite (lang (laLocalDlDir), eoMacro + eoCodes);
  ComReadLn (S, 80, ofAllowEmpty);
  S := Trim (UpString (S));
{$IFDEF WIN32}
  If Application. Terminated Then Exit;
{$ENDIF}
  If S = '' Then
  Begin
    ShutDown;
    Exit;
  End;

  If Not DirExists (S) Then
  Begin
    Message (lang (laLdDirNotFound));
    ShutDown;
    Exit;
  End;

  If Not AutoDL Then ExportDescs;
  xFile := FileName;

  While (FileName <> '') Or Next2Transfer ({$IFNDEF WIN32} nil, {$ENDIF} xFile) Do
  Begin
    While1;
  End;

  LocalDownLoad := True;
End;

Procedure LocalUpLoad;

Procedure While1;
Begin
  B := FileExists (AddBackSlash (ULDir) + JustFileName (SRec. Name));

  If Not B Or (B and Cnf. ULOverWrite) Then
  Begin
    ComWrite (lang (laCopyFile), eoMacro+eoCodes);
    ComWrite (Trim (NiceFileName (SRec. Name, 30)) + '...', 0);

    xFile := AddBackSlash (ULDir) + JustFileName (SRec. Name);

    If Not tCopyFile (sRec. Name, xFile, True) Then
    Begin
      ComWriteLn (lang (laCopyError), eoMacro + eoCodes);
      LogFile ({$IFNDEF WIN32} nil, {$ENDIF} lfReceiveFail);
    End Else
    Begin
      ComWriteLn (lang (laCopyOk), eoMacro + eoCodes);
      LogFile ({$IFNDEF WIN32} nil, {$ENDIF} lfReceiveOk);
    End;
  End Else
    Message (lang (laFileName) + JustFileName (SRec. Name) + lang (laAlreadyExists));

  mFindNext (SRec);
End;

Begin
  SetTitle ('local mode upload');
  ComWriteLn ('|' + lang (laLocalUpLoad), eoMacro + eoCodes);
  ComWrite (lang (laLocalUlFileName), eoMacro + eoCodes);
  S := ''; ComReadLn (S, 80, ofAllowEmpty); S := Trim (S);

{$IFDEF WIN32}
  If Application. Terminated Then Exit;
{$ENDIF}
  If S = '' Then Exit;

  If Not FileExists (S) Then
  Begin
    Message (lang (laLuFileNotFound));
    Exit;
  End;

  mFindFirst (JustPathName (S), JustFileName (S), AnyFile{$IFNDEF OS2}-VolumeID{$ENDIF}-Hidden-Directory, SRec);

  While {$IFNDEF WIN32} DOS. {$ENDIF} DOSerror = 0 Do
  Begin
    While1;
  End;

  mFindDone (SRec);
End;

Begin
  TransferTime := 0;
  TransferSize := 0;
  trMode := TransferMode;
  trState := State;
  Index2Transfer := 1;
  oFileArea := R. FileArea;
  lfName := FileName;
  lfState := State;

  If UpFiles <> Nil Then UpFiles^. DeleteAll;
  If Not Local Then SetProtocol (R. Protocol);

  If State <> tsUploadMsg Then
  If State <> tsPrivate Then
  Begin
    If trMode = Receive Then
    If (FileArea. UL_Security > R. Security) Or
       (Not FlagsValid (R. Flags, FileArea. UL_Flags)) Then
    Begin
      Message ('|' + lang (laSecurityLow));
      Exit;
    End;
  End Else
  Begin
    UpLoadToUser := FileName;
    If UpLoadToUser = '' Then

    While True Do
    Begin
      If Cnf. CapitalizeNames
      Then SetInputCap (Proper, LettersOnly)
      Else SetInputCap (NoCaps, LettersOnly);

      UpLoadToUser := '';
      ComWrite (lang (laULforuser), eoMacro + eoCodes);
      ComReadLn (UpLoadToUser, 36, ofAllowEmpty + ofFramed);
    {$IFDEF WIN32}
      If Application. Terminated Then Exit;
    {$ENDIF}

      If Trim (UpLoadToUser) = '' Then
      Begin
        SetInputCap (NoCaps, AllChars);
        Exit;
      End;

      If Not Is_User (UploadToUser, Cnf. Aliases)
      Then
        ComWriteLn ('|' + lang (laUser) + UpLoadToUser +
        lang (laUserNotFound) + '|', ofAllowEmpty + ofFramed)
      Else
      Begin
        GetUser (UpLoadToUser, tR, Cnf. Aliases);
        UpLoadToUser := tR. Name;
        Break;
      End;
    End;

  End;

  SetInputCap (NoCaps, AllChars);

  If trMode = Transmit Then
  Begin
    If FileName <> '' Then
    Begin
      oSizeOfAll := SizeOfAll;
      SizeOfAll := gFileSize (FileName);
    End;

    If Not AutoDL Then ShowTransferHeader;
  End;

  If TransferMode = Receive Then
  Begin
    If State = tsPrivate Then ULDir := Cnf. PrivUploadsDir Else
    If FileName = '' Then ULDir := FileArea. ULPath Else ULDir := FileName;
    If AutoDL Then GetDir (0, ULDir)
    Else If Not CheckFreeSpace Then Exit;
  End Else
  If Not AutoDL And Not Local Then
  Begin
    { CopyLocal processing ... }
    DirCreated := False;

    For i := 0 To F2Transfer^. Count-1 Do
    Begin
      With PTagFileRec (F2Transfer ^. At (i))^ Do
      Begin
        If R. FileGroup <> GroupNum Then SetFileGroup (GroupNum);
        If R. FileArea <> AreaNum Then SetFileArea (AreaNum);
      End;

      If Not FileArea. CopyLocal Then Continue;
      S1 := PTagFileRec (F2Transfer^. At (i))^. PathName;

      If R. Protocol in ['1'..'8'] Then
      Begin
        Repeat
          S := Cnf. DoorInfoDir + TempFName + 'cl.tor'
        Until Not FileExists (S);

        Reps^. Insert (NewStr (PTagFileRec (F2Transfer^. At (i))^. PathName + #255 + S));
      End Else
      Begin
        If Not DirCreated Then
        Begin
          Repeat
            CLDirName := Cnf. DoorInfoDir + TempFName
          Until Not DirExists (CLDirName);
          MkDir (CLDirName);
          DirCreated := (IOResult = 0);
        End;

        S := AddBackSlash (CLDirName) + JustFileName (S1);
        { PTagFileRec (F2Transfer^. At (i))^. PathName := S; }
        Reps^. Insert (NewStr (S));
      End;

      If Not tCopyFile (S1, S, True) Then LogWrite ('!', 'Ooops... CopyLocal error happened.') Else
      Begin
      {$IFNDEF WIN32}
        Assign (F, S);
        SetFAttr (F, Archive);
      {$ELSE}
        FileSetAttr (S, faArchive);
      {$ENDIF}
      End;
    End;
  End;

  If Local Then
  Case TransferMode Of
    Transmit : If Not LocalDownLoad Then Exit;
    Receive  : LocalUpLoad;
  End Else
  Begin
    ComWriteLn (#10, 0);
    SmartLine; SmartLine;

    If TransferMode = Transmit Then
    Begin

      LogWrite ('&', 'DownLoad. Using ' + ProtocolDef. Name + ' protocol');
      LogWrite ('&', Long2Str (F2Transfer^. Count) + ' files selected, total size: ' + NiceFileSize (SizeOfAll));

      LogWrite ('&', 'Estimated transfer time : ' +
      Long2Str (Round (EstimatedTransferTime (SizeOfAll, R. AvgCPS, GetConnectSpeed)/60)) + ' min. at ' +
      Long2Str (GetConnectSpeed) + ' bps, average cps: ' +
      Long2Str (R. AvgCPS));

      ExportDescs;
      ComWriteLn (lang (laSendFile), eoMacro+eoCodes);
    End Else
      ComWriteLn (lang (laRecvFile), eoMacro+eoCodes);

    NeedThreadClose := True;
    Repeat Until Not ThreadLocked;
    xFile := '';

    If ((Str2Long (R. Protocol)-1 in [Xmodem, XmodemCRC, Xmodem1K, Xmodem1KG]) Or
       ((Not ProtocolDef. Batch) And Not (R. Protocol in ['1'..'8']))) And
       (TransferMode = Receive) Then
    Begin
      xFile := 'com1';

      While isDevice (xFile) Do
        xFile := FileArea. ULPath + JustFileName (GetAnswer (
        lang (laAskFileName), 13, 0, ''));

      If xFile = '' Then Exit;
    End;

    If Not (R. Protocol in ['1'..'8']) Then {external protocol}
    Begin
      tDeleteFile (ProtocolDef. Log);

      If TransferMode = Transmit Then
      Begin
        KWord := ProtocolDef. DLKeyWord;

        {generating list file}

        Assign (List, ProtocolDef. List);
        ReWrite (List);

        For i := 0 To F2Transfer^. Count-1 Do
        If Not (R. Protocol in ['1'..'8']) Then
        Begin
          With PTagFileRec (F2Transfer ^. At (i))^ Do
          Begin
            If R. FileGroup <> GroupNum Then SetFileGroup (GroupNum);
            If R. FileArea <> AreaNum Then SetFileArea (AreaNum);
          End;

          If Not FileArea. CopyLocal
          Then WriteLn (List, PTagFileRec (F2Transfer^. At (i))^. PathName)
          Else WriteLn (List, AddBackSlash (CLDirName) + JustFileName (
               PTagFileRec (F2Transfer^. At (i))^. PathName));

        End Else
          WriteLn (List, PTagFileRec (F2Transfer^. At (i))^. PathName);

        Close (List);

        GetDir (0, S1);
        SmartChDir (Copy (Cnf. DoorInfoDir, 1, Length (Cnf. DoorInfoDir)-1));
        DosShell (TranslateExecParams (ProtocolDef. DLCommand), exCommand, False);
        tDeleteFile (ProtocolDef. List);
        SmartChDir (S1);
      End Else
      Begin
        KWord := ProtocolDef. ULKeyWord;
        If xFile = '' Then xFile := ULDir;
        GetDir (0, S1);
        SmartChDir (Copy (Cnf. DoorInfoDir, 1, Length (Cnf. DoorInfoDir)-1));
        DosShell (TranslateExecParams (PlaceSubStr (ProtocolDef.
        ULCommand, '*U', xFile)), exCommand, False);
        SmartChDir (S1);
      End;

      If IOResult <> 0 Then;

      Assign (List, ProtocolDef. Log);
      ReSet (List);

      If IOResult = 0 Then
      Begin
        LogWrite ('&', 'Reading protocol log file ' + ProtocolDef. Log);

        While Not EoF (List) Do
        Begin
          ReadLn (List, S);
          FName := '';

          If Pos (KWord, S) <> 0 Then
          Begin
            FName := UpString (Trim (ExtractWord (ProtocolDef. WordOffs+1, Copy
                              (S, Pos (KWord, S)+Length (KWord), 255),
                              [' ', ','])));

            LogWrite ('&', 'File name is: ' + FName);

            If TransferMode = Receive Then
            Begin
              LogWrite ('*', '!R: ' + sm (smFile) + Trim (NiceFileName (FName, 40)));
              UpLoadOk (FName, gFileSize (FName), 0);
            End Else
            Begin
              If FName = JustFileName (FName) Then
              Begin
                mFindFirst (FileArea. DLPath, FName, AnyFile-Directory, SRec);
                If {$IFNDEF WIN32} DOS.  {$ENDIF} DOSerror = 0 Then FName := UpString (SRec. Name);
                mFindDone (SRec);
              End;

              For i := 0 To F2Transfer^. Count-1 Do
              If UpString (PTagFileRec (F2Transfer^. At (i))^. PathName) = FName Then
              Begin
                LogWrite ('*', '!S: ' + sm (smFile) + Trim (NiceFileName (FName, 40)));
                DownLoadOk (gFileSize (FName), i);
              End;
            End;
          End Else
            LogWrite ('&', 'Line "' + S + '" ignored');
        End;
        Close (List);
      End Else
        LogWrite ('!', sm (smFile) + ProtocolDef. Log + sm (smNotFound));

    End Else
    Begin
    {$IFNDEF WIN32}
      SetErrorProc (P, Errorz);
      Case Str2Long (R. Protocol)-1 Of
        Xmodem    : InitXmodem (Prot, P, False, False);
        XmodemCRC : InitCustomXmodem (Prot, P, True, True, 0);
        Xmodem1K  : InitXmodem (Prot, P, True, False);
        Xmodem1KG : InitXmodem (Prot, P, True, True);
        Ymodem    : InitYmodem (Prot, P, True, False);
        YmodemG   : InitYmodem (Prot, P, True, True);
        Zmodem    : InitCustomZmodem (Prot, P, DefProtocolOptions);
        Zmodem8K  : InitCustomZmodem (Prot, P, apZmodem8K);
      End;
    {$ELSE}
      Prot. ProtocolType := ProtN2T [R. Protocol];
    {$ENDIF}

    {$IFNDEF WIN32}
      If GetAsyncStatus <> ecOk Then
      Begin
        ProtocolMsg (sm (smErrorProtInit));
        Exit;
      End;
    {$ENDIF}

      GetDir (0, CurDir);

      If TransferMode = Receive Then
      If AutoDL Then SetDestinationDirectory (Prot, CurDir) Else
      If FileName = '' Then SetDestinationDirectory (Prot, FileArea. ULPath)
      Else SetDestinationDirectory (Prot, FileName);

      If State = tsPrivate Then
      SetDestinationDirectory (Prot, Cnf. PrivUploadsDir);

    {$IFNDEF WIN32}
      InitProtBox (Prot, TransferMode);
      Prot^. PData^. StatusInterval := 110;
      SetShowStatusProc (Prot, StatusP);
      SetBackgroundProc (Prot, BackGroundProc);
      SetLogFileProc (Prot, LogFile);
      SetOverwriteOption (Prot, WriteRename);
      SetAcceptFileFunc (Prot, AcceptFile);
      SetBlockWait (Prot, RelaxedBlockWait);
    {$ELSE}
      MainForm. ProtBox. Caption := ' ' + ULorDL [trMode] + ' ';
    {$ENDIF}

      HiddenCursor;

      If FileName <> '' Then SetFileMask (Prot, FileName) Else
      If (FileName = '') and (F2Transfer^. Count > 0) and (Not
         {$IFNDEF WIN32} Prot^. PData^. BatchProtocol
         {$ELSE} Prot. Batch {$ENDIF}) Then
      SetFileMask (Prot, PTagFileRec (F2Transfer^. At (0))^. PathName);

    {$IFNDEF WIN32}
      If Prot^. PData^. BatchProtocol and (FileName = '') Then
        SetNextFileFunc (Prot, Next2Transfer);
    {$ENDIF}

      If Str2Long (R. Protocol)-1 = ZModem Then SetRecoverOption (Prot, True);
      If Str2Long (R. Protocol)-1 = ZModem8K Then SetRecoverOption (Prot, True);

      {$IFNDEF WIN32}
      SB := StatusBar;
      If Not SB Then SwitchStatusBar;
      {$ENDIF}
      If TransferMode = Receive Then
      Begin
        SetTitle ('uploading files(s)');
        Case Str2Long (R. Protocol)-1 Of
          Zmodem, ZModem8K: Begin
                  {$IFNDEF WIN32}
                    ProtocolReceiveZM (Prot);
                    DoneZmodem (Prot);
                  {$ENDIF}
                  End;

          Xmodem, XmodemCRC, Xmodem1K, Xmodem1KG:
                  Begin
                  {$IFNDEF WIN32}
                    SetReceiveFileName (Prot, xFile);
                    ProtocolReceiveXM (Prot);
                    DoneXmodem (Prot);
                  {$ELSE}
                    Prot. FileName := xFile;
                  {$ENDIF}
                  End;

          Ymodem, YmodemG:
                  Begin
                  {$IFNDEF WIN32}
                    ProtocolReceiveYM (Prot);
                    DoneYmodem (Prot);
                  {$ENDIF}
                  End;
        End;
      {$IFDEF WIN32}
        Prot. StartReceive;
      {$ENDIF}
      End Else
      If TransferMode = Transmit Then
      Begin
    {$IFNDEF WIN32}
        Case Str2Long (R. Protocol)-1 Of
          Zmodem, Zmodem8K:
              Begin
                ProtocolTransmitZM (Prot);
                DoneZmodem (Prot);
              End;
          Xmodem, XmodemCRC, Xmodem1K, Xmodem1KG:
              Begin
                ProtocolTransmitXM (Prot);
                DoneXmodem (Prot);
              End;
          Ymodem, YmodemG:
              Begin
                ProtocolTransmitYM (Prot);
                DoneYmodem (Prot);
              End;
        End;
    {$ELSE}
        Prot. StartTransmit;
    {$ENDIF}
      End;

    {$IFNDEF WIN32}
      If AsyncStatus <> ecOk Then ProtocolMsg (sm (smTransFail));
      DoneProtBox;
    {$ELSE}

      Repeat
        Application. ProcessMessages;
      Until Not Prot. InProgress Or Application. Terminated;

      If Application. Terminated Then Exit;

    {$ENDIF}
    {$IFNDEF WIN32}
    If Not SB Then SwitchStatusBar;
    {$ENDIF}
    End;
  End;

  ClearSent;
  S := Cnf. DoorInfoDir + 'file_id.1';
  tDeleteFile (S);
  R. FileArea := oFileArea;
  If TransferMode = Transmit Then SetFileArea (0);
  NormalCursor;
  NeedThreadClose := False;

  If (TransferMode = Receive) And (trState in
     [tsNormal, tsPrivate]) And Not AutoDL
  Then UploadDone;

  ShowStatusBar;

  If TransferMode = Receive Then
  Begin
    ExecScript ('upload');
    ExecRexx ('upload');
  End Else
  Begin
    ExecScript ('download');
    ExecRexx ('download');
  End;

  ShutDown;
End;

Procedure DownLoad;
Var
  Tmp, AddStr     : PathStr;
  TmpC            : Char;
  DirInfo         : mSearchRec;
  j               : Byte;
  Found, FreeFile : Boolean;
  FileRec         : TTagFileRec;

Label
  1, 3;

Procedure ShowList;
Var
  i, saveArea, saveGroup : Word;
  T, W, k, l             : LongInt;

Begin
  Cls;
  If F2Transfer^. Count = 0 Then
  Begin
    ComWriteLn (lang (laNoFilesSelected), eoMacro + eoCodes);
    Exit;
  End;

  k := 0;
  l := 0;
  InitMore (0);
  saveArea := R. FileArea;
  saveGroup := R. FileGroup;

  For i := 0 To F2Transfer^. Count - 1 Do
  With PTagFileRec (F2Transfer^. At (i))^ Do
  Begin
    If (AreaNum <> k) Or (GroupNum <> l) Then
    Begin
      T := AreaNum;
      W := GroupNum;

      Case T Of
        -10 : ComWriteLn (#13#10'     ' + EmuColor (Cnf. ColorScheme [umItem]) +
              lang (laPrevTagArea) + #13#10, eoMacro + eoCodes);
        -20 : ComWriteLn (#13#10'     ' + EmuColor (Cnf. ColorScheme [umItem]) +
              lang (laFromUsersArea) + #13#10, eoMacro + eoCodes);
      Else
        Begin
          SetFileGroup (W);
          SetFileArea (T);

          ComWriteLn (#13#10 + EmuColor (Cnf. ColorScheme [umNumber]) +
                      LeftPadCh (Long2Str (T), ' ', 3) +
                      EmuColor (Cnf. ColorScheme [umDot]) + '. ' +
                      EmuColor (Cnf. ColorScheme [umItem]) +
                      FileArea. Name + #13#10, eoMacro + eoCodes);
        End;
      End;

      Inc (MoreLines, 3);
    End;

    ComWrite (EmuColor (Cnf. ColorScheme [flTagNum]) + LeftPadCh
             (Long2Str (I + 1), '0', 3) + ' ' + EmuColor
             (Cnf. ColorScheme [flFileName]) + PadCh (UpString
             (JustFileName (PTagFileRec (F2Transfer^. At (i))^. PathName)), ' ', 12) + '  ' + PadCh
             (NiceFileSize (PTagFileRec (F2Transfer^. At (i))^. Size), ' ', 14), eoMacro + eoCodes);

    If PTagFileRec (F2Transfer^. At (i))^. Free
    Then ComWrite (lang (laFreeDL), eoMacro + eoCodes);

    If T = -20 Then
    ComWrite (lang (laFileFromUser) + PTagFileRec (F2Transfer^. At (i))^. FromName, eoMacro + eoCodes);

    ComWriteLn ('', 0);

    k := AreaNum;
    l := GroupNum;

    If Not More Then Break;
  End;

  SetFileGroup (saveGroup);
  SetFileArea (saveArea);
End;

Procedure DeleteFromList;
Var
  i, j     : Byte;
  Token    : String [5];

Procedure Del (FNum: Integer);
Begin
  If (FNum > F2Transfer^. Count) Or (FNum <= 0)
  Then Exit;

  Dec (FNum);
  Dec (SizeOfAll, PTagFileRec (F2Transfer^. At (FNum))^. Size);

  With PTagFileRec (F2Transfer^. At (FNum))^ Do
  If (FromName <> '') And
     (AddBackSlash (UpString (JustPathName (PathName))) = AddBackSlash (UpString (Cnf. PrivUploadsDir)))
  Then tDeleteFile (PathName);

  PTagFileRec (F2Transfer^. At (FNum))^. PathName := '!';
End;

Begin
  ComWrite (lang (laDelFileNo), eoMacro + eoCodes);
  Tmp := '';
  ComReadLn (Tmp, 50, ofAllowEmpty);
{$IFDEF WIN32}
  If Application. Terminated Then Exit;
{$ENDIF}
  If Trim (Tmp) = '' Then Exit;

  For i := 1 To WordCount (Tmp, [' ', ',']) Do
  Begin
    Token := ExtractWord (i, Tmp, [' ', ',']);
    If ConsistsOf (Token, ['0'..'9', '-']) Then
    If Pos ('-', Tmp) <> 0 Then
    Begin
      For j := Str2Long (ExtractWord (1, Token, ['-']))
          To   Str2Long (ExtractWord (2, Token, ['-'])) Do
      Del (j);
    End Else
      Del (Str2Long (ExtractWord (i, Tmp, [' ', ','])));
  End;

  ClearSent;
End;

Var
  i   : Word;

Begin
  ShowList;
  If F2Transfer^. Count <> 0 Then Goto 3;

  1:
  If (FileArea. DL_Security > R. Security) Or
     (Not FlagsValid (R. Flags, FileArea. DL_Flags)) Then
  Begin
    ComWriteLn (lang (laSecurityLow), eoMacro + eoCodes);
    GoTo 3;
  End;

  If (FileArea. MinSpeed > GetConnectSpeed) and (Not Local) Then
  Begin
    Message (#13 + Replicate (' ', 79) + #13 + lang (laTooSlow));
    GoTo 3;
  End;

  ComWrite (lang (laAddFile), eoMacro + eoCodes);
  AddStr := '';
  ComReadLn (AddStr, 80, ofAllowEmpty);
  AddStr := UpString (AddStr);
{$IFDEF WIN32}
  If Application. Terminated Then Exit;
{$ENDIF}
  If Trim (addstr) = '' Then Goto 3;
  Found := False;

  For j := 1 To WordCount (addstr, [' ',',']) Do
  Begin
    tmp := ExtractWord (j, addstr, [' ', ',']);
    If MultiFileExists (FileArea. DLPath, tmp) Then
    Begin
      Found := True;
      Break;
    End;
  End;

  If Not Found Then
  Begin
    ComWriteLn (lang (laAddFileNotFound), eoMacro + eoCodes);
    GoTo 1;
  End;

  For j := 1 To WordCount (addstr, [' ', ',']) Do
  Begin
    tmp := ExtractWord (j, addstr, [' ', ',']);
    mFindFirst (FileArea. DLPath, tmp, AnyFile{$IFNDEF OS2}-VolumeID{$ENDIF}-Directory, DirInfo);

    While {$IFNDEF WIN32} DOS {$ELSE} OpCrt {$ENDIF}. DosError = 0 Do
    Begin
      If InFilesBBS (JustName (UpString (DirInfo. Name)) + '.' +
         JustExtension (UpString (DirInfo. Name))) Then
      Begin
        OutFilesBBSString (JustName (UpString (DirInfo. Name)) + '.' +
                           JustExtension (UpString (DirInfo. Name)));
        TmpC := Query_YNQ (lang (laSelectFile));
      {$IFDEF WIN32}
        If Application. Terminated Then Exit;
      {$ENDIF}

        FreeFile := IsFree (UpString (DirInfo. Info. Name));

        If (TmpC = 'y') and (Not InTagList (FileArea. DLPath + UpString
           (DirInfo. Name))) Then

          If (R. TodayK + Trunc (DirInfo. Info. Size/1024) + Trunc (SizeOfAll/1024) < R. DailySize) Or FreeFile
            Or (Cnf. DontUseDLLimitWhenTagging) Then
          Begin
            If (R. TodayK + Trunc (DirInfo. Info. Size/1024 + SizeOfAll/1024) <
               Round ((GetConnectSpeed/10 * (R. TotalTime - MidSec +
               EnterTime))/1024)) Or Local Or FreeFile Or (Cnf. DontUseTimeLimitWhenTagging) Then
            Begin
              FileRec. PathName := AddBackSlash (ExtractWord (DirInfo. mfMaskNum,
                                   FileArea. DLPath, [' ', ','])) + UpString
                                   (DirInfo. Info. Name);
              FileRec. AreaNum  := R. FileArea;
              FileRec. GroupNum := R. FileGroup;
              FileRec. Size     := DirInfo. Info. Size;
              FileRec. Free     := FreeFile;
              FileRec. FromName := '';

              If Not InTagList (FileRec. PathName) Then
              Begin
                F2Transfer^. Insert (NewTagFile (FileRec));
                Inc (SizeOfAll, DirInfo. Info. Size);
              End;

            End Else Message (lang (laDLTimeLimit));
          End Else Message (lang (laDLLimitExceed));

        If TmpC = 'q' Then Break;
      End;
      mFindNext (DirInfo);
    End;
    {$IFDEF OS2}
    mFindDone (DirInfo);
    {$ENDIF}
  End;

  3:
  i := MenuBar (#13#10 + lang (laFAreaLine), lang (laFAreaKeys)+#13);
{$IFDEF WIN32}
  If Application. Terminated Then Exit;
{$ENDIF}
  Case i Of
    1  : Begin
           ComWriteLn ('', 0);
           GoTo 1;
         End;
    2  : Begin
           ShowList;
           ComWriteLn ('', 0);
           DeleteFromList;
           GoTo 3;
         End;
    3  : Exit;
    4  : Begin
           ShowList;
           GoTo 3;
         End;
    5  : Begin
           If (Not R. Frames) or (EmuGoToXY (1, 1) = '') Then ComWriteLn ('', 0);

           If (Query (lang (laClearList), False, ofFramed)) And
              (F2Transfer^. Count > 0) Then
           Begin
             For i := 0 To F2Transfer^. Count-1 Do
             With PTagFileRec (F2Transfer^. At (i))^ Do
             If (FromName <> '') And (AddBackSlash (UpString (JustPathName
                (PathName))) = AddBackSlash (UpString (Cnf. PrivUploadsDir)))
             Then tDeleteFile (PathName);

             F2Transfer^. DeleteAll;
             SizeOfAll := 0;
           End;

           ShowList;
           Goto 3;
         End;

    6  : Begin
           If (F2Transfer^. Count = 0) and (SizeOfAll > 0) Then i := 1 Else i := F2Transfer^. Count;

           If Local Then
           Begin
             LogWrite ('&', Long2Str (F2Transfer^. Count) + ' files selected, total size: ' + NiceFileSize (SizeOfAll));
             LogWrite ('&', 'Download mode: LOCAL');
           End;

           If i > 0 Then
             Transfer ('', Transmit, tsNormal)
           Else
             Message (#13#10 + lang (laNoFilesSelected));
         End;

  End;
End;

{$IFNDEF WIN32}
Procedure Errorz;
Begin
  If ProtocolInProgress (P) Then Exit;
  If StatusCode mod 10000 <> 0 Then ProtocolMsg (Status2Str (StatusCode));
End;
{$ENDIF}

End.