home *** CD-ROM | disk | FTP | other *** search
/ PC Pro 1999 February / DPPCPRO0299.ISO / February / Delphi / Runimage / DELPHI20 / DEMOS / INTERNET / NNTP / MAIN.PAS < prev    next >
Encoding:
Pascal/Delphi Source File  |  1996-06-10  |  13.1 KB  |  527 lines

  1. unit main;
  2.  
  3. interface
  4.  
  5. uses
  6.   Windows, Messages, SysUtils, Classes, Graphics, Controls, Forms, Dialogs,
  7.   Menus, OleCtrls, StdCtrls, ComCtrls, ExtCtrls, Buttons, OLE2, ISP;
  8.  
  9. const
  10.   efListGroups = 0;
  11.   efGetArticleHeaders = 1;
  12.   efGetArticleNumbers = 2;
  13.   efGetArticle = 3;
  14.  
  15. type
  16.   TNewsForm = class(TForm)
  17.     NNTP1: TNNTP;
  18.     MainMenu1: TMainMenu;
  19.     File1: TMenuItem;
  20.     Exit1: TMenuItem;
  21.     N1: TMenuItem;
  22.     FileDisconnectItem: TMenuItem;
  23.     FileConnectItem: TMenuItem;
  24.     Panel1: TPanel;
  25.     Bevel1: TBevel;
  26.     StatusBar: TStatusBar;
  27.     SmallImages: TImageList;
  28.     Panel2: TPanel;
  29.     NewsGroups: TTreeView;
  30.     Bevel2: TBevel;
  31.     Panel3: TPanel;
  32.     Memo1: TMemo;
  33.     Panel5: TPanel;
  34.     Panel4: TPanel;
  35.     ConnectBtn: TSpeedButton;
  36.     RefreshBtn: TSpeedButton;
  37.     Bevel3: TBevel;
  38.     MsgHeaders: TListBox;
  39.     Label1: TLabel;
  40.     Label2: TLabel;
  41.     procedure FileConnectItemClick(Sender: TObject);
  42.     procedure NNTP1AuthenticateRequest(Sender: TObject; var UserId,
  43.       Password: string);
  44.     procedure NNTP1ProtocolStateChanged(Sender: TObject;
  45.       ProtocolState: Smallint);
  46.     procedure NNTP1DocOutput(Sender: TObject; const DocOutput: Variant);
  47.     procedure NNTP1StateChanged(Sender: TObject; State: Smallint);
  48.     procedure Exit1Click(Sender: TObject);
  49.     procedure NNTP1Error(Sender: TObject; Number: Smallint;
  50.       var Description: string; Scode: Integer; const Source,
  51.       HelpFile: string; HelpContext: Integer; var CancelDisplay: Wordbool);
  52.     procedure NNTP1Banner(Sender: TObject; const Banner: string);
  53.     procedure MsgHeadersDblClick(Sender: TObject);
  54.     procedure NNTP1SelectGroup(Sender: TObject; const groupName: string;
  55.       firstMessage, lastMessage, msgCount: Integer);
  56.     procedure FormClose(Sender: TObject; var Action: TCloseAction);
  57.     procedure NewsGroupsChange(Sender: TObject; Node: TTreeNode);
  58.     procedure RefreshBtnClick(Sender: TObject);
  59.     procedure FileDisconnectItemClick(Sender: TObject);
  60.   private
  61.     EventFlag: Integer;
  62.     function NodePath(Node: TTreeNode): String;
  63.   public
  64.     Data: String;
  65.   end;
  66.  
  67. var
  68.   NewsForm: TNewsForm;
  69.   Remainder: String;
  70.   Nodes: TStringList;
  71.   CurrentGroup: String;
  72.   GroupCount: Integer;
  73.  
  74. implementation
  75.  
  76. uses Connect;
  77.  
  78. {$R *.DFM}
  79.  
  80. { TParser }
  81.  
  82. type
  83.  
  84.   TToken = (etEnd, etSymbol, etName, etLiteral);
  85.  
  86.   TParser = class
  87.   private
  88.     FFlags: Integer;
  89.     FText: string;
  90.     FSourcePtr: PChar;
  91.     FSourceLine: Integer;
  92.     FTokenPtr: PChar;
  93.     FTokenString: string;
  94.     FToken: TToken;
  95.     procedure SkipBlanks;
  96.     procedure NextToken;
  97.     function TokenName: string;
  98.     function TokenSymbolIs(const S: string): Boolean;
  99.   public
  100.     constructor Create(const Text: string; Groups: Boolean);
  101.   end;
  102.  
  103. const
  104.   sfAllowSpaces = 1;
  105.  
  106. constructor TParser.Create(const Text: string; Groups: Boolean);
  107. begin
  108.   FText := Text;
  109.   FSourceLine := 1;
  110.   FSourcePtr := PChar(Text);
  111.   if Groups then
  112.     FFlags := sfAllowSpaces
  113.   else
  114.     FFlags := 0;
  115.   NextToken;
  116. end;
  117.  
  118. procedure TParser.SkipBlanks;
  119. begin
  120.   while True do
  121.   begin
  122.     case FSourcePtr^ of
  123.       #0:
  124.         begin
  125.           if FSourcePtr^ = #0 then Exit;
  126.           Continue;
  127.         end;
  128.       #10:
  129.         Inc(FSourceLine);
  130.       #33..#255:
  131.         Exit;
  132.     end;
  133.     Inc(FSourcePtr);
  134.   end;
  135. end;
  136.  
  137. procedure TParser.NextToken;
  138. var
  139.   P, TokenStart: PChar;
  140.   L: Integer;
  141.   StrBuf: array[0..255] of Char;
  142. begin
  143.   SkipBlanks;
  144.   FTokenString := '';
  145.   P := FSourcePtr;
  146.   while (P^ <> #0) and (P^ <= ' ') do Inc(P);
  147.   FTokenPtr := P;
  148.   case P^ of
  149.     '0'..'9':
  150.       begin
  151.         TokenStart := P;
  152.         Inc(P);
  153.         while P^ in ['0'..'9'] do Inc(P);
  154.         SetString(FTokenString, TokenStart, P - TokenStart);
  155.         FToken := etLiteral;
  156.       end;
  157.     #13: Inc(FSourceLine);
  158.     #0:
  159.       FToken := etEnd;
  160.   else
  161.     begin
  162.       TokenStart := P;
  163.       Inc(P);
  164.       if FFlags = sfAllowSpaces then
  165.         while not (P^ in [#0, #13, ' ']) do Inc(P)
  166.       else
  167.         while not (P^ in [#0, #13]) do Inc(P);
  168.       SetString(FTokenString, TokenStart, P - TokenStart);
  169.       FToken := etSymbol;
  170.     end;
  171.   end;
  172.   FSourcePtr := P;
  173. end;
  174.  
  175. function TParser.TokenName: string;
  176. begin
  177.   if FSourcePtr = FTokenPtr then Result := LoadStr(0) else
  178.   begin
  179.     SetString(Result, FTokenPtr, FSourcePtr - FTokenPtr);
  180.     Result := '''' + Result + '''';
  181.   end;
  182. end;
  183.  
  184. function TParser.TokenSymbolIs(const S: string): Boolean;
  185. begin
  186.   Result := (FToken = etSymbol) and (CompareText(FTokenString, S) = 0);
  187. end;
  188.  
  189. function FirstItem(var ItemList: ShortString): ShortString;
  190. var
  191.   P: Integer;
  192. begin
  193.   P := Pos('.', ItemList);
  194.   if P = 0 then
  195.   begin
  196.     Result := ItemList;
  197.     P := Length(ItemList);
  198.   end
  199.   else
  200.     Result := Copy(ItemList, 1, P - 1);
  201.   Delete(ItemList, 1, P);
  202. end;
  203.  
  204. procedure AddItem(GroupName: ShortString);
  205. var
  206.   Name, NewGroup: String;
  207.   LeafText: String;
  208.   Index, i: Integer;
  209.   Groups: Integer;
  210.   Item: ShortString;
  211.   TheNodes: TStringList;
  212. begin
  213.   Groups := 1;
  214.   for i := 0 to Length(GroupName) do
  215.     if GroupName[i] = '.' then
  216.       Inc(Groups);
  217.   TheNodes := Nodes;
  218.   for i := 0 to Groups - 1 do
  219.   begin
  220.     Item := FirstItem(GroupName);
  221.     Index := TheNodes.IndexOf(Item);
  222.     if Index = -1 then
  223.     begin
  224.       Index := TheNodes.AddObject(Item, TStringList.Create);
  225.       TheNodes := TStringList(TheNodes.Objects[Index]);
  226.       TheNodes.Sorted := True;
  227.     end
  228.     else
  229.       TheNodes := TStringList(TheNodes.Objects[Index]);
  230.   end;
  231.   Inc(GroupCount);
  232. end;
  233.  
  234. procedure ParseGroups(Data: String);
  235. var
  236.   Parser: TParser;
  237.   OldSrcLine: Integer;
  238. begin
  239.   Parser := TParser.Create(Data, True);
  240.   OldSrcLine := 0;
  241.   while Parser.FToken <> etEnd do
  242.   begin
  243.     if Parser.FSourceLine <> OldSrcLine then
  244.     begin
  245.       AddItem(Parser.FTokenString);
  246.       OldSrcLine := Parser.FSourceLine;
  247.     end;
  248.     Parser.NextToken;
  249.   end;
  250. end;
  251.  
  252. procedure ParseHeaders(Data: String);
  253. var
  254.   Parser: TParser;
  255.   MsgNo: LongInt;
  256.   Header: String;
  257.   OldSrcLine: Integer;
  258. begin
  259.   Parser := TParser.Create(Data, False);
  260.   while Parser.FToken <> etEnd do
  261.   begin
  262.     MsgNo := StrToInt(Parser.FTokenString);
  263.     OldSrcLine := Parser.FSourceLine;
  264.     Parser.NextToken;
  265.     Header := '';
  266.     while (OldSrcLine = Parser.FSourceLine) do
  267.     begin
  268.       Header := Header + ' ' + Parser.FTokenString;
  269.       Parser.NextToken;
  270.       if Parser.FToken = etEnd then
  271.         Break;
  272.     end;
  273.     NewsForm.MsgHeaders.Items.AddObject(Header, Pointer(MsgNo));
  274.   end;
  275. end;
  276.  
  277. procedure DestroyList(AList: TStringList);
  278. var
  279.   i: Integer;
  280. begin
  281.   for i := 0 to AList.Count - 1 do
  282.     if AList.Objects[i] <> nil then
  283.       DestroyList(TStringList(AList.Objects[i]));
  284.   AList.Free;
  285. end;
  286.  
  287. procedure BuildTree(Parent: TTreeNode; List: TStrings);
  288. var
  289.   i: Integer;
  290.   Node: TTreeNode;
  291. begin
  292.   for i := 0 to List.Count - 1 do
  293.     if List.Objects[i] <> nil then
  294.     begin
  295.       Node := NewsForm.NewsGroups.Items.AddChild(Parent, List[i]);
  296.       Node.ImageIndex := 0;
  297.       Node.SelectedIndex := 1;
  298.       BuildTree(Node, TStrings(List.Objects[i]));
  299.     end
  300.     else
  301.       NewsForm.NewsGroups.Items.AddChild(Parent, List[i]);
  302. end;
  303.  
  304. function TNewsForm.NodePath(Node: TTreeNode): String;
  305. begin
  306.   if Node.Parent = nil then
  307.     Result := Node.Text
  308.   else
  309.     Result := NodePath(Node.Parent) + '.' + Node.Text;
  310. end;
  311.  
  312. procedure TNewsForm.FileConnectItemClick(Sender: TObject);
  313. begin
  314.   ConnectDlg := TConnectDlg.Create(Self);
  315.   try
  316.     if ConnectDlg.ShowModal = mrOk then
  317.       with NNTP1 do
  318.         Connect(ConnectDlg.ServerEdit.Text, RemotePort);
  319.   finally
  320.     ConnectDlg.Free;
  321.   end;
  322. end;
  323.  
  324. procedure TNewsForm.NNTP1AuthenticateRequest(Sender: TObject; var UserId,
  325.   Password: string);
  326. begin
  327.   UserID := '';
  328.   Password := '';
  329. end;
  330.  
  331. procedure TNewsForm.NNTP1ProtocolStateChanged(Sender: TObject;
  332.   ProtocolState: Smallint);
  333. begin
  334.   case ProtocolState of
  335.     nntpBase: ;
  336.     nntpTransaction:
  337.       begin
  338.         EventFlag := efListGroups;
  339.         Nodes := TStringList.Create;
  340.         Nodes.Sorted := True;
  341.         NNTP1.ListGroups;
  342.       end;
  343.   end;
  344. end;
  345.  
  346. procedure TNewsForm.NNTP1DocOutput(Sender: TObject; const DocOutput: Variant);
  347. var
  348.   S: String;
  349.   i: integer;
  350.   MsgNo, Header: String;
  351. begin
  352.   Statusbar.Panels[2].Text := Format('Bytes: %s',[DocOutput.BytesTransferred]);
  353.   case DocOutput.State of
  354.     icDocBegin:
  355.       begin
  356.         if EventFlag = efListGroups then
  357.           Memo1.Lines.Add('Retrieving news groups...');
  358.         Data := '';
  359.         GroupCount := 0;
  360.       end;
  361.     icDocData:
  362.       begin
  363.         DocOutput.GetData(S, VT_BSTR);
  364.         Data := Data + S;
  365.         if EventFlag = efGetArticle then
  366.           Memo1.Lines.Add(S);
  367.       end;
  368.     icDocEnd:
  369.       begin
  370.         case EventFlag of
  371.           efListGroups:
  372.             begin
  373.               ParseGroups(Data);
  374.               Memo1.Lines.Add('Done.'#13#10'Building news group tree...');
  375.               NewsGroups.Items.BeginUpdate;
  376.               try
  377.                 BuildTree(nil, Nodes);
  378.                 DestroyList(Nodes);
  379.                 Statusbar.Panels[1].Text := Format('%d Groups',[GroupCount]);
  380.               finally
  381.                 NewsGroups.Items.EndUpdate;
  382.                 Memo1.Lines.Add('Done.');
  383.               end;
  384.             end;
  385.           efGetArticleHeaders: ParseHeaders(Data);
  386.           efGetArticle:
  387.             begin
  388.               Memo1.SelStart := 0;
  389.               SendMessage(Memo1.Handle, EM_ScrollCaret, 0, 0);
  390.             end;
  391.         end;
  392.         SetLength(Data, 0);
  393.       end;
  394.   end;
  395.   Refresh;
  396. end;
  397.  
  398. procedure TNewsForm.NNTP1StateChanged(Sender: TObject; State: Smallint);
  399. begin
  400.   with Memo1.Lines do
  401.     case NNTP1.State of
  402.       prcConnecting   : Add('Connecting');
  403.       prcResolvingHost: Add('Resolving Host: ' + NNTP1.RemoteHost);
  404.       prcHostResolved : Add('Host resolved');
  405.       prcConnected    :
  406.         begin
  407.           Add('Connected to: ' + NNTP1.RemoteHost);
  408.           Statusbar.Panels[0].Text := 'Connected to: ' + NNTP1.RemoteHost;
  409.           ConnectBtn.Enabled := False;
  410.           RefreshBtn.Enabled := True;
  411.         end;
  412.       prcDisconnecting: Text := NNTP1.ReplyString;
  413.       prcDisconnected :
  414.         begin
  415.           Statusbar.Panels[0].Text := 'Disconnected';
  416.           Caption := 'News Reader';
  417.           Label1.Caption := '';
  418.           ConnectBtn.Enabled := True;
  419.           RefreshBtn.Enabled := False;
  420.         end;
  421.     end;
  422. end;
  423.  
  424. procedure TNewsForm.Exit1Click(Sender: TObject);
  425. begin
  426.   if NNTP1.State <> prcDisconnected then
  427.   begin
  428.     if NNTP1.Busy then NNTP1.Cancel;
  429.     NNTP1.Quit;
  430.     while NNTP1.State <> prcDisconnected do
  431.       Application.ProcessMessages;
  432.   end;
  433.   Close;
  434. end;
  435.  
  436. procedure TNewsForm.NNTP1Error(Sender: TObject; Number: Smallint;
  437.   var Description: string; Scode: Integer; const Source, HelpFile: string;
  438.   HelpContext: Integer; var CancelDisplay: Wordbool);
  439. begin
  440. //  MessageDlg(Description, mtError, [mbOk], 0);
  441. end;
  442.  
  443. procedure TNewsForm.NNTP1Banner(Sender: TObject; const Banner: string);
  444. begin
  445.   Memo1.Lines.Add(Banner);
  446. end;
  447.  
  448. procedure TNewsForm.NNTP1SelectGroup(Sender: TObject; const groupName: string;
  449.   firstMessage, lastMessage, msgCount: Integer);
  450. begin
  451.   EventFlag := efGetArticleHeaders;
  452.   Statusbar.Panels[1].Text := Format('%d Article(s)',[msgCount]);
  453.   NNTP1.GetArticleHeaders('subject', FirstMessage, lastMessage);
  454. end;
  455.  
  456. procedure TNewsForm.MsgHeadersDblClick(Sender: TObject);
  457. var
  458.   Article: Integer;
  459. begin
  460.   if NNTP1.Busy then exit;
  461.   EventFlag := efGetArticle;
  462.   Memo1.Clear;
  463.   if MsgHeaders.ItemIndex = -1 then exit;
  464.   Caption := 'News Reader: ' + MsgHeaders.Items[MsgHeaders.ItemIndex];
  465.   Article := Integer(MsgHeaders.Items.Objects[MsgHeaders.ItemIndex]);
  466.   NNTP1.GetArticlebyArticleNumber(Article);
  467. end;
  468.  
  469. procedure TNewsForm.FormClose(Sender: TObject; var Action: TCloseAction);
  470. begin
  471.   if NNTP1.State <> prcDisconnected then
  472.   begin
  473.     if NNTP1.Busy then NNTP1.Cancel;  
  474.     NNTP1.Quit;
  475.     while NNTP1.State <> prcDisconnected do
  476.       Application.ProcessMessages;
  477.   end;
  478. end;
  479.  
  480. procedure TNewsForm.NewsGroupsChange(Sender: TObject; Node: TTreeNode);
  481. var
  482.   NP: String;
  483. begin
  484.   if (NNTP1.State = prcConnected) and not NNTP1.Busy then
  485.     with MsgHeaders do
  486.     begin
  487.       Items.BeginUpdate;
  488.       try
  489.         Items.Clear;
  490.         Memo1.Lines.Clear;
  491.         NP := NodePath(NewsGroups.Selected);
  492.         Statusbar.Panels[2].Text := 'Bytes: 0';
  493.         Statusbar.Panels[1].Text := '0 Article(s)';
  494.         if NNTP1.Busy then
  495.           NNTP1.Cancel;
  496.         NNTP1.SelectGroup(NP);
  497.         Label1.Caption := 'Contents of ''' + NP + '''';
  498.       finally
  499.         Items.EndUpdate;
  500.       end;
  501.     end;
  502. end;
  503.  
  504. procedure TNewsForm.RefreshBtnClick(Sender: TObject);
  505. begin
  506.   if NewsGroups.Selected <> nil then
  507.     NewsGroupsChange(nil, NewsGroups.Selected);
  508. end;
  509.  
  510. procedure TNewsForm.FileDisconnectItemClick(Sender: TObject);
  511. begin
  512.   if NNTP1.Busy then NNTP1.Cancel;
  513.   NNTP1.Quit;
  514.   while NNTP1.Busy do
  515.     Application.ProcessMessages;
  516.   with NewsGroups.Items do
  517.   begin
  518.     BeginUpdate;
  519.     Clear;
  520.     EndUpdate;
  521.   end;
  522.   MsgHeaders.Items.Clear;
  523.   Memo1.Lines.Clear;
  524. end;
  525.  
  526. end.
  527.