home *** CD-ROM | disk | FTP | other *** search
/ Delphi 2 - Developers' Solutions / Delphi 2 Developers' Solutions.iso / dds / chap10 / howto07 / delphi10 / ccicnntp.pas < prev    next >
Encoding:
Pascal/Delphi Source File  |  1996-06-12  |  44.2 KB  |  1,232 lines

  1. unit Ccicnntp;
  2.  
  3. interface
  4.  
  5. uses
  6.   SysUtils, WinTypes, WinProcs, Messages, Classes, Graphics, Controls,
  7.   Forms, Dialogs, ExtCtrls, StdCtrls, Buttons, Menus, FileCtrl, CCWSock, CCICCInf,
  8.   CCICCPrf, IniFiles, Gauges , CCUUCode, CCiccfrm;
  9. type
  10.   { Component to hold NNTP handling capabilities }
  11.   TNNTPComponent = class( TWinControl )
  12.   public
  13.     NNTPCommandInProgress ,
  14.     Connection_Established : Boolean;
  15.     Socket1 : TCCSocket;
  16.     constructor Create( AOwner : TComponent ); override;
  17.     destructor Destroy; override;
  18.     function EstablishConnection( PCRPointer : PConnectionsRecord ) : Boolean;
  19.     function Disconnect : Boolean;
  20.     function DoCStyleFormat(       TheText      : string;
  21.                              const TheArguments : array of const ) : String;
  22.     procedure UpdateGauge( BytesFinished , TotalToHandle : longint );
  23.     procedure AddProgressText( WhatText : String );
  24.     procedure ShowProgressText( WhatText : String );
  25.     procedure ShowProgressErrorText( WhatText : String );
  26.     function GetNNTPServerResponse( var ResponseString : String ) : integer;
  27.     procedure NNTPSocketsErrorOccurred( Sender     : TObject;
  28.                                      ErrorCode  : Integer;
  29.                                      TheMessage : String   );
  30.     function PerformNNTPCommand(
  31.                     TheCommand   : string;
  32.               const TheArguments : array of const ) : Integer;
  33.     function PerformBlindNNTPCommand( TheCommand   : string ) : Integer;
  34.     function PerformNNTPExtendedCommand(
  35.                     TheCommand   : string;
  36.               const TheArguments : array of const ) : Integer;
  37.     function GetNNTPServerExtendedResponse( ResponseString : PChar ) : integer;
  38.     function GetNextSDItem(     WorkingString : String;
  39.                             var TheIndex      : Integer ) : String;
  40.     function GetListOfAvailableNewsGroups : Boolean;
  41.     function PurgeReadSentArticleListings( TheNGRecord : PNewsGroupRecord ): Boolean;
  42.     procedure ParseNewsGroupListing(     TheListing : String;
  43.                                      var GroupName  : String;
  44.                                      var LowCurrent : Longint;
  45.                                      var HighCurrent : Longint;
  46.                                      var Postable    : Boolean  );
  47.     function SetCurrentNewsGroup( TheNGRecord : PNewsGroupRecord;
  48.                                   DoUpdate    : Boolean           ) : Boolean;
  49.     function CheckForNewNews( TheNGRecord : PNewsGroupRecord ) : Boolean;
  50.     function CheckAllNewNews : Boolean;
  51.     procedure ParseArticleListing(     TheListing       : String;
  52.                                    var TotalAvailable   : Longint;
  53.                                    var LowestAvailable  : Longint;
  54.                                    var HighestAvailable : Longint );
  55.     function GetArticleHeader( TheNumber     : Longint;
  56.                                TheReturnList : TStringList ) : Boolean;
  57.     function GetAllArticleHeaders( TheNGRecord : PNewsGroupRecord ) : Boolean;
  58.     function DownloadArticleListing( TheNumber : Longint;
  59.                                      TheFileName : String ) : Boolean;
  60.     function DownloadAllArticleListings( TheNGRecord : PNewsGroupRecord ) : Boolean;
  61.     function GetHeaderSubject( HList : TStringList ) : String;
  62.     function GetHeaderSender( HList : TStringList ) : String;
  63.     function DownloadAllMarkedArticleListings( TheNGRecord : PNewsGroupRecord;
  64.                                                TheListbox  : TListbox          ) : Boolean;
  65.   end;
  66.  
  67. implementation
  68.  
  69. { This function calls an extended response NNTP command routine }
  70. function TNNTPComponent.PerformNNTPExtendedCommand(
  71.                TheCommand   : string;
  72.          const TheArguments : array of const ) : Integer;
  73. var TheBuffer : string; { Text buffer }
  74. begin
  75.   { If command in progress send back -1 error }
  76.   if NNTPCommandInProgress then
  77.   begin
  78.     Result := -1;
  79.     exit;
  80.   end;
  81.   { Set status variable }
  82.   NNTPCommandInProgress := True;
  83.   { Set global error code }
  84.   GlobalErrorCode := 0;
  85.   { Format output string }
  86.   TheBuffer := Format( TheCommand , TheArguments );
  87.   { Preset failure code }
  88.   Result := TCPIP_STATUS_FATAL_ERROR;
  89.   { If invalid socket or no connection abort }
  90.   if ( Socket1.TheSocket = INVALID_SOCKET ) or not Connection_Established then
  91.    exit;
  92.   { Send the buffer plus EOL chars }
  93.   Socket1.StringData := TheBuffer + #13#10;
  94.   { if abort due to timeout or other error exit }
  95.   if (( GlobalAbortedFlag ) or ( GlobalErrorCode <> 0 )) then exit;
  96.   { Otherwise return preliminary code }
  97.   Result := TCPIP_STATUS_PRELIMINARY;
  98. end;
  99.  
  100. { This function gets an extended period-ended multiline response from the server }
  101. function TNNTPComponent.GetNNTPServerExtendedResponse( ResponseString : PChar ) : integer;
  102. var
  103.   { Assume ResponseString already allocated as 0..513 }
  104.   { Pointer to the response string }
  105.   TheBuffer ,
  106.   BufferPointer : array[0..255] of char;
  107.   HolderBuffer : array[0..513] of char;
  108.   { Character to check for response code }
  109.   ResponseChar   : char;
  110.   { Pointers into returned string }
  111.   TheIndex ,
  112.   TheLength     : integer;
  113.   { Control variable }
  114.   LeftoversInPan ,
  115.   Finished      : Boolean;
  116.   BufferString : String;
  117. begin
  118.   { Preset fatal error }
  119.   Result := TCPIP_STATUS_FATAL_ERROR;
  120.   { Start loop control }
  121.   LeftoversInPan := false;
  122.   Finished := false;
  123.   StrCopy( HolderBuffer , '' );
  124.   repeat
  125.     { Do a peek }
  126.     BufferString := Socket1.PeekData;
  127.     { If timeout or other error exit }
  128.     if (( GlobalAbortedFlag ) or ( GlobalErrorCode <> 0 )) then exit;
  129.     { Find end of line character }
  130.     TheIndex := Pos( #10 , BufferString );
  131.     if TheIndex = 0 then
  132.     begin
  133.       TheIndex := Pos( #13 , BufferString );
  134.       if TheIndex = 0 then
  135.       begin
  136.         TheIndex := Pos( #0 , BufferString );
  137.         if TheIndex = 0 then
  138.         begin
  139.           TheIndex := Length( BufferString );
  140.           LeftoversInPan := True;
  141.           StrPCopy( TheBuffer , BufferString );
  142.           StrCat( HolderBuffer , TheBuffer );
  143.           LeftoversOnTable := false;
  144.         end;
  145.       end;
  146.     end;
  147.     { If an end of line then process the line }
  148.     if TheIndex > 0 then
  149.     begin
  150.       { Get length of string }
  151.       TheLength := TheIndex;
  152.       { Receive actual data }
  153.       Socket1.CCSockReceive( Socket1.TheSocket   ,
  154.                              @BufferPointer[ 0 ] ,
  155.                              TheLength              );
  156.       { Abort if timeout or error }
  157.       if (( GlobalAbortedFlag ) or ( GlobalErrorCode <> 0 )) then exit;
  158.       { Put in the length byte }
  159.       BufferPointer[ TheLength ] := Chr( 0 );
  160.       if LeftOversOnTable then
  161.       begin
  162.         LeftOversOnTable := false;
  163.         StrCopy( ResponseString , HolderBuffer );
  164.         StrCat( ResponseString , BufferPointer );
  165.       end
  166.       else
  167.       begin
  168.         if not LeftoversInPan then StrCopy( ResponseString , BufferPointer );
  169.       end;
  170.       if LeftoversInPan then
  171.       begin
  172.         LeftoversInPan := false;
  173.         LeftoversOnTable := true;
  174.       end
  175.       else
  176.       begin
  177.         ResponseChar := ResponseString[ 0 ];
  178.         if (( ResponseChar = '.' ) and ( StrLen( ResponseString ) <= 3 )) then
  179.         begin
  180.           Finished := true;
  181.           Result := TCPIP_STATUS_COMPLETED;
  182.         end
  183.         else
  184.         begin
  185.           if ResponseChar = '.' then ResponseString[ 0 ] := ' ';
  186.           Finished := true;
  187.           Result := TCPIP_STATUS_PRELIMINARY;
  188.         end;
  189.       end;
  190.     end;
  191.   until ( Finished and ( not LeftoversOnTable ));
  192.   StrLCopy( ResponseString , ResponseString , StrLen( ResponseString ) - 2 );
  193. end;
  194.  
  195. { This function moves along a string from an index, getting the next }
  196. { string delimited item or last one on string.                       }
  197. function TNNTPComponent.GetNextSDItem(     WorkingString : String;
  198.                                        var TheIndex      : Integer ) : String;
  199. var HoldingString : String;
  200. begin
  201.   HoldingString := Copy( WorkingString , TheIndex + 1 , 255 );
  202.   TheIndex := Pos( ' ' , HoldingString );
  203.   if TheIndex = 0 then
  204.   begin
  205.     Result := HoldingString;
  206.   end
  207.   else
  208.   begin
  209.     HoldingString := Copy( HoldingString , 1 , TheIndex - 1 );
  210.     Result := HoldingString;
  211.   end;
  212. end;
  213.  
  214. { This is the first true "network" function; it sends a LIST command, eats }
  215. { a single 215 response and then grabs PChars of data from the server till }
  216. { It returns a period character. The returned line is sent to a NEWSGRP    }
  217. { file and a status update is send through.                                }
  218. function TNNTPComponent.GetListOfAvailableNewsGroups : Boolean;
  219. var TheReturnString : String;  { Internal string holder }
  220.     TheResult       : Integer; { Internal int holder    }
  221.     HoldPChar ,
  222.     TheHoldingPChar ,
  223.     TheReturnPChar  : PChar;
  224.     TheNGFile       : TextFile;
  225.     D1 , D2     : Longint;
  226.     D3          : Boolean;
  227.     GroupString : String;
  228.     TotalGroups : Longint;
  229. begin
  230.   Result := false;
  231.   TheReturnString :=
  232.    DoCStyleFormat( 'LIST' ,
  233.     [ nil ] );
  234.   { Put result in progress and status line }
  235.   AddProgressText( TheReturnString );
  236.   ShowProgressText( TheReturnString );
  237.   { Begin login sequence with user name }
  238.   TheResult := PerformNNTPCommand( 'LIST', [ nil ] );
  239.   if TheResult <> TCPIP_STATUS_PRELIMINARY then
  240.   begin
  241.     NNTPCommandInProgress := false;
  242.     Result := false;
  243.     exit;
  244.   end;
  245.   repeat
  246.     TheResult := GetNNTPServerResponse( TheReturnString );
  247.     { Put result in progress and status line }
  248.     AddProgressText( TheReturnString );
  249.     ShowProgressText( TheReturnString );
  250.   until (( GlobalAbortedFlag ) or ( TheResult <> TCPIP_STATUS_PRELIMINARY ));
  251.   NNTPCommandInProgress := false;
  252.   if ( GlobalAbortedFlag or ( TheResult <> TCPIP_STATUS_COMPLETED )) then
  253.   begin
  254.     { Do clever C formatting trick }
  255.     TheReturnString :=
  256.      DoCStyleFormat( 'LIST Failed!' ,
  257.       [ nil ] );
  258.     { Put result in progress and status line }
  259.     AddProgressText( TheReturnString );
  260.     ShowProgressErrorText( TheReturnString );
  261.     { Signal error }
  262.     Result := False;
  263.     { leave }
  264.     exit;
  265.   end;
  266.   try
  267.     AssignFile( TheNGFile , NewsPath + '\NEWSGRP.TXT' );
  268.     Rewrite( TheNGFile );
  269.   except
  270.     Socket1.OutOfBand := 'ABOR'+#13#10;
  271.     repeat
  272.       TheResult := GetNNTPServerResponse( TheReturnString );
  273.       { Put result in progress and status line }
  274.       AddProgressText( TheReturnString );
  275.       ShowProgressText( TheReturnString );
  276.     until (( GlobalAbortedFlag ) or ( TheResult <> TCPIP_STATUS_PRELIMINARY ));
  277.     Result := false;
  278.     exit;
  279.   end;
  280.   GetMem( TheReturnPChar , 514 );
  281.   HoldPChar := TheReturnPChar;
  282.   TotalGroups := 0;
  283.   CCICInfoDlg.ListBox1.Clear;
  284.   repeat
  285.     Application.ProcessMessages;
  286.     if GlobalAbortedFlag then exit;
  287.     Inc(TotalGroups );
  288.     TheResult := GetNNTPServerExtendedResponse( TheReturnPChar );
  289.     if StrLen( TheReturnPChar ) > 255 then
  290.     begin
  291.       Getmem( TheHoldingPChar , 255 );
  292.       while StrLen( TheReturnPChar ) > 255 do
  293.       begin
  294.         StrCopy( TheHoldingPChar , '' );
  295.         StrMove( TheHoldingPChar , TheReturnPChar , 255 );
  296.         TheReturnPChar := TheReturnPChar + 256;
  297.         TheReturnString := StrPas( TheHoldingPChar );
  298.         ParseNewsGroupListing( TheReturnString, GroupString, D1 , D2 , D3 );
  299.       end;
  300.       FreeMem( TheHoldingPChar , 255 );
  301.       Writeln( TheNGFile , GroupString );
  302.       CCICInfoDlg.ListBox1.Items.Add( GroupString );
  303.       CCINetCCForm.Panel1.Caption := GroupString +
  304.        '(' + IntToStr( TotalGroups ) + ')';
  305.     end
  306.     else
  307.     begin
  308.       TheReturnString := StrPas( TheReturnPChar );
  309.       ParseNewsGroupListing( TheReturnString, GroupString, D1 , D2 , D3 );
  310.       Writeln( TheNGFile , GroupString );
  311.       CCICInfoDlg.ListBox1.Items.Add( GroupString );
  312.       CCINetCCForm.Panel1.Caption := GroupString +
  313.        '(' + IntToStr( TotalGroups ) + ')';
  314.     end;
  315.   until (( GlobalAbortedFlag ) or ( TheResult = TCPIP_STATUS_COMPLETED ));
  316.   FreeMem( HoldPChar , 514 );
  317.   CloseFile( TheNGFile );
  318.   Result := true;
  319.   CCINetCCForm.Panel1.Caption := 'Finished LIST!';
  320. end;
  321.  
  322. { This method sets a news group and updates its internal data }
  323. function TNNTPComponent.CheckForNewNews( TheNGRecord : PNewsGroupRecord ) : Boolean;
  324. begin
  325.   { Gee, that was easy! }
  326.   Result := SetCurrentNewsGroup( TheNGRecord , true );
  327. end;
  328.  
  329. { This method takes all the data in the NewsRCList and if subscribed, CNN's it }
  330. function TNNTPComponent.CheckAllNewNews : Boolean;
  331. var Counter_1   : Integer;
  332.     TheNGRecord : PNewsGroupRecord;
  333. begin
  334.   Result := true;
  335.   for Counter_1 := 0 to TheNewsRCList.Count - 1 do
  336.   begin
  337.     TheNGRecord := PNewsGroupRecord( TheNewsRCList.Items[ Counter_1 ] );
  338.     if TheNGRecord^.GSubScribed then Result := CheckForNewNews( TheNGRecord );
  339.   end;
  340. end;
  341.  
  342. { This method splits up a listing and pulls out its component data }
  343. procedure TNNTPComponent.ParseNewsGroupListing(     TheListing : String;
  344.                                 var GroupName  : String;
  345.                                 var LowCurrent : Longint;
  346.                                 var HighCurrent : Longint;
  347.                                 var Postable    : Boolean  );
  348. var HoldingString ,
  349.     HoldingString2 : String;
  350.     WorkingIndex  : Integer;
  351. begin
  352.   WorkingIndex := Pos( ' ' , TheListing );
  353.   if WorkingIndex = 0 then
  354.   begin
  355.     GroupName := TheListing;
  356.     LowCurrent :=  -1;
  357.     HighCurrent := -1;
  358.     Postable := false;
  359.     exit;
  360.   end;
  361.   GroupName := Copy( TheListing , 1 , WorkingIndex - 1 );
  362.   HoldingString := Copy( TheListing , WorkingIndex + 1 , 255 );
  363.   WorkingIndex := Pos(  ' ' , HoldingString );
  364.   HoldingString2 := Copy( HoldingString , 1 , WorkingIndex - 1 );
  365.   LowCurrent := StrToInt( HoldingString2 );
  366.   HoldingString := Copy( HoldingString , WorkingIndex + 1 , 255 );
  367.   WorkingIndex := Pos(  ' ' , HoldingString );
  368.   HoldingString2 := Copy( HoldingString , 1 , WorkingIndex - 1 );
  369.   HighCurrent := StrToInt( HoldingString2 );
  370.   HoldingString := Copy( HoldingString , WorkingIndex + 1 , 255 );
  371.   if (( HoldingString[ 1 ] = 'y' ) or ( HoldingString[ 1 ] = 'Y' )) then
  372.    Postable := true else Postable := false;
  373. end;
  374.  
  375. { This is another "Network" command which sets the GROUP to the name of the }
  376. { imported record. The imported record is also updated to reflect current   }
  377. { available articles.                                                       }
  378. function TNNTPComponent.SetCurrentNewsGroup(
  379.           TheNGRecord : PNewsGroupRecord; DoUpdate : Boolean ) : Boolean;
  380. var TheReturnString : String;  { Internal string holder }
  381.     TheResult       : Integer; { Internal int holder    }
  382.     TAA , LAA , HAA : Longint;
  383. begin
  384.   TheReturnString :=
  385.    DoCStyleFormat( 'GROUP %s' ,
  386.     [ TheNGRecord^.GRealName ] );
  387.   { Put result in progress and status line }
  388.   AddProgressText( TheReturnString );
  389.   ShowProgressText( TheReturnString );
  390.   { Begin login sequence with user name }
  391.   TheResult := PerformNNTPCommand( 'GROUP %s',
  392.                                   [ TheNGRecord^.GRealName ] );
  393.   if TheResult <> TCPIP_STATUS_PRELIMINARY then
  394.   begin
  395.     NNTPCommandInProgress := false;
  396.     Result := false;
  397.     exit;
  398.   end;
  399.   repeat
  400.     TheResult := GetNNTPServerResponse( TheReturnString );
  401.     { Put result in progress and status line }
  402.     AddProgressText( TheReturnString );
  403.     ShowProgressText( TheReturnString );
  404.   until (( GlobalAbortedFlag ) or ( TheResult <> TCPIP_STATUS_PRELIMINARY ));
  405.   NNTPCommandInProgress := false;
  406.   if ( GlobalAbortedFlag or ( TheResult <> TCPIP_STATUS_COMPLETED )) then
  407.   begin
  408.     { Do clever C formatting trick }
  409.     TheReturnString :=
  410.      DoCStyleFormat( 'GROUP %s Not Available!' ,
  411.       [ TheNGRecord^.GRealName ] );
  412.     { Put result in progress and status line }
  413.     AddProgressText( TheReturnString );
  414.     ShowProgressErrorText( TheReturnString );
  415.     { Signal error }
  416.     Result := False;
  417.     { leave }
  418.     exit;
  419.   end;
  420.   Result := True;
  421.   { Leave if only want to set group }
  422.   if not DoUpdate then exit;
  423.   { Split out the articles listing into its three numbers }
  424.   ParseArticleListing( TheReturnString , TAA , LAA , HAA );
  425.   { Work on the numbers to make sure display is consistent }
  426.   with TheNGRecord^ do
  427.   begin
  428.     { Set internal pointers }
  429.     GTotalAvailable := TAA;
  430.     GLowestAvailable := LAA;
  431.     GHighestAvailable := HAA;
  432.     if GLowest < GLowestAvailable then
  433.     begin { All stored articles have expired or there are none }
  434.       GTotalNew := GTotalAvailable;      { Total new is total available    }
  435.       GLowest := GLowestAvailable - 1;   { set low and high to below start }
  436.       GHighest := GLowestAvailable - 1; { until something is read }
  437.     end
  438.     else
  439.     begin { Some read articles haven't expired; assume all still good }
  440.       GTotalNew := GHighestAvailable - GHighest; { Total since last download }
  441.       if GTotalNew < 0 then GTotalNew := 0; { Just in case... }
  442.     end;
  443.   end;
  444. end;
  445.  
  446. { This method splits out the GROUP response line into TAA, LAA , HAA }
  447. procedure TNNTPComponent.ParseArticleListing(     TheListing       : String;
  448.                               var TotalAvailable   : Longint;
  449.                               var LowestAvailable  : Longint;
  450.                               var HighestAvailable : Longint );
  451. var WorkingString ,
  452.     WorkingString2 : String;
  453.     WorkingIndex   : Integer;
  454. begin
  455.   WorkingString := Copy( TheListing , 5, 255 );
  456.   WorkingIndex := Pos( ' ' , WorkingString );
  457.   WorkingString2 := Copy( WorkingString , 1 , WorkingIndex - 1 );
  458.   TotalAvailable := StrToInt( WorkingString2 );
  459.   WorkingString := Copy( WorkingString , WorkingIndex + 1 , 255 );
  460.   WorkingIndex := Pos( ' ' , WorkingString );
  461.   WorkingString2 := Copy( WorkingString , 1 , WorkingIndex - 1 );
  462.   LowestAvailable := StrToInt( WorkingString2 );
  463.   WorkingString := Copy( WorkingString , WorkingIndex + 1 , 255 );
  464.   WorkingIndex := Pos( ' ' , WorkingString );
  465.   WorkingString2 := Copy( WorkingString , 1 , WorkingIndex - 1 );
  466.   HighestAvailable := StrToInt( WorkingString2 );
  467. end;
  468.  
  469. { This method uses the HEAD command to get a complete article header }
  470. function TNNTPComponent.GetArticleHeader( TheNumber     : Longint;
  471.                           TheReturnList : TStringList ) : Boolean;
  472. var TheReturnString : String;  { Internal string holder }
  473.     TheResult       : Integer; { Internal int holder    }
  474.     HoldPChar ,
  475.     TheReturnPChar ,
  476.     TheHoldingPChar : PChar;
  477. begin
  478.   TheReturnString :=
  479.    DoCStyleFormat( 'HEAD %d' ,
  480.     [ TheNumber ] );
  481.   { Put result in progress and status line }
  482.   AddProgressText( TheReturnString );
  483.   ShowProgressText( TheReturnString );
  484.   { Begin login sequence with user name }
  485.   TheResult := PerformNNTPCommand( 'HEAD %d', [ TheNumber ] );
  486.   if TheResult <> TCPIP_STATUS_PRELIMINARY then
  487.   begin
  488.     NNTPCommandInProgress := false;
  489.     Result := false;
  490.     exit;
  491.   end;
  492.   repeat
  493.     TheResult := GetNNTPServerResponse( TheReturnString );
  494.     { Put result in progress and status line }
  495.     AddProgressText( TheReturnString );
  496.     ShowProgressText( TheReturnString );
  497.   until (( GlobalAbortedFlag ) or ( TheResult <> TCPIP_STATUS_PRELIMINARY ));
  498.   NNTPCommandInProgress := false;
  499.   if ( GlobalAbortedFlag or ( TheResult <> TCPIP_STATUS_COMPLETED )) then
  500.   begin
  501.     { Do clever C formatting trick }
  502.     TheReturnString :=
  503.      DoCStyleFormat( 'Head %d Failed!' ,
  504.       [ TheNumber ] );
  505.     { Put result in progress and status line }
  506.     AddProgressText( TheReturnString );
  507.     ShowProgressErrorText( TheReturnString );
  508.     { Signal error }
  509.     Result := False;
  510.     { leave }
  511.     exit;
  512.   end;
  513.   GetMem( TheReturnPChar , 514 );
  514.   HoldPChar := TheReturnPchar;
  515.   TheReturnList.Clear;
  516.   repeat
  517.     TheResult := GetNNTPServerExtendedResponse( TheReturnPChar );
  518.     if StrLen( TheReturnPChar ) > 255 then
  519.     begin
  520.       Getmem( TheHoldingPChar , 255 );
  521.       while StrLen( TheReturnPChar ) > 255 do
  522.       begin
  523.         StrCopy( TheHoldingPChar , '' );
  524.         StrMove( TheHoldingPChar , TheReturnPChar , 255 );
  525.         TheReturnPChar := TheReturnPChar + 256;
  526.         TheReturnString := StrPas( TheHoldingPChar );
  527.         TheReturnList.Add( TheReturnString );
  528.       end;
  529.       StrCopy( TheHoldingPChar , '' );
  530.       StrMove( TheHoldingPChar , TheReturnPChar , StrLen( TheReturnPChar ));
  531.       TheReturnString := StrPas( TheHoldingPChar );
  532.       TheReturnString := '\' + TheReturnString;
  533.       TheReturnList.Add( TheReturnString );
  534.       FreeMem( TheHoldingPChar , 255 );
  535.     end
  536.     else
  537.     begin
  538.       TheReturnString := StrPas( TheReturnPChar );
  539.       TheReturnList.Add( TheReturnString );
  540.     end;
  541.   until (( GlobalAbortedFlag ) or ( TheResult = TCPIP_STATUS_COMPLETED ));
  542.   FreeMem( HoldPChar , 514 );
  543.   Result := true;
  544. end;
  545.  
  546. { This method parses a header stringlist and obtains the subject line }
  547. function TNNTPComponent.GetHeaderSubject( HList : TStringList ) : String;
  548. var Counter_1     : Integer;
  549.     Finished      : Boolean;
  550.     WorkingIndex  : Integer;
  551.     WorkingString : String;
  552. begin
  553.   Counter_1 := 0;
  554.   Finished := false;
  555.   WorkingString := '[No Subject]';
  556.   while (( not Finished ) and ( Counter_1 < HList.Count - 1 )) do
  557.   begin
  558.     WorkingIndex := Pos( 'SUBJECT:' , Uppercase( HList.Strings[ Counter_1 ] ));
  559.     if WorkingIndex > 0 then
  560.     begin
  561.       WorkingString := Copy( HList.Strings[ Counter_1 ] , 9 , 255 );
  562.       Finished := true;
  563.     end
  564.     else Counter_1 := Counter_1 + 1;
  565.   end;
  566.   Result := WorkingString;
  567. end;
  568.  
  569. { This method parses a header stringlist and obtains the sender's ID }
  570. function TNNTPComponent.GetHeaderSender( HList : TStringList ) : String;
  571. var Counter_1     : Integer;
  572.     Finished      : Boolean;
  573.     WorkingIndex  : Integer;
  574.     WorkingString : String;
  575. begin
  576.   Counter_1 := 0;
  577.   Finished := false;
  578.   WorkingString := '';
  579.   while (( not Finished ) and ( Counter_1 < HList.Count - 1 )) do
  580.   begin
  581.     WorkingIndex := Pos( 'FROM:' , Uppercase( HList.Strings[ Counter_1 ] ));
  582.     if WorkingIndex > 0 then
  583.     begin
  584.       WorkingString := Copy( HList.Strings[ Counter_1 ] , 6 , 255 );
  585.       Finished := true;
  586.     end
  587.     else Counter_1 := Counter_1 + 1;
  588.   end;
  589.   Result := WorkingString;
  590. end;
  591.  
  592.  
  593. { This method updates the available headers in the header file for a newsgroup }
  594. function TNNTPComponent.GetAllArticleHeaders( TheNGRecord : PNewsGroupRecord ) : Boolean;
  595. var TheNGARecord   : PNewsGroupArticleRecord;
  596.     Counter_1      : Integer;
  597.     TheHeaderList  : TStringList;
  598.     WorkingList    : TList;
  599.     WorkingCounter : Longint;
  600. begin
  601.   { Do this for ease of coding }
  602.   with TheNGRecord^ do
  603.   begin
  604.     { Get the current TList of article headers }
  605.     WorkingList := TList( GLTag );
  606.     { Set Group Command without updating }
  607.     if not SetCurrentNewsGroup( TheNGRecord , false ) then
  608.     begin
  609.       { Abort if can't get newsgroup }
  610.       Result := false;
  611.       exit;
  612.     end;
  613.     { create the stringlist for header info }
  614.     TheHeaderList := TStringList.Create;
  615.     { Determine how many to get from computed availability }
  616.     WorkingCounter := GHighestAvailable - GTotalNew + 1;
  617.     { Run up to total new articles }
  618.     for Counter_1 := 1 to GTotalNew do
  619.     begin
  620.       { Try to get the header }
  621.       if GetArticleHeader( WorkingCounter , TheHeaderList ) then
  622.       begin
  623.         { If succeed create new header record }
  624.         New( TheNGARecord );
  625.         with TheNGARecord^ do
  626.         begin
  627.           { Fill in all the fields with nominal or acquired data }
  628.           NGAGroupname   := GRealName;
  629.           NGASubject     := GetHeaderSubject( TheHeaderList );
  630.           NGANumber      := WorkingCounter;
  631.           NGADownloaded  := false;
  632.           NGASender      := GetHeaderSender( TheHeaderList );
  633.           NGARead        := false;
  634.           NGAPosted      := false;
  635.           NGAArtFileName := '';
  636.         end;
  637.         { Put record on list }
  638.         WorkingList.Add( TheNGARecord );
  639.       end;
  640.       { Either way increment the counter }
  641.       WorkingCounter := WorkingCounter + 1;
  642.     end;
  643.     { Update all the pointer numbers to indicate all article headers gotten }
  644.     GTotalUnreadArticles := GTotalUnreadArticles + GTotalAvailable;
  645.     GTotalArticles := GTotalArticles + GTotalAvailable;
  646.     GTotalAvailable := 0;
  647.     GTotalNew := 0;
  648.     GLowestAvailable := GHighestAvailable;
  649.     GLowest := GLowestAvailable;
  650.     GHighest := GLowestAvailable;
  651.     { Save off the pointer to the modified TList }
  652.     GLTag := Longint( WorkingList );
  653.     { Clean Up and leave }
  654.     Result := true;
  655.     TheHeaderList.Free;
  656.   end;
  657. end;
  658.  
  659. { This function deletes all read/sent articles and associated files }
  660. function TNNTPComponent.PurgeReadSentArticleListings(
  661.  TheNGRecord : PNewsGroupRecord ) : Boolean;
  662. var TheNGARecord   : PNewsGroupArticleRecord;
  663.     Counter_1      : Integer;
  664.     WorkingList    : TList;
  665.     Finished       : Boolean;
  666. begin
  667.   { Do this for ease of coding }
  668.   with TheNGRecord^ do
  669.   begin
  670.     { Get the current TList of article headers }
  671.     WorkingList := TList( GLTag );
  672.     { Run up to total new articles }
  673.     for Counter_1 := 0 to WorkingList.Count - 1 do
  674.     begin
  675.       TheNGARecord := PNewsGroupArticleRecord( WorkingList.Items[ Counter_1 ] );
  676.       if ( TheNGARecord^.NGAPosted or TheNGARecord^.NGARead ) then
  677.       begin
  678.         Dec( GTotalArticles );
  679.         if FileExists( NewsPath + '\' + TheNGARecord^.NGAArtFilename ) then
  680.          {DeleteFile( NewsPath + '\' + TheNGARecord^.NGAArtFileName )};
  681.       end;
  682.     end;
  683.     Counter_1 := 0;
  684.     Finished := False;
  685.     while Not Finished do
  686.     begin
  687.       TheNGARecord := PNewsGroupArticleRecord( WorkingList.Items[ Counter_1 ] );
  688.       if ( TheNGARecord^.NGAPosted or TheNGARecord^.NGARead ) then
  689.       begin
  690.         WorkingList.Delete( Counter_1 );
  691.       end
  692.       else Counter_1 := Counter_1 + 1;
  693.       if Counter_1 > WorkingList.Count - 1 then Finished := true;
  694.     end;
  695.   end;
  696.   Result := true;
  697. end;
  698.  
  699. { This method uses the ARTICLE command to obtain an article and put it in a  }
  700. { preset/supplied file. It is designed to work by itself or inside DAALs     }
  701. function TNNTPComponent.DownloadArticleListing( TheNumber   : Longint;
  702.                                                 TheFileName : String   ) : Boolean;
  703. var TheReturnString : String;  { Internal string holder }
  704.     TheResult       : Integer; { Internal int holder    }
  705.     HoldPChar ,
  706.     TheReturnPChar ,
  707.     TheHoldingPChar : PChar;
  708.     TheArticleFile       : TextFile;
  709. begin
  710.   TheReturnString :=
  711.    DoCStyleFormat( 'ARTICLE %d' ,
  712.     [ TheNumber ] );
  713.   { Put result in progress and status line }
  714.   AddProgressText( TheReturnString );
  715.   ShowProgressText( TheReturnString );
  716.   { Begin login sequence with user name }
  717.   TheResult := PerformNNTPCommand( 'ARTICLE %d', [ TheNumber ] );
  718.   if TheResult <> TCPIP_STATUS_PRELIMINARY then
  719.   begin
  720.     NNTPCommandInProgress := false;
  721.     Result := false;
  722.     exit;
  723.   end;
  724.   repeat
  725.     TheResult := GetNNTPServerResponse( TheReturnString );
  726.     { Put result in progress and status line }
  727.     AddProgressText( TheReturnString );
  728.     ShowProgressText( TheReturnString );
  729.   until (( GlobalAbortedFlag ) or ( TheResult <> TCPIP_STATUS_PRELIMINARY ));
  730.   NNTPCommandInProgress := false;
  731.   if ( GlobalAbortedFlag or ( TheResult <> TCPIP_STATUS_COMPLETED )) then
  732.   begin
  733.     { Do clever C formatting trick }
  734.     TheReturnString :=
  735.      DoCStyleFormat( 'Article %d Failed!' ,
  736.       [ TheNumber ] );
  737.     { Put result in progress and status line }
  738.     AddProgressText( TheReturnString );
  739.     ShowProgressErrorText( TheReturnString );
  740.     { Signal error }
  741.     Result := False;
  742.     { leave }
  743.     exit;
  744.   end;
  745.   GetMem( TheReturnPChar , 514 );
  746.   HoldPChar := TheReturnPChar;
  747.   try
  748.     AssignFile( TheArticleFile , TheFileName );
  749.     Rewrite( TheArticleFile );
  750.   except
  751.     MessageDlg( 'Unable to open News Article file ' + TheFileName + '!' ,
  752.      mtError , [mbok],0 );
  753.     Socket1.OutOfBand := 'ABOR'+#13#10;
  754.     repeat
  755.       TheResult := GetNNTPServerResponse( TheReturnString );
  756.       { Put result in progress and status line }
  757.       AddProgressText( TheReturnString );
  758.       ShowProgressText( TheReturnString );
  759.     until (( GlobalAbortedFlag ) or ( TheResult <> TCPIP_STATUS_PRELIMINARY ));
  760.     result := false;
  761.     exit;
  762.   end;
  763.   repeat
  764.     TheResult := GetNNTPServerExtendedResponse( TheReturnPChar );
  765.     if StrLen( TheReturnPChar ) > 255 then
  766.     begin
  767.       Getmem( TheHoldingPChar , 255 );
  768.       while StrLen( TheReturnPChar ) > 255 do
  769.       begin
  770.         StrCopy( TheHoldingPChar , '' );
  771.         StrMove( TheHoldingPChar , TheReturnPChar , 255 );
  772.         TheReturnPChar := TheReturnPChar + 256;
  773.         TheReturnString := StrPas( TheHoldingPChar );
  774.         Writeln( TheArticleFile , TheReturnString );
  775.       end;
  776.       StrCopy( TheHoldingPChar , '' );
  777.       StrMove( TheHoldingPChar , TheReturnPChar , StrLen( TheReturnPChar ));
  778.       TheReturnString := StrPas( TheHoldingPChar );
  779.       TheReturnString := '\' + TheReturnString;
  780.       Writeln( TheArticleFile , TheReturnString );
  781.       FreeMem( TheHoldingPChar , 255 );
  782.     end
  783.     else
  784.     begin
  785.       TheReturnString := StrPas( TheReturnPChar );
  786.       Writeln( TheArticleFile , TheReturnString );
  787.     end;
  788.   until (( GlobalAbortedFlag ) or ( TheResult = TCPIP_STATUS_COMPLETED ));
  789.   FreeMem( HoldPChar , 514 );
  790.   CloseFile( TheArticleFile );
  791.   Result := true;
  792. end;
  793.  
  794. { This method Gets all the Article Listings for a newsgroup which have not been  }
  795. { Downloaded and gets them into text files. It displays Article count, # & bytes }
  796. { in the status line during the process.                                         }
  797. function TNNTPComponent.DownloadAllArticleListings( TheNGRecord : PNewsGroupRecord ) : Boolean;
  798. var WorkingList   : TList;
  799.     TheNGARecord  : PNewsGroupArticleRecord;
  800.     WorkingGroupNumber,
  801.     WorkingNumber       : Longint;
  802.     Counter_1 : Integer;
  803.     WorkingFileName : String;
  804. begin
  805.   if not SetCurrentNewsGroup( TheNGRecord , false ) then
  806.   begin
  807.     { Abort if can't get newsgroup }
  808.     Result := false;
  809.     exit;
  810.   end;
  811.   with TheNGRecord^ do
  812.   begin
  813.     WorkingGroupNumber := GIDNumber;
  814.     WorkingList := TList( GLTag );
  815.     for Counter_1 := 0 to WorkingList.Count - 1 do
  816.     begin
  817.       TheNGARecord := PNewsGroupArticleRecord( WorkingList.Items[ Counter_1 ] );
  818.       with TheNGARecord^ do
  819.       begin
  820.         if not NGADownloaded then
  821.         begin
  822.           NGADownloaded := true;
  823.           WorkingNumber := NGANumber;
  824.           WorkingFileName := 'AR' + IntToStr( WorkingNumber );
  825.           if Length( WorkingFileName ) > 8 then WorkingFileName :=
  826.            Copy( WorkingFileName , 1 , 8 );
  827.           WorkingFileName := WorkingFileName + '.' +
  828.            IntToStr( WorkingGroupNumber );
  829.           NGAArtFileName := WorkingFileName;
  830.           WorkingFileName := NewsPath + '\' + WorkingFileName;
  831.           DownloadArticleListing( WorkingNumber , WorkingFileName );
  832.         end;
  833.       end;
  834.     end;
  835.     GLTag := Longint( WorkingList );
  836.     Result := true;
  837.   end;
  838. end;
  839.  
  840. { This function is similar to the above but uses only marked entries in LB2 }
  841. function TNNTPComponent.DownloadAllMarkedArticleListings(
  842.  TheNGRecord : PNewsGroupRecord; TheListBox : TListBox ) : Boolean;
  843. var WorkingString : String;
  844.     WorkingIndex  : Integer;
  845.     WorkingList   : TList;
  846.     TheNGARecord  : PNewsGroupArticleRecord;
  847.     WorkingGroupNumber,
  848.     WorkingNumber       : Longint;
  849.     Counter_2 ,
  850.     Counter_1 : Integer;
  851.     WorkingFileName : String;
  852. begin
  853.   if not SetCurrentNewsGroup( TheNGRecord , false ) then
  854.   begin
  855.     { Abort if can't get newsgroup }
  856.     Result := false;
  857.     exit;
  858.   end;
  859.   with TheNGRecord^ do
  860.   begin
  861.     WorkingIndex := Pos( 'G' , GFileName );
  862.     WorkingString := Copy( GFileName , WorkingIndex + 1 , 255 );
  863.     WorkingIndex := Pos( '.' , WorkingString );
  864.     WorkingString := Copy( WorkingString , 1 , WorkingIndex - 1 );
  865.     WorkingGroupNumber := StrToInt( WorkingString );
  866.     WorkingList := TList( GLTag );
  867.     for Counter_1 := 0 to TheListBox.Items.Count - 1 do
  868.     begin
  869.       if TheListBox.Selected[ Counter_1 ] then
  870.       begin
  871.         WorkingString :=
  872.          TheFTPComponent.StripBrackets( TheListBox.Items[ Counter_1 ] );
  873.         WorkingNumber := StrToInt( WorkingString );
  874.         TheNGARecord := nil;
  875.         for Counter_2 := 0 to WorkingList.Count - 1 do
  876.         begin
  877.           TheNGARecord := PNewsGroupArticleRecord( WorkingList.Items[ Counter_1 ] );
  878.           if TheNGARecord^.NGANumber = WorkingNumber then break;
  879.         end;
  880.         if assigned( TheNGARecord ) then with TheNGARecord^ do
  881.         begin
  882.           if not NGADownloaded then
  883.           begin
  884.             NGADownloaded := true;
  885.             WorkingNumber := NGANumber;
  886.             WorkingFileName := 'AR' + IntToStr( WorkingNumber );
  887.             if Length( WorkingFileName ) > 8 then WorkingFileName :=
  888.              Copy( WorkingFileName , 1 , 8 );
  889.             WorkingFileName := WorkingFileName + '.' +
  890.              IntToStr( WorkingGroupNumber );
  891.             NGAArtFileName := WorkingFileName;
  892.             WorkingFileName := NewsPath + '\' + WorkingFileName;
  893.             DownloadArticleListing( WorkingNumber , WorkingFileName );
  894.           end;
  895.         end;
  896.       end;
  897.     end;
  898.     GLTag := Longint( WorkingList );
  899.     Result := true;
  900.   end;
  901. end;
  902.  
  903. { This sends FTP progress text to the Inet form }
  904. procedure TNNTPComponent.ShowProgressErrorText( WhatText : String );
  905. begin
  906.   CCInetCCForm.ShowProgressErrorText( WhatText );
  907. end;
  908.  
  909. { This is a core function! It performs an FTP command and if no timeout }
  910. { return a preliminary ok.                                              }
  911. function TNNTPComponent.PerformNNTPCommand(
  912.                  TheCommand        : string;
  913.            const TheArguments      : array of const ) : Integer;
  914. var TheBuffer : string; { Text buffer }
  915. begin
  916.   { If command in progress send back -1 error }
  917.   if NNTPCommandInProgress then
  918.   begin
  919.     Result := -1;
  920.     exit;
  921.   end;
  922.   { Set status variable }
  923.   NNTPCommandInProgress := True;
  924.   { Set global error code }
  925.   GlobalErrorCode := 0;
  926.   { Format output string }
  927.   TheBuffer := Format( TheCommand , TheArguments );
  928.   { Preset failure code }
  929.   Result := TCPIP_STATUS_FATAL_ERROR;
  930.   { If invalid socket or no connection abort }
  931.   if ( Socket1.TheSocket = INVALID_SOCKET ) or not Connection_Established then
  932.    exit;
  933.   { Send the buffer plus EOL chars }
  934.   Socket1.StringData := TheBuffer + #13#10;
  935.   { if abort due to timeout or other error exit }
  936.   if (( GlobalAbortedFlag ) or ( GlobalErrorCode <> 0 )) then exit;
  937.   { Otherwise return preliminary code }
  938.   Result := TCPIP_STATUS_PRELIMINARY;
  939. end;
  940.  
  941. { This is a core function! It performs an FTP command and if no timeout }
  942. { return a preliminary ok.                                              }
  943. function TNNTPComponent.PerformBlindNNTPCommand( TheCommand : string ) : Integer;
  944. var TheBuffer : string; { Text buffer }
  945. begin
  946.   { If command in progress send back -1 error }
  947.   if NNTPCommandInProgress then
  948.   begin
  949.     Result := -1;
  950.     exit;
  951.   end;
  952.   { Set status variable }
  953.   NNTPCommandInProgress := True;
  954.   { Set global error code }
  955.   GlobalErrorCode := 0;
  956.   { Format output string }
  957.   TheBuffer := TheCommand;
  958.   { Preset failure code }
  959.   Result := TCPIP_STATUS_FATAL_ERROR;
  960.   { If invalid socket or no connection abort }
  961.   if ( Socket1.TheSocket = INVALID_SOCKET ) or not Connection_Established then
  962.    exit;
  963.   { Send the buffer plus EOL chars }
  964.   Socket1.StringData := TheBuffer + #13#10;
  965.   { if abort due to timeout or other error exit }
  966.   if (( GlobalAbortedFlag ) or ( GlobalErrorCode <> 0 )) then exit;
  967.   { Otherwise return preliminary code }
  968.   Result := TCPIP_STATUS_PRELIMINARY;
  969. end;
  970.  
  971. { This function gets up to 255 chars of data plus a return code from FTP serv }
  972. function TNNTPComponent.GetNNTPServerResponse(
  973.           var ResponseString : String ) : integer;
  974. var
  975.   { Buffer string for response line }
  976.   TheBuffer     : string;
  977.   { Pointer to the response string }
  978.   BufferPointer : array[0..255] of char absolute TheBuffer;
  979.   { Character to check for response code }
  980.   ResponseChar   : char;
  981.   { Pointers into returned string }
  982.   TheIndex ,
  983.   TheLength     : integer;
  984.   { Control variable }
  985.   LeftoversInPan ,
  986.   Finished      : Boolean;
  987. begin
  988.   { Preset fatal error }
  989.   Result := TCPIP_STATUS_FATAL_ERROR;
  990.   { Start loop control }
  991.   LeftoversInPan := false;
  992.   Finished := false;
  993.   repeat
  994.     { Do a peek }
  995.     TheBuffer := Socket1.PeekData;
  996.     { If timeout or other error exit }
  997.     if (( GlobalAbortedFlag ) or ( GlobalErrorCode <> 0 )) then exit;
  998.     { Find end of line character }
  999.     TheIndex := Pos( #10 , TheBuffer );
  1000.     if TheIndex = 0 then
  1001.     begin
  1002.       TheIndex := Pos( #13 , TheBuffer );
  1003.       if TheIndex = 0 then
  1004.       begin
  1005.         TheIndex := Pos( #0 , TheBuffer );
  1006.         if TheIndex = 0 then
  1007.         begin
  1008.           TheIndex := Length( TheBuffer );
  1009.           LeftoversInPan := True;
  1010.           LeftoverText := LeftoverText + TheBuffer;
  1011.           LeftoversOnTable := false;
  1012.         end;
  1013.       end;
  1014.     end;
  1015.     { If an end of line then process the line }
  1016.     if TheIndex > 0 then
  1017.     begin
  1018.       { Get length of string }
  1019.       TheLength := TheIndex;
  1020.       { Receive actual data }
  1021.       Socket1.CCSockReceive( Socket1.TheSocket   ,
  1022.                              @BufferPointer[ 1 ] ,
  1023.                              TheLength              );
  1024.       { Abort if timeout or error }
  1025.       if (( GlobalAbortedFlag ) or ( GlobalErrorCode <> 0 )) then exit;
  1026.       { Put in the length byte }
  1027.       BufferPointer[ 0 ] := Chr( TheLength );
  1028.       if LeftOversOnTable then
  1029.       begin
  1030.         LeftOversOnTable := false;
  1031.         ResponseString := LeftoverText + TheBuffer;
  1032.         TheBuffer := ResponseString;
  1033.         LeftoverText := '';
  1034.       end;
  1035.       if LeftoversInPan then
  1036.       begin
  1037.         LeftoversInPan := false;
  1038.         LeftoversOnTable := true;
  1039.       end;
  1040.       { Get first number character }
  1041.       ResponseChar := TheBuffer[ 1 ];
  1042.       { Get the value of the number from 1 to 5 }
  1043.       if (( ResponseChar >= '1' ) and ( ResponseChar <= '5' )) then
  1044.       begin
  1045.         Finished := true;
  1046.         Result := Ord( ResponseChar ) - 48;
  1047.       end;
  1048.     end
  1049.     else
  1050.     begin
  1051.     end;
  1052.   until ( Finished and ( not LeftoversOnTable ));
  1053.   { Return buffer as response string }
  1054.   ResponseString := TheBuffer;
  1055. end;
  1056.  
  1057. { Boilerplate error routine }
  1058. procedure TNNTPComponent.NNTPSocketsErrorOccurred( Sender     : TObject;
  1059.                                                  ErrorCode  : Integer;
  1060.                                                  TheMessage : String   );
  1061. begin
  1062.   CCInetCCForm.SocketsErrorOccurred( Sender,ErrorCode,TheMessage );
  1063. end;
  1064.  
  1065. { This is the FTP components initial connection routine }
  1066. function TNNTPComponent.EstablishConnection(
  1067.           PCRPointer : PConnectionsRecord ) : Boolean;
  1068. var TheReturnString : String;  { Internal string holder }
  1069.     TheResult       : Integer; { Internal int holder    }
  1070. begin
  1071.   { Set default FTP Port value }
  1072.   Socket1.PortName := '119';
  1073.   { Get the ip address from the record }
  1074.   Socket1.IPAddressName := PCRPointer^.CIPAddress;
  1075.   { Set blocking mode }
  1076.   Socket1.AsynchMode := False;
  1077.   { Clear condition variables }
  1078.   GlobalErrorCode := 0;
  1079.   GlobalAbortedFlag := false;
  1080.   { Actually attempt to connect }
  1081.   Socket1.CCSockConnect;
  1082.   { Check if connected }
  1083.   if (( GlobalAbortedFlag ) or ( GlobalErrorCode <> 0 ) or
  1084.       ( Socket1.TheSocket = INVALID_SOCKET )) then
  1085.   begin { Didn't connect; signal error and abort }
  1086.     { Do clever C formatting trick }
  1087.     TheReturnString :=
  1088.      DoCStyleFormat( 'NNTP Host %s Connection Failed!' ,
  1089.       [ PCRPointer^.CIPAddress ] );
  1090.     { Put result in progress and status line }
  1091.     AddProgressText( TheReturnString );
  1092.     ShowProgressErrorText( TheReturnString );
  1093.     { Signal error }
  1094.     Result := False;
  1095.     { leave }
  1096.     exit;
  1097.   end
  1098.   else
  1099.   begin
  1100.     Connection_Established := true;
  1101.     { Signal successful connection }
  1102.     TheReturnString := DoCStyleFormat(
  1103.       'Connected on Local port: %s with IP: %s',
  1104.       [ Socket1.GetSocketPort( Socket1.TheSocket ),
  1105.         Socket1.GetSocketIPAddress( Socket1.TheSocket )]);
  1106.     { Put result in progress and status line }
  1107.     CCINetCCForm.AddProgressText( TheReturnString );
  1108.     CCINetCCForm.ShowProgressText( TheReturnString );
  1109.     TheReturnString := DoCStyleFormat(
  1110.      'Connected to Remote port: %s with IP: %s',
  1111.       [ Socket1.GetSocketPeerPort( Socket1.TheSocket ),
  1112.         Socket1.GetSocketPeerIPAddress( Socket1.TheSocket )]);
  1113.     { Put result in progress and status line }
  1114.     CCINetCCForm.AddProgressText( TheReturnString );
  1115.     CCINetCCForm.ShowProgressText( TheReturnString );
  1116.     TheReturnString := DoCStyleFormat( 'Successfully connected to %s',
  1117.      [ Socket1.IPAddressName ]);
  1118.     { Put result in progress and status line }
  1119.     CCINetCCForm.AddProgressText( TheReturnString );
  1120.     CCINetCCForm.ShowProgressText( TheReturnString );
  1121.     repeat
  1122.       TheResult := GetNNTPServerResponse( TheReturnString );
  1123.       { Put result in progress and status line }
  1124.       AddProgressText( TheReturnString );
  1125.       ShowProgressText( TheReturnString );
  1126.     until (( GlobalAbortedFlag ) or ( TheResult <> TCPIP_STATUS_PRELIMINARY ));
  1127.     if ( GlobalAbortedFlag or ( TheResult <> TCPIP_STATUS_COMPLETED )) then
  1128.     begin
  1129.       { Do clever C formatting trick }
  1130.       TheReturnString :=
  1131.        DoCStyleFormat( 'NNTP Host %s Connection Failed!' ,
  1132.         [ PCRPointer^.CIPAddress ] );
  1133.       { Put result in progress and status line }
  1134.       AddProgressText( TheReturnString );
  1135.       ShowProgressErrorText( TheReturnString );
  1136.       { Signal error }
  1137.       Result := False;
  1138.       { leave }
  1139.       exit;
  1140.     end
  1141.     else Result := true; { Signal no problem }
  1142.   end;
  1143. end;
  1144.  
  1145. { This is the FTP component constructor; it creates 2 sockets }
  1146. constructor TNNTPComponent.Create( AOwner : TComponent );
  1147. begin
  1148.   { do inherited create }
  1149.   inherited Create( AOwner );
  1150.   { Create socket, put in their parent, and error procs }
  1151.   Socket1 := TCCSocket.Create( Self );
  1152.   Socket1.Parent := Self;
  1153.   Socket1.OnErrorOccurred := NNTPSocketsErrorOccurred;
  1154.   { Set up booleans }
  1155.   Connection_Established := false;
  1156.   NNTPCommandInProgress := false;
  1157. end;
  1158.  
  1159. { This is the FTP component destructor; it frees 2 sockets }
  1160. destructor TNNTPComponent.Destroy;
  1161. begin
  1162.   { Free the socket }
  1163.   Socket1.Free;
  1164.   { and call inherited }
  1165.   inherited Destroy;
  1166. end;
  1167.  
  1168. procedure TNNTPComponent.UpdateGauge( BytesFinished , TotalToHandle : longint );
  1169. begin
  1170.   CCInetCCForm.UpdateGauge( BytesFinished , TotalToHandle );
  1171. end;
  1172.  
  1173. { This sends FTP progress text to the Inet form }
  1174. procedure TNNTPComponent.AddProgressText( WhatText : String );
  1175. begin
  1176.   CCInetCCForm.AddProgressText( WhatText );
  1177. end;
  1178.  
  1179. { This sends FTP progress text to the Inet form }
  1180. procedure TNNTPComponent.ShowProgressText( WhatText : String );
  1181. begin
  1182.   CCInetCCForm.ShowProgressText( WhatText );
  1183. end;
  1184.  
  1185. { This is the FTP components QUIT routine }
  1186. function TNNTPComponent.Disconnect : Boolean;
  1187. var TheReturnString : String;  { Internal string holder }
  1188.     TheResult       : Integer; { Internal int holder    }
  1189. begin
  1190.   TheReturnString :=
  1191.    DoCStyleFormat( 'QUIT' ,
  1192.     [ nil ] );
  1193.   { Put result in progress and status line }
  1194.   AddProgressText( TheReturnString );
  1195.   ShowProgressText( TheReturnString );
  1196.   { Begin login sequence with user name }
  1197.   PerformNNTPCommand( 'QUIT', [ nil ] );
  1198.   repeat
  1199.     TheResult := GetNNTPServerResponse( TheReturnString );
  1200.     { Put result in progress and status line }
  1201.     AddProgressText( TheReturnString );
  1202.     ShowProgressText( TheReturnString );
  1203.   until (( GlobalAbortedFlag ) or ( TheResult <> TCPIP_STATUS_PRELIMINARY ));
  1204.   NNTPCommandInProgress := false;
  1205.   if ( GlobalAbortedFlag or ( TheResult <> TCPIP_STATUS_COMPLETED )) then
  1206.   begin
  1207.     { Do clever C formatting trick }
  1208.     TheReturnString :=
  1209.      DoCStyleFormat( 'NNTP Host Connection Failed!' ,
  1210.       [ nil ] );
  1211.     { Put result in progress and status line }
  1212.     AddProgressText( TheReturnString );
  1213.     ShowProgressErrorText( TheReturnString );
  1214.     { Signal error }
  1215.     Result := False;
  1216.     { leave }
  1217.     exit;
  1218.   end
  1219.   else Result := true; { Signal no problem }
  1220. end;
  1221.  
  1222. { This is a clever c-style formatting trick }
  1223. function TNNTPComponent.DoCStyleFormat(
  1224.                 TheText      : string;
  1225.           const TheArguments : array of const ) : String;
  1226. begin
  1227.   Result := Format( TheText , TheArguments ) + #13#10;
  1228. end;
  1229.  
  1230.  
  1231. end.
  1232.