home *** CD-ROM | disk | FTP | other *** search
/ Delphi 5 for Professionals / DELPHI5.iso / Info / Extras / NetManage / Demos / Nntp / main.pas < prev    next >
Encoding:
Pascal/Delphi Source File  |  1997-03-18  |  12.4 KB  |  499 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, ActiveX, isp3;
  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 NNTP1ProtocolStateChanged(Sender: TObject;
  43.       ProtocolState: Smallint);
  44.     procedure NNTP1StateChanged(Sender: TObject; State: Smallint);
  45.     procedure Exit1Click(Sender: TObject);
  46.     procedure MsgHeadersDblClick(Sender: TObject);
  47.     procedure FormClose(Sender: TObject; var Action: TCloseAction);
  48.     procedure NewsGroupsChange(Sender: TObject; Node: TTreeNode);
  49.     procedure RefreshBtnClick(Sender: TObject);
  50.     procedure FileDisconnectItemClick(Sender: TObject);
  51.     procedure NNTP1Banner(Sender: TObject; const Banner: WideString);
  52.     procedure NNTP1DocOutput(Sender: TObject; const DocOutput: DocOutput);
  53.     procedure NNTP1Error(Sender: TObject; Number: Smallint;
  54.       var Description: WideString; Scode: Integer; const Source,
  55.       HelpFile: WideString; HelpContext: Integer;
  56.       var CancelDisplay: WordBool);
  57.     procedure NNTP1SelectGroup(Sender: TObject;
  58.       const groupName: WideString; firstMessage, lastMessage,
  59.       msgCount: Integer);
  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.   public
  98.     constructor Create(const Text: string; Groups: Boolean);
  99.   end;
  100.  
  101. const
  102.   sfAllowSpaces = 1;
  103.  
  104. constructor TParser.Create(const Text: string; Groups: Boolean);
  105. begin
  106.   FText := Text;
  107.   FSourceLine := 1;
  108.   FSourcePtr := PChar(Text);
  109.   if Groups then
  110.     FFlags := sfAllowSpaces
  111.   else
  112.     FFlags := 0;
  113.   NextToken;
  114. end;
  115.  
  116. procedure TParser.SkipBlanks;
  117. begin
  118.   while True do
  119.   begin
  120.     case FSourcePtr^ of
  121.       #0:
  122.         begin
  123.           if FSourcePtr^ = #0 then Exit;
  124.           Continue;
  125.         end;
  126.       #10:
  127.         Inc(FSourceLine);
  128.       #33..#255:
  129.         Exit;
  130.     end;
  131.     Inc(FSourcePtr);
  132.   end;
  133. end;
  134.  
  135. procedure TParser.NextToken;
  136. var
  137.   P, TokenStart: PChar;
  138. begin
  139.   SkipBlanks;
  140.   FTokenString := '';
  141.   P := FSourcePtr;
  142.   while (P^ <> #0) and (P^ <= ' ') do Inc(P);
  143.   FTokenPtr := P;
  144.   case P^ of
  145.     '0'..'9':
  146.       begin
  147.         TokenStart := P;
  148.         Inc(P);
  149.         while P^ in ['0'..'9'] do Inc(P);
  150.         SetString(FTokenString, TokenStart, P - TokenStart);
  151.         FToken := etLiteral;
  152.       end;
  153.     #13: Inc(FSourceLine);
  154.     #0:
  155.       FToken := etEnd;
  156.   else
  157.     begin
  158.       TokenStart := P;
  159.       Inc(P);
  160.       if FFlags = sfAllowSpaces then
  161.         while not (P^ in [#0, #13, ' ']) do Inc(P)
  162.       else
  163.         while not (P^ in [#0, #13]) do Inc(P);
  164.       SetString(FTokenString, TokenStart, P - TokenStart);
  165.       FToken := etSymbol;
  166.     end;
  167.   end;
  168.   FSourcePtr := P;
  169. end;
  170.  
  171. function FirstItem(var ItemList: ShortString): ShortString;
  172. var
  173.   P: Integer;
  174. begin
  175.   P := AnsiPos('.', ItemList);
  176.   if P = 0 then
  177.   begin
  178.     Result := ItemList;
  179.     P := Length(ItemList);
  180.   end
  181.   else
  182.     Result := Copy(ItemList, 1, P - 1);
  183.   Delete(ItemList, 1, P);
  184. end;
  185.  
  186. procedure AddItem(GroupName: ShortString);
  187. var
  188.   Index, i: Integer;
  189.   Groups: Integer;
  190.   Item: ShortString;
  191.   TheNodes: TStringList;
  192. begin
  193.   Groups := 1;
  194.   for i := 0 to Length(GroupName) do
  195.     if GroupName[i] = '.' then
  196.       Inc(Groups);
  197.   TheNodes := Nodes;
  198.   for i := 0 to Groups - 1 do
  199.   begin
  200.     Item := FirstItem(GroupName);
  201.     Index := TheNodes.IndexOf(Item);
  202.     if Index = -1 then
  203.     begin
  204.       Index := TheNodes.AddObject(Item, TStringList.Create);
  205.       TheNodes := TStringList(TheNodes.Objects[Index]);
  206.       TheNodes.Sorted := True;
  207.     end
  208.     else
  209.       TheNodes := TStringList(TheNodes.Objects[Index]);
  210.   end;
  211.   Inc(GroupCount);
  212. end;
  213.  
  214. procedure ParseGroups(Data: String);
  215. var
  216.   Parser: TParser;
  217.   OldSrcLine: Integer;
  218. begin
  219.   Parser := TParser.Create(Data, True);
  220.   OldSrcLine := 0;
  221.   while Parser.FToken <> etEnd do
  222.   begin
  223.     if Parser.FSourceLine <> OldSrcLine then
  224.     begin
  225.       AddItem(Parser.FTokenString);
  226.       OldSrcLine := Parser.FSourceLine;
  227.     end;
  228.     Parser.NextToken;
  229.   end;
  230. end;
  231.  
  232. procedure ParseHeaders(Data: String);
  233. var
  234.   Parser: TParser;
  235.   MsgNo: LongInt;
  236.   Header: String;
  237.   OldSrcLine: Integer;
  238. begin
  239.   Parser := TParser.Create(Data, False);
  240.   while Parser.FToken <> etEnd do
  241.   begin
  242.     MsgNo := StrToInt(Parser.FTokenString);
  243.     OldSrcLine := Parser.FSourceLine;
  244.     Parser.NextToken;
  245.     Header := '';
  246.     while (OldSrcLine = Parser.FSourceLine) do
  247.     begin
  248.       Header := Header + ' ' + Parser.FTokenString;
  249.       Parser.NextToken;
  250.       if Parser.FToken = etEnd then
  251.         Break;
  252.     end;
  253.     NewsForm.MsgHeaders.Items.AddObject(Header, Pointer(MsgNo));
  254.   end;
  255. end;
  256.  
  257. procedure DestroyList(AList: TStringList);
  258. var
  259.   i: Integer;
  260. begin
  261.   for i := 0 to AList.Count - 1 do
  262.     if AList.Objects[i] <> nil then
  263.       DestroyList(TStringList(AList.Objects[i]));
  264.   AList.Free;
  265. end;
  266.  
  267. procedure BuildTree(Parent: TTreeNode; List: TStrings);
  268. var
  269.   i: Integer;
  270.   Node: TTreeNode;
  271. begin
  272.   for i := 0 to List.Count - 1 do
  273.     if List.Objects[i] <> nil then
  274.     begin
  275.       Node := NewsForm.NewsGroups.Items.AddChild(Parent, List[i]);
  276.       Node.ImageIndex := 0;
  277.       Node.SelectedIndex := 1;
  278.       BuildTree(Node, TStrings(List.Objects[i]));
  279.     end
  280.     else
  281.       NewsForm.NewsGroups.Items.AddChild(Parent, List[i]);
  282. end;
  283.  
  284. function TNewsForm.NodePath(Node: TTreeNode): String;
  285. begin
  286.   if Node.Parent = nil then
  287.     Result := Node.Text
  288.   else
  289.     Result := NodePath(Node.Parent) + '.' + Node.Text;
  290. end;
  291.  
  292. procedure TNewsForm.FileConnectItemClick(Sender: TObject);
  293. begin
  294.   ConnectDlg := TConnectDlg.Create(Self);
  295.   try
  296.     if ConnectDlg.ShowModal = mrOk then
  297.       with NNTP1 do
  298.         Connect(ConnectDlg.ServerEdit.Text, RemotePort);
  299.   finally
  300.     ConnectDlg.Free;
  301.   end;
  302. end;
  303.  
  304. procedure TNewsForm.NNTP1ProtocolStateChanged(Sender: TObject;
  305.   ProtocolState: Smallint);
  306. begin
  307.   case ProtocolState of
  308.     nntpBase: ;
  309.     nntpTransaction:
  310.       begin
  311.         EventFlag := efListGroups;
  312.         Nodes := TStringList.Create;
  313.         Nodes.Sorted := True;
  314.         NNTP1.ListGroups;
  315.       end;
  316.   end;
  317. end;
  318.  
  319. procedure TNewsForm.NNTP1StateChanged(Sender: TObject; State: Smallint);
  320. begin
  321.   with Memo1.Lines do
  322.     case NNTP1.State of
  323.       prcConnecting   : Add('Connecting');
  324.       prcResolvingHost: Add('Resolving Host: ' + NNTP1.RemoteHost);
  325.       prcHostResolved : Add('Host resolved');
  326.       prcConnected    :
  327.         begin
  328.           Add('Connected to: ' + NNTP1.RemoteHost);
  329.           Statusbar.Panels[0].Text := 'Connected to: ' + NNTP1.RemoteHost;
  330.           ConnectBtn.Enabled := False;
  331.           FileConnectItem.Enabled := False;
  332.           RefreshBtn.Enabled := True;
  333.         end;
  334.       prcDisconnecting: Text := NNTP1.ReplyString;
  335.       prcDisconnected :
  336.         begin
  337.           Statusbar.Panels[0].Text := 'Disconnected';
  338.           Caption := 'News Reader';
  339.           Label1.Caption := '';
  340.           ConnectBtn.Enabled := True;
  341.           FileConnectItem.Enabled := True;
  342.           RefreshBtn.Enabled := False;
  343.         end;
  344.     end;
  345. end;
  346.  
  347. procedure TNewsForm.Exit1Click(Sender: TObject);
  348. begin
  349.   if NNTP1.State <> prcDisconnected then
  350.   begin
  351.     if NNTP1.Busy then NNTP1.Cancel;
  352.     NNTP1.Quit;
  353.     while NNTP1.State <> prcDisconnected do
  354.       Application.ProcessMessages;
  355.   end;
  356.   Close;
  357. end;
  358.  
  359. procedure TNewsForm.MsgHeadersDblClick(Sender: TObject);
  360. var
  361.   Article: Integer;
  362. begin
  363.   if NNTP1.Busy then exit;
  364.   EventFlag := efGetArticle;
  365.   Memo1.Clear;
  366.   if MsgHeaders.ItemIndex = -1 then exit;
  367.   Caption := 'News Reader: ' + MsgHeaders.Items[MsgHeaders.ItemIndex];
  368.   Article := Integer(MsgHeaders.Items.Objects[MsgHeaders.ItemIndex]);
  369.   NNTP1.GetArticlebyArticleNumber(Article);
  370. end;
  371.  
  372. procedure TNewsForm.FormClose(Sender: TObject; var Action: TCloseAction);
  373. begin
  374.   if NNTP1.State <> prcDisconnected then
  375.   begin
  376.     if NNTP1.Busy then NNTP1.Cancel;  
  377.     NNTP1.Quit;
  378.     while NNTP1.State <> prcDisconnected do
  379.       Application.ProcessMessages;
  380.   end;
  381. end;
  382.  
  383. procedure TNewsForm.NewsGroupsChange(Sender: TObject; Node: TTreeNode);
  384. var
  385.   NP: String;
  386. begin
  387.   if (NNTP1.State = prcConnected) and not NNTP1.Busy then
  388.     with MsgHeaders do
  389.     begin
  390.       Items.BeginUpdate;
  391.       try
  392.         Items.Clear;
  393.         Memo1.Lines.Clear;
  394.         NP := NodePath(NewsGroups.Selected);
  395.         Statusbar.Panels[2].Text := 'Bytes: 0';
  396.         Statusbar.Panels[1].Text := '0 Article(s)';
  397.         if NNTP1.Busy then
  398.           NNTP1.Cancel;
  399.         NNTP1.SelectGroup(NP);
  400.         Label1.Caption := 'Contents of ''' + NP + '''';
  401.       finally
  402.         Items.EndUpdate;
  403.       end;
  404.     end;
  405. end;
  406.  
  407. procedure TNewsForm.RefreshBtnClick(Sender: TObject);
  408. begin
  409.   if NewsGroups.Selected <> nil then
  410.     NewsGroupsChange(nil, NewsGroups.Selected);
  411. end;
  412.  
  413. procedure TNewsForm.FileDisconnectItemClick(Sender: TObject);
  414. begin
  415.   if NNTP1.Busy then NNTP1.Cancel;
  416.   NNTP1.Quit;
  417.   while NNTP1.Busy do
  418.     Application.ProcessMessages;
  419.   with NewsGroups.Items do
  420.   begin
  421.     BeginUpdate;
  422.     Clear;
  423.     EndUpdate;
  424.   end;
  425.   MsgHeaders.Items.Clear;
  426.   Memo1.Lines.Clear;
  427. end;
  428.  
  429. procedure TNewsForm.NNTP1Banner(Sender: TObject; const Banner: WideString);
  430. begin
  431.   Memo1.Lines.Add(Banner);
  432. end;
  433.  
  434. procedure TNewsForm.NNTP1DocOutput(Sender: TObject;
  435.   const DocOutput: DocOutput);
  436. begin
  437.   Statusbar.Panels[2].Text := Format('Bytes: %d',[DocOutput.BytesTransferred]);
  438.   case DocOutput.State of
  439.     icDocBegin:
  440.       begin
  441.         if EventFlag = efListGroups then
  442.           Memo1.Lines.Add('Retrieving news groups...');
  443.         Data := '';
  444.         GroupCount := 0;
  445.       end;
  446.     icDocData:
  447.       begin
  448.         Data := Data + DocOutput.DataString;
  449.         if EventFlag = efGetArticle then
  450.           Memo1.Lines.Add(Data);
  451.       end;
  452.     icDocEnd:
  453.       begin
  454.         case EventFlag of
  455.           efListGroups:
  456.             begin
  457.               ParseGroups(Data);
  458.               Memo1.Lines.Add('Done.'#13#10'Building news group tree...');
  459.               NewsGroups.Items.BeginUpdate;
  460.               try
  461.                 BuildTree(nil, Nodes);
  462.                 DestroyList(Nodes);
  463.                 Statusbar.Panels[1].Text := Format('%d Groups',[GroupCount]);
  464.               finally
  465.                 NewsGroups.Items.EndUpdate;
  466.                 Memo1.Lines.Add('Done.');
  467.               end;
  468.             end;
  469.           efGetArticleHeaders: ParseHeaders(Data);
  470.           efGetArticle:
  471.             begin
  472.               Memo1.SelStart := 0;
  473.               SendMessage(Memo1.Handle, EM_ScrollCaret, 0, 0);
  474.             end;
  475.         end;
  476.         SetLength(Data, 0);
  477.       end;
  478.   end;
  479.   Refresh;
  480. end;
  481.  
  482. procedure TNewsForm.NNTP1Error(Sender: TObject; Number: Smallint;
  483.   var Description: WideString; Scode: Integer; const Source,
  484.   HelpFile: WideString; HelpContext: Integer; var CancelDisplay: WordBool);
  485. begin
  486. //  MessageDlg(Description, mtError, [mbOk], 0);
  487. end;
  488.  
  489. procedure TNewsForm.NNTP1SelectGroup(Sender: TObject;
  490.   const groupName: WideString; firstMessage, lastMessage,
  491.   msgCount: Integer);
  492. begin
  493.   EventFlag := efGetArticleHeaders;
  494.   Statusbar.Panels[1].Text := Format('%d Article(s)',[msgCount]);
  495.   NNTP1.GetArticleHeaders('subject', FirstMessage, lastMessage);
  496. end;
  497.  
  498. end.
  499.