IdGopher.pas 24 KB

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