IdGopher.pas 24 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569570571572573574575576577578579580581582583584585586587588589590591592593594595596597598599600601602603604605606607608609610611612613614615616617618619620621622623624625626627628629630631632633634635636637638639640641642643644645646647648649650651652653654655656657658659660661662663664665666667668669670671672673674675676677678679680681682683684685686687688689690691692693694695696697698699700701702703704705706707708709710711712713714715716717718719720721722723724
  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. procedure InitComponent; override;
  200. public
  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. procedure TIdGopher.InitComponent;
  221. begin
  222. inherited InitComponent;
  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. var
  351. LEnc: IIdTextEncoding;
  352. begin
  353. LEnc := IndyTextEncoding_8Bit;
  354. WriteStringToStream(ADestStream, APreviousData, LEnc{$IFDEF STRING_IS_ANSI}, LEnc{$ENDIF});
  355. BeginWork(wmRead,ExpectedLength);
  356. try
  357. IOHandler.Capture(ADestStream, '.', True); {Do not Localize}
  358. finally
  359. EndWork(wmRead);
  360. end; //try..finally
  361. end;
  362. procedure TIdGopher.ProcessFile ( ADestStream : TStream; APreviousData : String = ''; {Do not Localize}
  363. const ExpectedLength : Integer = 0);
  364. var
  365. LEnc: IIdTextEncoding;
  366. begin
  367. BeginWork(wmRead,ExpectedLength);
  368. try
  369. LEnc := IndyTextEncoding_8Bit;
  370. WriteStringToStream(ADestStream, APreviousData, LEnc{$IFDEF STRING_IS_ANSI}, LEnc{$ENDIF});
  371. IOHandler.ReadStream(ADestStream, -1, True);
  372. ADestStream.Position := 0;
  373. finally
  374. EndWork(wmRead);
  375. end;
  376. end;
  377. Function TIdGopher.Search(ASelector, AQuery : String) : TIdGopherMenu;
  378. begin
  379. Connect;
  380. try
  381. {Gopher does not give a greating}
  382. IOHandler.WriteLn ( ASelector + TAB + AQuery );
  383. Result := ProcessDirectory;
  384. finally
  385. Disconnect;
  386. end; {try .. finally .. end }
  387. end;
  388. procedure TIdGopher.GetFile (ASelector : String; ADestStream : TStream;
  389. IsGopherPlus : Boolean = False;
  390. AView: String = ''); {Do not Localize}
  391. var
  392. Reply : Char;
  393. LengthBytes : Integer; {legnth of the gopher items}
  394. begin
  395. Connect;
  396. try
  397. if not IsGopherPlus then
  398. begin
  399. IOHandler.WriteLn ( ASelector );
  400. ProcessFile ( ADestStream );
  401. end // if not IsGopherPlus then
  402. else
  403. begin
  404. {I hope that this drops the size attribute and that this will cause the
  405. Views to work, I'm not sure} {Do not Localize}
  406. AView := Trim ( Fetch ( AView, ':' ) ); {Do not Localize}
  407. IOHandler.WriteLn ( ASelector + TAB +'+'+ AView ); {Do not Localize}
  408. {We read only one byte from the peer}
  409. Reply := Char(IOHandler.ReadByte);
  410. {Get the additonal reply code for error or success}
  411. case Reply of
  412. '-' : begin {Do not Localize}
  413. {Get the length byte}
  414. IOHandler.ReadLn;
  415. ProcessGopherError;
  416. end; {-}
  417. {success - read file}
  418. '+' : begin {Do not Localize}
  419. {Get the length byte}
  420. LengthBytes := IndyStrToInt ( IOHandler.ReadLn );
  421. case LengthBytes of
  422. {dot terminated - probably a text file}
  423. -1 : ProcessTextFile ( ADestStream );
  424. {just read until I disconnect you}
  425. -2 : ProcessFile ( ADestStream );
  426. else
  427. ProcessFile ( ADestStream, '', LengthBytes); {Do not Localize}
  428. end; //case LengthBytes of
  429. end; {+}
  430. else
  431. begin
  432. ProcessFile ( ADestStream, Reply );
  433. end; //else ..case Reply of
  434. end; //case Reply of
  435. end; //else..if IsGopherPlus then
  436. finally
  437. Disconnect;
  438. end; {try .. finally .. end }
  439. end;
  440. function TIdGopher.GetMenu ( ASelector : String; IsGopherPlus : Boolean = False; AView : String = '' ) : {Do not Localize}
  441. TIdGopherMenu;
  442. var
  443. Reply : Char;
  444. LengthBytes : Integer; {legnth of the gopher items}
  445. begin
  446. Result := nil;
  447. Connect;
  448. try
  449. if not IsGopherPlus then
  450. begin
  451. IOHandler.WriteLn ( ASelector );
  452. Result := ProcessDirectory;
  453. end // if not IsGopherPlus then
  454. else
  455. begin
  456. {Gopher does not give a greating}
  457. IOHandler.WriteLn ( ASelector + TAB+'+' + AView ); {Do not Localize}
  458. {We read only one byte from the peer}
  459. Reply := Char(IOHandler.ReadByte);
  460. {Get the additonal reply code for error or success}
  461. case Reply of
  462. '-' : begin {Do not Localize}
  463. IOHandler.ReadLn;
  464. ProcessGopherError;
  465. end; {-}
  466. '+' : begin {Do not Localize}
  467. {Get the length byte}
  468. LengthBytes := IndyStrToInt ( IOHandler.ReadLn );
  469. Result := ProcessDirectory ('', LengthBytes ); {Do not Localize}
  470. end; {+}
  471. else
  472. begin
  473. Result := ProcessDirectory ( Reply );
  474. end; //else..case Reply of
  475. end; //case Reply of
  476. end; //if not IsGopherPlus then
  477. finally
  478. Disconnect;
  479. end; {try .. finally .. end }
  480. end;
  481. Function TIdGopher.GetExtendedMenu(ASelector, AView: String) : TIdGopherMenu;
  482. var
  483. Reply : Char;
  484. LengthBytes : Integer; {legnth of the gopher items}
  485. begin
  486. Result := nil;
  487. Connect; try
  488. {Gopher does not give a greating}
  489. IOHandler.WriteLn(ASelector + TAB + '$' + AView); {Do not Localize}
  490. {We read only one byte from the peer}
  491. Reply := Char(IOHandler.ReadByte);
  492. {Get the additonal reply code for error or success}
  493. case Reply of
  494. '-' : begin {Do not Localize}
  495. IOHandler.ReadLn;
  496. ProcessGopherError;
  497. end; {-}
  498. '+' : begin {Do not Localize}
  499. {Get the length byte}
  500. LengthBytes := IndyStrToInt ( IOHandler.ReadLn );
  501. Result := LoadExtendedDirectory( '', LengthBytes); {Do not Localize}
  502. end; {+}
  503. else
  504. Result := ProcessDirectory ( Reply );
  505. end; //case Reply of
  506. finally
  507. Disconnect;
  508. end; {try .. finally .. end }
  509. end;
  510. procedure TIdGopher.GetTextFile(ASelector: String; ADestStream: TStream;
  511. IsGopherPlus: Boolean; AView: String);
  512. var
  513. Reply : Char;
  514. LengthBytes : Integer; {length of the gopher items}
  515. begin
  516. Connect;
  517. try
  518. if not IsGopherPlus then
  519. begin
  520. IOHandler.WriteLn ( ASelector );
  521. ProcessTextFile ( ADestStream );
  522. end // if not IsGopherPlus then
  523. else
  524. begin
  525. {I hope that this drops the size attribute and that this will cause the
  526. Views to work, I'm not sure} {Do not Localize}
  527. AView := Trim ( Fetch ( AView, ':' ) ); {Do not Localize}
  528. IOHandler.WriteLn ( ASelector + TAB +'+'+ AView ); {Do not Localize}
  529. {We read only one byte from the peer}
  530. Reply := Char(IOHandler.ReadByte);
  531. {Get the additonal reply code for error or success}
  532. case Reply of
  533. '-' : begin {Do not Localize}
  534. {Get the length byte}
  535. IOHandler.ReadLn;
  536. ProcessGopherError;
  537. end; {-}
  538. {success - read file}
  539. '+' : begin {Do not Localize}
  540. {Get the length byte}
  541. LengthBytes := IndyStrToInt ( IOHandler.ReadLn );
  542. case LengthBytes of
  543. {dot terminated - probably a text file}
  544. -1 : ProcessTextFile ( ADestStream );
  545. {just read until I disconnect you}
  546. -2 : ProcessFile ( ADestStream );
  547. else
  548. ProcessTextFile ( ADestStream, '', LengthBytes); {Do not Localize}
  549. end; //case LengthBytes of
  550. end; {+}
  551. else
  552. begin
  553. ProcessTextFile ( ADestStream, Reply );
  554. end; //else ..case Reply of
  555. end; //case Reply of
  556. end; //else..if IsGopherPlus then
  557. finally
  558. Disconnect;
  559. end; {try .. finally .. end }
  560. end;
  561. { TIdGopherMenu }
  562. function TIdGopherMenu.Add: TIdGopherMenuItem;
  563. begin
  564. Result := TIdGopherMenuItem ( inherited Add );
  565. end;
  566. constructor TIdGopherMenu.Create;
  567. begin
  568. inherited Create ( TIdGopherMenuItem );
  569. end;
  570. function TIdGopherMenu.GetItem(Index: Integer): TIdGopherMenuItem;
  571. begin
  572. result := TIdGopherMenuItem( inherited Items [ index ] );
  573. end;
  574. procedure TIdGopherMenu.SetItem( Index: Integer;
  575. const Value: TIdGopherMenuItem );
  576. begin
  577. inherited SetItem ( Index, Value );
  578. end;
  579. { TIdGopherMenuItem }
  580. constructor TIdGopherMenuItem.Create(ACollection: TCollection);
  581. begin
  582. inherited Create(ACollection);
  583. FGopherBlock := TIdHeaderList.Create(QuotePlain);
  584. {we don't unfold or fold lines as headers in that block start with a space} {Do not Localize}
  585. FGopherBlock.UnfoldLines := False;
  586. FGopherBlock.FoldLines := False;
  587. FViews := TStringList.Create;
  588. FAbstract := TStringList.Create;
  589. FAsk := TIdHeaderList.Create(QuotePlain);
  590. fAdminEmail := TIdEMailAddressItem.Create ( nil );
  591. end;
  592. destructor TIdGopherMenuItem.Destroy;
  593. begin
  594. FreeAndNil ( fAdminEmail );
  595. FreeAndNil ( FAsk );
  596. FreeAndNil ( FAbstract );
  597. FreeAndNil ( FGopherBlock );
  598. FreeAndNil ( FViews );
  599. inherited Destroy;
  600. end;
  601. procedure TIdGopherMenuItem.DoneSettingInfoBlock;
  602. {These constants are for blocks we wish to obtain - don't change as they are
  603. part of Gopher+ protocol}
  604. const
  605. BlockTypes : Array [1..3] of String = ('+VIEWS', '+ABSTRACT', '+ASK'); {Do not Localize}
  606. var
  607. idx : Integer;
  608. line : String;
  609. Procedure ParseBlock ( Block : TStrings);
  610. {Put our the sublock in the Block TIdStrings and increment
  611. the pointer appropriatriately}
  612. begin
  613. Inc ( idx );
  614. while ( idx < FGopherBlock.Count ) and
  615. ( FGopherBlock [ idx ] [ 1 ] = ' ' ) do {Do not Localize}
  616. begin
  617. Block.Add ( TrimLeft ( FGopherBlock [ idx ] ) );
  618. Inc ( idx );
  619. end; //while
  620. {correct for incrementation in the main while loop}
  621. Dec ( idx );
  622. end;
  623. begin
  624. idx := 0;
  625. while ( idx < FGopherBlock.Count ) do
  626. begin
  627. Line := FGopherBlock [ idx ];
  628. Line := Fetch( Line, ':' ); {Do not Localize}
  629. case PosInStrArray ( Line, BlockTypes, False ) of
  630. {+VIEWS:}
  631. 0 : ParseBlock ( FViews );
  632. {+ABSTRACT:}
  633. 1 : ParseBlock ( FAbstract );
  634. {+ASK:}
  635. 2 : ParseBlock ( FAsk );
  636. end;
  637. Inc ( idx );
  638. end;
  639. fAdminEmail.Text := FGopherBlock.Values [ ' Admin' ]; {Do not Localize}
  640. end;
  641. function TIdGopherMenuItem.GetGeog: String;
  642. begin
  643. Result := FGopherBlock.Values [ ' Geog' ]; {Do not Localize}
  644. end;
  645. function TIdGopherMenuItem.GetLastModified: String;
  646. begin
  647. Result := FGopherBlock.Values [ ' Mod-Date' ]; {Do not Localize}
  648. end;
  649. function TIdGopherMenuItem.GetLocation: String;
  650. begin
  651. Result := FGopherBlock.Values [ ' Loc' ]; {Do not Localize}
  652. end;
  653. function TIdGopherMenuItem.GetOrganization: String;
  654. begin
  655. Result := FGopherBlock.Values [ ' Org' ]; {Do not Localize}
  656. end;
  657. end.