IdGopher.pas 23 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569570571572573574575576577578579580581582583584585586587588589590591592593594595596597598599600601602603604605606607608609610611612613614615616617618619620621622623624625626627628629630631632633634635636637638639640641642643644645646647648649650651652653654655656657658659660
  1. { $HDR$}
  2. {**********************************************************************}
  3. { Unit archived using Team Coherence }
  4. { Team Coherence is Copyright 2002 by Quality Software Components }
  5. { }
  6. { For further information / comments, visit our WEB site at }
  7. { http://www.TeamCoherence.com }
  8. {**********************************************************************}
  9. {}
  10. { $Log: 10171: IdGopher.pas
  11. {
  12. { Rev 1.0 2002.11.12 10:39:30 PM czhower
  13. }
  14. {*******************************************************}
  15. { }
  16. { Indy Gopher Client TIdGopher }
  17. { }
  18. { Copyright (C) 2000 Winshoes Working Group }
  19. { Started by J. Peter Mugaas }
  20. { April 20, 2000 }
  21. { }
  22. {*******************************************************}
  23. {
  24. 2000-June- 9 J. Peter Mugaas
  25. -adjusted the Gopher+ support so that line-unfolding is disabled in
  26. FGopherBlock. Many headers we use start with spaces
  27. -made the ASK block into a TIdHeaderList to facilitate use better. This does
  28. unfold lines
  29. 2000-May -24 J. Peter Mugaas
  30. -changed interface of file retrieval routines to so DestStream property does
  31. not have to even exist now.
  32. 2000-May -17 J. Peter Mugaas
  33. -Optimized the DoneSettingInfoBlock method in the TIdGopherMenuItem object
  34. -Added Ask property to the TIdGopherMenuItem
  35. 2000-May -13 J. Peter Mugaas
  36. -Chanded the event types and classes to be prefixed with Id.
  37. 2000-Apr.-28 J. Peter Mugaas
  38. -Added built in Gopher+ support
  39. 2000-Apr.-21 J. Peter Mugaas
  40. -Added the ability to receive a file
  41. -Restructured this component to make the code more reabible,
  42. facilitate processing, and improve object orientation
  43. 2000-Apr.-20 J. Peter Mugaas
  44. -Started this unit
  45. }
  46. unit IdGopher;
  47. interface
  48. uses
  49. Classes,
  50. IdAssignedNumbers,
  51. IdEMailAddress,
  52. IdHeaderList, IdTCPClient;
  53. type
  54. TIdGopherMenuItem = class ( TCollectionItem )
  55. protected
  56. FTitle : String;
  57. FItemType : Char;
  58. FSelector : String;
  59. FServer : String;
  60. FPort : Integer;
  61. FGopherPlusItem : Boolean;
  62. FGopherBlock : TIdHeaderList;
  63. FViews : TStringlist;
  64. FURL : String;
  65. FAbstract : TStringList;
  66. FAsk : TIdHeaderList;
  67. fAdminEmail : TIdEMailAddressItem;
  68. function GetLastModified : String;
  69. function GetOrganization : String;
  70. function GetLocation : String;
  71. function GetGeog : String;
  72. public
  73. constructor Create(ACollection: TCollection); override;
  74. destructor Destroy; override;
  75. {This procedure updates several internal variables and should be done when
  76. all data has been added}
  77. procedure DoneSettingInfoBlock; virtual;
  78. {This is the title for the gopher Menu item and should be displayed to the
  79. user}
  80. property Title : String read FTitle write FTitle;
  81. {This charactor indicates the type of Item that this is.
  82. Use this to determine what methods to call to get the item}
  83. property ItemType : Char read FItemType write FItemType;
  84. {This is the Selector you use to retreive the item}
  85. property Selector : String read FSelector write FSelector;
  86. {This is the server you connect to and request the item from. Set the host
  87. property to this when retrieving it}
  88. property Server : String read FServer write FServer;
  89. {This indicates the port you connect to in order to request the item. Set
  90. the port property to this value to get an item.}
  91. property Port : Integer read FPort write FPort;
  92. {This indicates if the item is on a Gopher+ server - you can use
  93. GetExtended Menues for menus}
  94. property GopherPlusItem : Boolean read FGopherPlusItem
  95. write FGopherPlusItem;
  96. {These items are only available if you use the GetExtendedMenu method}
  97. {This is the complete information block for this gopher+ item}
  98. property GopherBlock : TIdHeaderList read FGopherBlock;
  99. {URL listed at +URL: Section }
  100. property URL : String read FURL;
  101. {This is the Gopher Views available for the item. You can include this
  102. when requesting it}
  103. property Views : TStringList read FViews;
  104. {abstract of Gopher item - had to be AAbstract due to Pascal reserved word}
  105. {this is a summery of a particular item - e.g. "Read about our greate
  106. products"}
  107. property AAbstract : TStringList read FAbstract;
  108. {This is the date that the item was last modified}
  109. property LastModified : String read GetLastModified;
  110. {This is contact information for the adminst}
  111. property AdminEMail : TIdEMailAddressItem read fAdminEmail;
  112. {This is the organization running the server and
  113. is usually only found in the Root item}
  114. property Organization : String read GetOrganization;
  115. {This is the location where the Gopher is
  116. and is usually only found in the Root item}
  117. property Location : String read GetLocation;
  118. {This is the latitude longitude and longitude of the Gopher server
  119. and is usually only found in the Root item}
  120. property Geog : String read GetGeog;
  121. {This Gopher+ information is used for prmoting users for Query data}
  122. property Ask : TIdHeaderList read FAsk;
  123. end;
  124. TIdGopherMenu = class ( TCollection )
  125. protected
  126. function GetItem ( Index: Integer ) : TIdGopherMenuItem;
  127. procedure SetItem ( Index: Integer; const Value: TIdGopherMenuItem );
  128. public
  129. constructor Create; reintroduce;
  130. function Add: TIdGopherMenuItem;
  131. property Items [ Index: Integer ] : TIdGopherMenuItem read GetItem
  132. write SetItem; default;
  133. end;
  134. TIdGopherMenuEvent = procedure ( Sender : TObject;
  135. MenuItem : TIdGopherMenuItem ) of object;
  136. TIdGopher = class ( TIdTCPClient )
  137. private
  138. { Private declarations }
  139. protected
  140. { Protected declarations }
  141. FOnMenuItem : TIdGopherMenuEvent;
  142. {This triggers the menu item event}
  143. Procedure DoMenu ( MenuItem : TIdGopherMenuItem );
  144. {This fires an exception for Gopher+ errors}
  145. Procedure ProcessGopherError;
  146. {This takes parses a string and makes a Menu Item for it}
  147. Function MenuItemFromString ( stLine : String; Menu : TIdGopherMenu)
  148. : TIdGopherMenuItem;
  149. {Process the menu while we retreive it}
  150. Function ProcessDirectory ( PreviousData : String = ''; {Do not Localize}
  151. const ExpectedLength: Integer = 0) : TIdGopherMenu;
  152. {This processes extended Gopher Menues}
  153. Function LoadExtendedDirectory ( PreviousData : String = ''; {Do not Localize}
  154. const ExpectedLength: Integer = 0) : TIdGopherMenu;
  155. {This processes the file when we retreive it and puts it in ADestStream. }
  156. procedure ProcessFile ( ADestStream : TStream; APreviousData : String = ''; {Do not Localize}
  157. const ExpectedLength : Integer = 0);
  158. {For Gopher +, we call this routine when we get a -2 length which means,
  159. read until you see EOL+.+EOL}
  160. Procedure ProcessTextFile ( ADestStream : TStream;
  161. APreviousData: String = ''; const ExpectedLength: Integer = 0); {Do not Localize}
  162. public
  163. { Public declarations }
  164. constructor Create ( AOwner: TComponent ); override;
  165. Function GetMenu (ASelector : String; IsGopherPlus : Boolean = False; AView : String = '' ) : {Do not Localize}
  166. TIdGopherMenu;
  167. Function Search(ASelector, AQuery : String) : TIdGopherMenu;
  168. procedure GetFile (ASelector : String; ADestStream : TStream; IsGopherPlus : Boolean = False; AView: String = ''); {Do not Localize}
  169. procedure GetTextFile(ASelector : String; ADestStream : TStream; IsGopherPlus : Boolean = False; AView: String = ''); {Do not Localize}
  170. Function GetExtendedMenu (ASelector : String; AView: String = '' ) : TIdGopherMenu; {Do not Localize}
  171. published
  172. { Published declarations }
  173. property OnMenuItem : TIdGopherMenuEvent read FOnMenuItem write FOnMenuItem;
  174. property Port default IdPORT_Gopher;
  175. end;
  176. implementation
  177. uses
  178. IdComponent, IdException,
  179. IdGlobal, IdGopherConsts,
  180. IdTCPConnection,
  181. SysUtils;
  182. Procedure WriteToStream(AStream : TStream; AString : String);
  183. begin
  184. if Length(AString) > 0 then
  185. AStream.Write( AString [ 1 ], Length ( AString ) );
  186. end;
  187. { TIdGopher }
  188. constructor TIdGopher.Create ( AOwner: TComponent );
  189. begin
  190. inherited;
  191. Port := IdPORT_GOPHER;
  192. end;
  193. procedure TIdGopher.DoMenu(MenuItem: TIdGopherMenuItem);
  194. begin
  195. if Assigned( FOnMenuItem ) then
  196. FOnMenuItem( Self, MenuItem );
  197. end;
  198. procedure TIdGopher.ProcessGopherError;
  199. var ErrorNo : Integer;
  200. ErrMsg : String;
  201. begin
  202. ErrMsg := AllData;
  203. {Get the error number from the error reply line}
  204. ErrorNo := StrToInt ( Fetch ( ErrMsg ) );
  205. {we want to drop the CRLF+'.'+CRLF} {Do not Localize}
  206. raise EIdProtocolReplyError.CreateError(ErrorNo, Copy(ErrMsg, 1, Length(ErrMsg) - 5));
  207. end;
  208. function TIdGopher.MenuItemFromString(stLine: String;
  209. Menu: TIdGopherMenu): TIdGopherMenuItem;
  210. begin
  211. {just in case a space thows things off}
  212. stLine := Trim(stLine);
  213. if Assigned ( Menu ) then
  214. begin
  215. Result := Menu.Add;
  216. end // if Assigned ( Menu ) then
  217. else
  218. begin
  219. Result := TIdGopherMenuItem.Create( nil );
  220. end; // else .. if Assigned ( Menu ) then
  221. {title and Item Type}
  222. Result.Title := IdGlobal.Fetch ( stLine, TAB );
  223. if Length ( Result.Title ) > 0 then
  224. begin
  225. Result.ItemType := Result.Title [ 1 ];
  226. end //if Length.Result.Title > 0 then
  227. else
  228. begin
  229. Result.ItemType := IdGopherItem_Error;
  230. end; //else..if Length.Result.Title > 0 then
  231. {drop first charactor because that was the item type indicator}
  232. Result.Title := Copy ( Result.Title, 2, Length ( Result.Title ) );
  233. {selector string}
  234. Result.Selector := Fetch ( stLine, TAB );
  235. {server}
  236. Result.Server := Fetch ( stLine, TAB );
  237. {port}
  238. Result.Port := StrToInt ( Fetch ( stLine, TAB ) );
  239. {is Gopher + Item}
  240. stLine := Fetch ( stLine, TAB );
  241. Result.GopherPlusItem := ( (Length ( stLine) > 0 ) and
  242. ( stLine [ 1 ] = '+' ) ); {Do not Localize}
  243. end;
  244. Function TIdGopher.LoadExtendedDirectory ( PreviousData : String = ''; {Do not Localize}
  245. const ExpectedLength: Integer = 0) : TIdGopherMenu;
  246. var
  247. stLine : String;
  248. gmnu : TIdGopherMenuItem;
  249. begin
  250. BeginWork(wmRead, ExpectedLength); try
  251. Result := TIdGopherMenu.Create;
  252. gmnu := nil;
  253. repeat
  254. stLine := PreviousData + ReadLn;
  255. {we use the Previous data only ONCE}
  256. PreviousData := ''; {Do not Localize}
  257. {we process each line only if it is not the last and the
  258. OnMenuItem is assigned}
  259. if ( stLine <> '.' ) then {Do not Localize}
  260. begin
  261. {This is a new Extended Gopher menu so lets start it}
  262. if ( Copy (stLine, 1, Length ( IdGopherPlusInfo ) ) = IdGopherPlusInfo ) then
  263. begin
  264. {fire event for previous item}
  265. if (gmnu <> nil) then
  266. begin
  267. gmnu.DoneSettingInfoBlock;
  268. DoMenu ( gmnu );
  269. end; //if (gmnu <> nil) then
  270. gmnu := MenuItemFromString ( RightStr( stLine,
  271. Length ( stLine ) - Length ( IdGopherPlusInfo ) ) , Result );
  272. gmnu.GopherBlock.Add ( stLine);
  273. end //if (Pos(IdGopherGPlusInfo, stLine) = 0) then
  274. else
  275. begin
  276. if Assigned( gmnu ) and (stLine <> '') then {Do not Localize}
  277. begin
  278. gmnu.GopherBlock.Add ( stLine );
  279. end;
  280. end; //else...if (Pos(IdGopherGPlusInfo, stLine) = 0) then
  281. end //if not stLine = '.' then {Do not Localize}
  282. else
  283. begin
  284. {fire event for the last line}
  285. if (gmnu <> nil) then
  286. begin
  287. DoMenu ( gmnu );
  288. end; //if (gmnu <> nil) then
  289. end; //if ( stLine <> '.' ) then {Do not Localize}
  290. until (stLine = '.') or not Connected; {Do not Localize}
  291. finally EndWork(wmRead); end;
  292. end;
  293. Function TIdGopher.ProcessDirectory ( PreviousData : String = ''; {Do not Localize}
  294. const ExpectedLength: Integer = 0) : TIdGopherMenu;
  295. var stLine : String;
  296. begin
  297. BeginWork(wmRead,ExpectedLength); try
  298. Result := TIdGopherMenu.Create;
  299. repeat
  300. stLine := PreviousData + ReadLn;
  301. {we use the Previous data only ONCE}
  302. PreviousData := ''; {Do not Localize}
  303. {we process each line only if it is not the last and the OnMenuItem
  304. is assigned}
  305. if ( stLine <> '.' ) then {Do not Localize}
  306. begin
  307. //add Gopher Menu item and fire event
  308. DoMenu ( MenuItemFromString ( stLine, Result ) );
  309. end; //if not stLine = '.' then {Do not Localize}
  310. until (stLine = '.') or not Connected; {Do not Localize}
  311. finally
  312. EndWork(wmRead);
  313. end; //try..finally
  314. end;
  315. procedure TIdGopher.ProcessTextFile(ADestStream : TStream; APreviousData: String = ''; {Do not Localize}
  316. const ExpectedLength: Integer = 0);
  317. begin
  318. WriteToStream(ADestStream, APreviousData);
  319. BeginWork(wmRead,ExpectedLength);
  320. try
  321. Capture(ADestStream,'.',True); {Do not Localize}
  322. finally
  323. EndWork(wmRead);
  324. end; //try..finally
  325. end;
  326. procedure TIdGopher.ProcessFile ( ADestStream : TStream; APreviousData : String = ''; {Do not Localize}
  327. const ExpectedLength : Integer = 0);
  328. begin
  329. BeginWork(wmRead,ExpectedLength);
  330. try
  331. WriteToStream(ADestStream, APreviousData);
  332. ReadStream(ADestStream,-1,True);
  333. ADestStream.Position := 0;
  334. finally
  335. EndWork(wmRead);
  336. end;
  337. end;
  338. Function TIdGopher.Search(ASelector, AQuery : String) : TIdGopherMenu;
  339. begin
  340. Connect;
  341. try
  342. {Gopher does not give a greating}
  343. WriteLn ( ASelector + TAB + AQuery );
  344. Result := ProcessDirectory;
  345. finally
  346. Disconnect;
  347. end; {try .. finally .. end }
  348. end;
  349. procedure TIdGopher.GetFile (ASelector : String; ADestStream : TStream;
  350. IsGopherPlus : Boolean = False;
  351. AView: String = ''); {Do not Localize}
  352. var Reply : Char;
  353. LengthBytes : Integer; {legnth of the gopher items}
  354. begin
  355. Connect;
  356. try
  357. if not IsGopherPlus then
  358. begin
  359. WriteLn ( ASelector );
  360. ProcessFile ( ADestStream );
  361. end // if not IsGopherPlus then
  362. else
  363. begin
  364. {I hope that this drops the size attribute and that this will cause the
  365. Views to work, I'm not sure} {Do not Localize}
  366. AView := Trim ( Fetch ( AView, ':' ) ); {Do not Localize}
  367. WriteLn ( ASelector + TAB +'+'+ AView ); {Do not Localize}
  368. {We read only one byte from the peer}
  369. ReadBuffer( Reply, 1 );
  370. {Get the additonal reply code for error or success}
  371. case Reply of
  372. '-' : begin {Do not Localize}
  373. {Get the length byte}
  374. ReadLn;
  375. ProcessGopherError;
  376. end; {-}
  377. {success - read file}
  378. '+' : begin {Do not Localize}
  379. {Get the length byte}
  380. LengthBytes := StrToInt ( ReadLn );
  381. case LengthBytes of
  382. {dot terminated - probably a text file}
  383. -1 : ProcessTextFile ( ADestStream );
  384. {just read until I disconnect you}
  385. -2 : ProcessFile ( ADestStream );
  386. else
  387. ProcessFile ( ADestStream, '', LengthBytes); {Do not Localize}
  388. end; //case LengthBytes of
  389. end; {+}
  390. else
  391. begin
  392. ProcessFile ( ADestStream, Reply );
  393. end; //else ..case Reply of
  394. end; //case Reply of
  395. end; //else..if IsGopherPlus then
  396. finally
  397. Disconnect;
  398. end; {try .. finally .. end }
  399. end;
  400. function TIdGopher.GetMenu ( ASelector : String; IsGopherPlus : Boolean = False; AView : String = '' ) : {Do not Localize}
  401. TIdGopherMenu;
  402. var Reply : Char;
  403. LengthBytes : Integer; {legnth of the gopher items}
  404. begin
  405. Result := nil;
  406. Connect;
  407. try
  408. if not IsGopherPlus then
  409. begin
  410. WriteLn ( ASelector );
  411. Result := ProcessDirectory;
  412. end // if not IsGopherPlus then
  413. else
  414. begin
  415. {Gopher does not give a greating}
  416. WriteLn ( ASelector + TAB+'+' + AView ); {Do not Localize}
  417. {We read only one byte from the peer}
  418. ReadBuffer( Reply, 1 );
  419. {Get the additonal reply code for error or success}
  420. case Reply of
  421. '-' : begin {Do not Localize}
  422. ReadLn;
  423. ProcessGopherError;
  424. end; {-}
  425. '+' : begin {Do not Localize}
  426. {Get the length byte}
  427. LengthBytes := StrToInt ( ReadLn );
  428. Result := ProcessDirectory ('', LengthBytes ); {Do not Localize}
  429. end; {+}
  430. else
  431. begin
  432. Result := ProcessDirectory ( Reply );
  433. end; //else..case Reply of
  434. end; //case Reply of
  435. end; //if not IsGopherPlus then
  436. finally
  437. Disconnect;
  438. end; {try .. finally .. end }
  439. end;
  440. Function TIdGopher.GetExtendedMenu(ASelector, AView: String) : TIdGopherMenu;
  441. var
  442. Reply : Char;
  443. LengthBytes : Integer; {legnth of the gopher items}
  444. begin
  445. Result := nil;
  446. Connect; try
  447. {Gopher does not give a greating}
  448. WriteLn(ASelector + TAB + '$' + AView); {Do not Localize}
  449. {We read only one byte from the peer}
  450. ReadBuffer(Reply, 1);
  451. {Get the additonal reply code for error or success}
  452. case Reply of
  453. '-' : begin {Do not Localize}
  454. ReadLn;
  455. ProcessGopherError;
  456. end; {-}
  457. '+' : begin {Do not Localize}
  458. {Get the length byte}
  459. LengthBytes := StrToInt ( ReadLn );
  460. Result := LoadExtendedDirectory( '', LengthBytes); {Do not Localize}
  461. end; {+}
  462. else
  463. Result := ProcessDirectory ( Reply );
  464. end; //case Reply of
  465. finally
  466. Disconnect;
  467. end; {try .. finally .. end }
  468. end;
  469. procedure TIdGopher.GetTextFile(ASelector: String; ADestStream: TStream;
  470. IsGopherPlus: Boolean; AView: String);
  471. var Reply : Char;
  472. LengthBytes : Integer; {legnth of the gopher items}
  473. begin
  474. Connect;
  475. try
  476. if not IsGopherPlus then
  477. begin
  478. WriteLn ( ASelector );
  479. ProcessTextFile ( ADestStream );
  480. end // if not IsGopherPlus then
  481. else
  482. begin
  483. {I hope that this drops the size attribute and that this will cause the
  484. Views to work, I'm not sure} {Do not Localize}
  485. AView := Trim ( Fetch ( AView, ':' ) ); {Do not Localize}
  486. WriteLn ( ASelector + TAB +'+'+ AView ); {Do not Localize}
  487. {We read only one byte from the peer}
  488. ReadBuffer( Reply, 1 );
  489. {Get the additonal reply code for error or success}
  490. case Reply of
  491. '-' : begin {Do not Localize}
  492. {Get the length byte}
  493. ReadLn;
  494. ProcessGopherError;
  495. end; {-}
  496. {success - read file}
  497. '+' : begin {Do not Localize}
  498. {Get the length byte}
  499. LengthBytes := StrToInt ( ReadLn );
  500. case LengthBytes of
  501. {dot terminated - probably a text file}
  502. -1 : ProcessTextFile ( ADestStream );
  503. {just read until I disconnect you}
  504. -2 : ProcessFile ( ADestStream );
  505. else
  506. ProcessTextFile ( ADestStream, '', LengthBytes); {Do not Localize}
  507. end; //case LengthBytes of
  508. end; {+}
  509. else
  510. begin
  511. ProcessTextFile ( ADestStream, Reply );
  512. end; //else ..case Reply of
  513. end; //case Reply of
  514. end; //else..if IsGopherPlus then
  515. finally
  516. Disconnect;
  517. end; {try .. finally .. end }
  518. end;
  519. { TIdGopherMenu }
  520. function TIdGopherMenu.Add: TIdGopherMenuItem;
  521. begin
  522. Result := TIdGopherMenuItem ( inherited Add );
  523. end;
  524. constructor TIdGopherMenu.Create;
  525. begin
  526. inherited Create ( TIdGopherMenuItem );
  527. end;
  528. function TIdGopherMenu.GetItem(Index: Integer): TIdGopherMenuItem;
  529. begin
  530. result := TIdGopherMenuItem( inherited Items [ index ] );
  531. end;
  532. procedure TIdGopherMenu.SetItem( Index: Integer;
  533. const Value: TIdGopherMenuItem );
  534. begin
  535. inherited SetItem ( Index, Value );
  536. end;
  537. { TIdGopherMenuItem }
  538. constructor TIdGopherMenuItem.Create(ACollection: TCollection);
  539. begin
  540. inherited;
  541. FGopherBlock := TIdHeaderList.Create;
  542. FGopherBlock.Sorted := False;
  543. FGopherBlock.Duplicates := dupAccept;
  544. {we don't unfold or fold lines as headers in that block start with a space} {Do not Localize}
  545. FGopherBlock.UnfoldLines := False;
  546. FGopherBlock.FoldLines := False;
  547. FViews := TStringList.Create;
  548. FAbstract := TStringList.Create;
  549. FAsk := TIdHeaderList.Create;
  550. fAdminEmail := TIdEMailAddressItem.Create ( nil );
  551. FAbstract.Sorted := False;
  552. end;
  553. destructor TIdGopherMenuItem.Destroy;
  554. begin
  555. FreeAndNil ( fAdminEmail );
  556. FreeAndNil ( FAsk );
  557. FreeAndNil ( FAbstract );
  558. FreeAndNil ( FGopherBlock );
  559. FreeAndNil ( FViews );
  560. inherited;
  561. end;
  562. procedure TIdGopherMenuItem.DoneSettingInfoBlock;
  563. {These constants are for blocks we wish to obtain - don't change as they are
  564. part of Gopher+ protocol}
  565. const
  566. BlockTypes : Array [1..3] of String = ('+VIEWS', '+ABSTRACT', '+ASK'); {Do not Localize}
  567. var
  568. idx : Integer;
  569. line : String;
  570. Procedure ParseBlock ( Block : TStringList);
  571. {Put our the sublock in the Block TStrings and increment
  572. the pointer appropriatriately}
  573. begin
  574. Inc ( idx );
  575. while ( idx < FGopherBlock.Count ) and
  576. ( FGopherBlock [ idx ] [ 1 ] = ' ' ) do {Do not Localize}
  577. begin
  578. Block.Add ( TrimLeft ( FGopherBlock [ idx ] ) );
  579. Inc ( idx );
  580. end; //while
  581. {correct for incrementation in the main while loop}
  582. Dec ( idx );
  583. end;
  584. begin
  585. idx := 0;
  586. while ( idx < FGopherBlock.Count ) do
  587. begin
  588. Line := FGopherBlock [ idx ];
  589. Line := UpperCase ( Fetch( Line, ':' ) ); {Do not Localize}
  590. case PosInStrArray ( Line, BlockTypes ) of
  591. {+VIEWS:}
  592. 0 : ParseBlock ( FViews );
  593. {+ABSTRACT:}
  594. 1 : ParseBlock ( FAbstract );
  595. {+ASK:}
  596. 2 : ParseBlock ( FAsk );
  597. end; //case PosInStrArray ( Line, BlockTypes ) of
  598. Inc ( idx );
  599. end; //while ( idx < FGopherBlock.Count ) do
  600. fAdminEmail.Text := FGopherBlock.Values [ ' Admin' ]; {Do not Localize}
  601. end;
  602. function TIdGopherMenuItem.GetGeog: String;
  603. begin
  604. Result := FGopherBlock.Values [ ' Geog' ]; {Do not Localize}
  605. end;
  606. function TIdGopherMenuItem.GetLastModified: String;
  607. begin
  608. Result := FGopherBlock.Values [ ' Mod-Date' ]; {Do not Localize}
  609. end;
  610. function TIdGopherMenuItem.GetLocation: String;
  611. begin
  612. Result := FGopherBlock.Values [ ' Loc' ]; {Do not Localize}
  613. end;
  614. function TIdGopherMenuItem.GetOrganization: String;
  615. begin
  616. Result := FGopherBlock.Values [ ' Org' ]; {Do not Localize}
  617. end;
  618. end.