PageRenderTime 25ms CodeModel.GetById 20ms RepoModel.GetById 0ms app.codeStats 0ms

/units/indy/IdGopher.pas

http://github.com/rofl0r/KOL
Pascal | 560 lines | 388 code | 39 blank | 133 comment | 4 complexity | eb788cd67da3ac6346a2b78304ba4850 MD5 | raw file
  1. // 29-nov-2002
  2. unit IdGopher;
  3. interface
  4. uses KOL { ,
  5. Classes } ,KOLClasses,
  6. IdEMailAddress, IdGlobal,
  7. IdHeaderList, IdTCPClient;
  8. type
  9. TIdGopherMenuItem = object(TCollectionItem)
  10. protected
  11. FTitle: string;
  12. FItemType: Char;
  13. FSelector: string;
  14. FServer: string;
  15. FPort: Integer;
  16. FGopherPlusItem: Boolean;
  17. FGopherBlock: TIdHeaderList;
  18. FViews: PStrList;
  19. FURL: string;
  20. FAbstract: PStrList;
  21. FAsk: TIdHeaderList;
  22. fAdminEmail: TIdEMailAddressItem;
  23. function GetLastModified: string;
  24. function GetOrganization: string;
  25. function GetLocation: string;
  26. function GetGeog: string;
  27. public
  28. { constructor Create(ACollection: TCollection); override;
  29. } destructor Destroy;
  30. virtual; procedure DoneSettingInfoBlock; virtual;
  31. property Title: string read FTitle write FTitle;
  32. property ItemType: Char read FItemType write FItemType;
  33. property Selector: string read FSelector write FSelector;
  34. property Server: string read FServer write FServer;
  35. property Port: Integer read FPort write FPort;
  36. property GopherPlusItem: Boolean read FGopherPlusItem
  37. write FGopherPlusItem;
  38. property GopherBlock: TIdHeaderList read FGopherBlock;
  39. property URL: string read FURL;
  40. property Views: PStrList read FViews;
  41. property AAbstract: PStrList read FAbstract;
  42. property LastModified: string read GetLastModified;
  43. property AdminEMail: TIdEMailAddressItem read fAdminEmail;
  44. property Organization: string read GetOrganization;
  45. property Location: string read GetLocation;
  46. property Geog: string read GetGeog;
  47. property Ask: TIdHeaderList read FAsk;
  48. end;
  49. PIdGopherMenuItem=^TIdGopherMenuItem;
  50. function NewIdGopherMenuItem(ACollection: TCollection):PIdGopherMenuItem;
  51. type
  52. TIdGopherMenu = object(TCollection)
  53. protected
  54. function GetItem(Index: Integer): TIdGopherMenuItem;
  55. procedure SetItem(Index: Integer; const Value: TIdGopherMenuItem);
  56. public
  57. { constructor Create; }// reintroduce;
  58. function Add: TIdGopherMenuItem;
  59. property Items[Index: Integer]: TIdGopherMenuItem read GetItem
  60. write SetItem; default;
  61. end;
  62. PIdGopherMenu=^TIdGopherMenu;
  63. function NewIdGopherMenu:PIdGopherMenu;
  64. type
  65. TIdGopherMenuEvent = procedure(Sender: TObject;
  66. MenuItem: TIdGopherMenuItem) of object;
  67. TIdGopher = object(TIdTCPClient)
  68. private
  69. protected
  70. FOnMenuItem: TIdGopherMenuEvent;
  71. procedure DoMenu(MenuItem: TIdGopherMenuItem);
  72. procedure ProcessGopherError;
  73. function MenuItemFromString(stLine: string; Menu: TIdGopherMenu)
  74. : TIdGopherMenuItem;
  75. function ProcessDirectory(PreviousData: string = '';
  76. const ExpectedLength: Integer = 0): TIdGopherMenu;
  77. function LoadExtendedDirectory(PreviousData: string = '';
  78. const ExpectedLength: Integer = 0): TIdGopherMenu;
  79. procedure ProcessFile(ADestStream: TStream; APreviousData: string = '';
  80. const ExpectedLength: Integer = 0);
  81. procedure ProcessTextFile(ADestStream: TStream;
  82. APreviousData: string = ''; const ExpectedLength: Integer = 0);
  83. public
  84. // constructor Create(AOwner: TComponent); override;
  85. function GetMenu(ASelector: string; IsGopherPlus: Boolean = False; AView:
  86. string = ''):
  87. TIdGopherMenu;
  88. function Search(ASelector, AQuery: string): TIdGopherMenu;
  89. procedure GetFile(ASelector: string; ADestStream: TStream; IsGopherPlus:
  90. Boolean = False; AView: string = '');
  91. procedure GetTextFile(ASelector: string; ADestStream: TStream; IsGopherPlus:
  92. Boolean = False; AView: string = '');
  93. function GetExtendedMenu(ASelector: string; AView: string = ''):
  94. TIdGopherMenu;
  95. // published
  96. property OnMenuItem: TIdGopherMenuEvent read FOnMenuItem write FOnMenuItem;
  97. property Port default IdPORT_GOPHER;
  98. end;
  99. PIdGopher=^TIdGopher;
  100. function NewIdGopher(AOwner: PControl):PIdGopher;
  101. implementation
  102. uses
  103. IdComponent,{ IdException,}
  104. IdGopherConsts,
  105. IdTCPConnection,
  106. SysUtils;
  107. procedure WriteToStream(AStream: TStream; AString: string);
  108. begin
  109. if Length(AString) > 0 then
  110. AStream.Write(AString[1], Length(AString));
  111. end;
  112. function NewIdGopher(AOwner: PControl):PIdGopher;
  113. // constructor TIdGopher.Create(AOwner: TComponent);
  114. begin
  115. // inherited;
  116. New( Result, Create );
  117. with Result^ do
  118. begin
  119. // Port := IdPORT_GOPHER;
  120. end;
  121. end;
  122. procedure TIdGopher.DoMenu(MenuItem: TIdGopherMenuItem);
  123. begin
  124. // if Assigned(FOnMenuItem) then
  125. // FOnMenuItem(Self, MenuItem);
  126. end;
  127. procedure TIdGopher.ProcessGopherError;
  128. var
  129. ErrorNo: Integer;
  130. ErrMsg: string;
  131. begin
  132. ErrMsg := AllData;
  133. ErrorNo := StrToInt(Fetch(ErrMsg));
  134. // raise EIdProtocolReplyError.CreateError(ErrorNo, Copy(ErrMsg, 1, Length(ErrMsg)
  135. // - 5));
  136. end;
  137. function TIdGopher.MenuItemFromString(stLine: string;
  138. Menu: TIdGopherMenu): TIdGopherMenuItem;
  139. begin
  140. { stLine := Trim(stLine);
  141. if Assigned(Menu) then
  142. begin
  143. Result := Menu.Add;
  144. end
  145. else
  146. begin
  147. Result := TIdGopherMenuItem.Create(nil);
  148. end;
  149. Result.Title := IdGlobal.Fetch(stLine, TAB);
  150. if Length(Result.Title) > 0 then
  151. begin
  152. Result.ItemType := Result.Title[1];
  153. end
  154. else
  155. begin
  156. Result.ItemType := IdGopherItem_Error;
  157. end;
  158. Result.Title := Copy(Result.Title, 2, Length(Result.Title));
  159. Result.Selector := Fetch(stLine, TAB);
  160. Result.Server := Fetch(stLine, TAB);
  161. Result.Port := StrToInt(Fetch(stLine, TAB));
  162. stLine := Fetch(stLine, TAB);
  163. Result.GopherPlusItem := ((Length(stLine) > 0) and
  164. (stLine[1] = '+'));}
  165. end;
  166. function TIdGopher.LoadExtendedDirectory(PreviousData: string = '';
  167. const ExpectedLength: Integer = 0): TIdGopherMenu;
  168. var
  169. stLine: string;
  170. gmnu: TIdGopherMenuItem;
  171. begin
  172. { BeginWork(wmRead, ExpectedLength);
  173. try
  174. Result := TIdGopherMenu.Create;
  175. gmnu := nil;
  176. repeat
  177. stLine := PreviousData + ReadLn;
  178. PreviousData := '';
  179. if (stLine <> '.') then
  180. begin
  181. if (Copy(stLine, 1, Length(IdGopherPlusInfo)) = IdGopherPlusInfo) then
  182. begin
  183. if (gmnu <> nil) then
  184. begin
  185. gmnu.DoneSettingInfoBlock;
  186. DoMenu(gmnu);
  187. end;
  188. gmnu := MenuItemFromString(RightStr(stLine,
  189. Length(stLine) - Length(IdGopherPlusInfo)), Result);
  190. gmnu.GopherBlock.Add(stLine);
  191. end
  192. else
  193. begin
  194. if Assigned(gmnu) and (stLine <> '') then
  195. begin
  196. gmnu.GopherBlock.Add(stLine);
  197. end;
  198. end;
  199. end
  200. else
  201. begin
  202. if (gmnu <> nil) then
  203. begin
  204. DoMenu(gmnu);
  205. end;
  206. end;
  207. until (stLine = '.') or not Connected;
  208. finally EndWork(wmRead);
  209. end;}
  210. end;
  211. function TIdGopher.ProcessDirectory(PreviousData: string = '';
  212. const ExpectedLength: Integer = 0): TIdGopherMenu;
  213. var
  214. stLine: string;
  215. begin
  216. { BeginWork(wmRead, ExpectedLength);
  217. try
  218. Result := TIdGopherMenu.Create;
  219. repeat
  220. stLine := PreviousData + ReadLn;
  221. PreviousData := '';
  222. if (stLine <> '.') then
  223. begin
  224. DoMenu(MenuItemFromString(stLine, Result));
  225. end;
  226. until (stLine = '.') or not Connected;
  227. finally
  228. EndWork(wmRead);
  229. end;}
  230. end;
  231. procedure TIdGopher.ProcessTextFile(ADestStream: TStream; APreviousData: string =
  232. '';
  233. const ExpectedLength: Integer = 0);
  234. begin
  235. WriteToStream(ADestStream, APreviousData);
  236. BeginWork(wmRead, ExpectedLength);
  237. try
  238. // Capture(ADestStream, '.', True);
  239. finally
  240. EndWork(wmRead);
  241. end;
  242. end;
  243. procedure TIdGopher.ProcessFile(ADestStream: TStream; APreviousData: string =
  244. '';
  245. const ExpectedLength: Integer = 0);
  246. begin
  247. BeginWork(wmRead, ExpectedLength);
  248. try
  249. WriteToStream(ADestStream, APreviousData);
  250. ReadStream(ADestStream, -1, True);
  251. ADestStream.Position := 0;
  252. finally
  253. EndWork(wmRead);
  254. end;
  255. end;
  256. function TIdGopher.Search(ASelector, AQuery: string): TIdGopherMenu;
  257. begin
  258. Connect;
  259. try
  260. WriteLn(ASelector + TAB + AQuery);
  261. Result := ProcessDirectory;
  262. finally
  263. Disconnect;
  264. end;
  265. end;
  266. procedure TIdGopher.GetFile(ASelector: string; ADestStream: TStream;
  267. IsGopherPlus: Boolean = False;
  268. AView: string = '');
  269. var
  270. Reply: Char;
  271. LengthBytes: Integer;
  272. begin
  273. Connect;
  274. try
  275. if not IsGopherPlus then
  276. begin
  277. WriteLn(ASelector);
  278. ProcessFile(ADestStream);
  279. end
  280. else
  281. begin
  282. AView := Trim(Fetch(AView, ':'));
  283. WriteLn(ASelector + TAB + '+' + AView);
  284. ReadBuffer(Reply, 1);
  285. case Reply of
  286. '-':
  287. begin
  288. ReadLn;
  289. ProcessGopherError;
  290. end;
  291. '+':
  292. begin
  293. LengthBytes := StrToInt(ReadLn);
  294. case LengthBytes of
  295. -1: ProcessTextFile(ADestStream);
  296. -2: ProcessFile(ADestStream);
  297. else
  298. ProcessFile(ADestStream, '', LengthBytes);
  299. end;
  300. end;
  301. else
  302. begin
  303. ProcessFile(ADestStream, Reply);
  304. end;
  305. end;
  306. end;
  307. finally
  308. Disconnect;
  309. end;
  310. end;
  311. function TIdGopher.GetMenu(ASelector: string; IsGopherPlus: Boolean = False;
  312. AView: string = ''):
  313. TIdGopherMenu;
  314. var
  315. Reply: Char;
  316. LengthBytes: Integer;
  317. begin
  318. // Result := nil;
  319. Connect;
  320. try
  321. if not IsGopherPlus then
  322. begin
  323. WriteLn(ASelector);
  324. Result := ProcessDirectory;
  325. end
  326. else
  327. begin
  328. WriteLn(ASelector + TAB + '+' + AView);
  329. ReadBuffer(Reply, 1);
  330. case Reply of
  331. '-':
  332. begin
  333. ReadLn;
  334. ProcessGopherError;
  335. end;
  336. '+':
  337. begin
  338. LengthBytes := StrToInt(ReadLn);
  339. Result := ProcessDirectory('', LengthBytes);
  340. end;
  341. else
  342. begin
  343. Result := ProcessDirectory(Reply);
  344. end;
  345. end;
  346. end;
  347. finally
  348. Disconnect;
  349. end;
  350. end;
  351. function TIdGopher.GetExtendedMenu(ASelector, AView: string): TIdGopherMenu;
  352. var
  353. Reply: Char;
  354. LengthBytes: Integer;
  355. begin
  356. // Result := nil;
  357. Connect;
  358. try
  359. WriteLn(ASelector + TAB + '$' + AView);
  360. ReadBuffer(Reply, 1);
  361. case Reply of
  362. '-':
  363. begin
  364. ReadLn;
  365. ProcessGopherError;
  366. end;
  367. '+':
  368. begin
  369. LengthBytes := StrToInt(ReadLn);
  370. Result := LoadExtendedDirectory('', LengthBytes);
  371. end;
  372. else
  373. Result := ProcessDirectory(Reply);
  374. end;
  375. finally
  376. Disconnect;
  377. end;
  378. end;
  379. procedure TIdGopher.GetTextFile(ASelector: string; ADestStream: TStream;
  380. IsGopherPlus: Boolean; AView: string);
  381. var
  382. Reply: Char;
  383. LengthBytes: Integer;
  384. begin
  385. Connect;
  386. try
  387. if not IsGopherPlus then
  388. begin
  389. WriteLn(ASelector);
  390. ProcessTextFile(ADestStream);
  391. end
  392. else
  393. begin
  394. AView := Trim(Fetch(AView, ':'));
  395. WriteLn(ASelector + TAB + '+' + AView);
  396. ReadBuffer(Reply, 1);
  397. case Reply of
  398. '-':
  399. begin
  400. ReadLn;
  401. ProcessGopherError;
  402. end;
  403. '+':
  404. begin
  405. LengthBytes := StrToInt(ReadLn);
  406. case LengthBytes of
  407. -1: ProcessTextFile(ADestStream);
  408. -2: ProcessFile(ADestStream);
  409. else
  410. ProcessTextFile(ADestStream, '', LengthBytes);
  411. end;
  412. end;
  413. else
  414. begin
  415. ProcessTextFile(ADestStream, Reply);
  416. end;
  417. end;
  418. end;
  419. finally
  420. Disconnect;
  421. end;
  422. end;
  423. function TIdGopherMenu.Add: TIdGopherMenuItem;
  424. begin
  425. // Result := TIdGopherMenuItem(inherited Add);
  426. end;
  427. //constructor TIdGopherMenu.Create;
  428. function NewIdGopherMenu:PIdGopherMenu;
  429. begin
  430. New( Result, Create );
  431. // inherited Create(TIdGopherMenuItem);
  432. end;
  433. function TIdGopherMenu.GetItem(Index: Integer): TIdGopherMenuItem;
  434. begin
  435. // result := TIdGopherMenuItem(inherited Items[index]);
  436. end;
  437. procedure TIdGopherMenu.SetItem(Index: Integer;
  438. const Value: TIdGopherMenuItem);
  439. begin
  440. // inherited SetItem(Index, Value);
  441. end;
  442. //constructor TIdGopherMenuItem.Create(ACollection: TCollection);
  443. function NewIdGopherMenuItem(ACollection: TCollection):PIdGopherMenuItem;
  444. begin
  445. New( Result, Create );
  446. with Result^ do
  447. begin
  448. // inherited;
  449. { FGopherBlock := TIdHeaderList.Create;
  450. FGopherBlock.Sorted := False;
  451. FGopherBlock.Duplicates := dupAccept;
  452. FGopherBlock.UnfoldLines := False;
  453. FGopherBlock.FoldLines := False;
  454. FViews := PStrList.Create;
  455. FAbstract := PStrList.Create;
  456. FAsk := TIdHeaderList.Create;
  457. fAdminEmail := TIdEMailAddressItem.Create(nil);
  458. FAbstract.Sorted := False;}
  459. end;
  460. end;
  461. destructor TIdGopherMenuItem.Destroy;
  462. begin
  463. FreeAndNil(fAdminEmail);
  464. FreeAndNil(FAsk);
  465. FreeAndNil(FAbstract);
  466. FreeAndNil(FGopherBlock);
  467. FreeAndNil(FViews);
  468. inherited;
  469. end;
  470. procedure TIdGopherMenuItem.DoneSettingInfoBlock;
  471. const
  472. BlockTypes: array[1..3] of string = ('+VIEWS', '+ABSTRACT', '+ASK');
  473. var
  474. idx: Integer;
  475. line: string;
  476. procedure ParseBlock(Block: PStrList);
  477. begin
  478. { Inc(idx);
  479. while (idx < FGopherBlock.Count) and
  480. (FGopherBlock[idx][1] = ' ') do
  481. begin
  482. Block.Add(TrimLeft(FGopherBlock[idx]));
  483. Inc(idx);
  484. end;
  485. Dec(idx);}
  486. end;
  487. begin
  488. (* idx := 0;
  489. while (idx < FGopherBlock.Count) do
  490. begin
  491. Line := FGopherBlock[idx];
  492. Line := UpperCase(Fetch(Line, ':'));
  493. case PosInStrArray(Line, BlockTypes) of
  494. {+VIEWS:}
  495. 0: ParseBlock(FViews);
  496. {+ABSTRACT:}
  497. 1: ParseBlock(FAbstract);
  498. {+ASK:}
  499. 2: ParseBlock(FAsk);
  500. end;
  501. Inc(idx);
  502. end;
  503. fAdminEmail.Text := FGopherBlock.Values[' Admin'];*)
  504. end;
  505. function TIdGopherMenuItem.GetGeog: string;
  506. begin
  507. Result := FGopherBlock.Values[' Geog'];
  508. end;
  509. function TIdGopherMenuItem.GetLastModified: string;
  510. begin
  511. Result := FGopherBlock.Values[' Mod-Date'];
  512. end;
  513. function TIdGopherMenuItem.GetLocation: string;
  514. begin
  515. Result := FGopherBlock.Values[' Loc'];
  516. end;
  517. function TIdGopherMenuItem.GetOrganization: string;
  518. begin
  519. Result := FGopherBlock.Values[' Org'];
  520. end;
  521. end.