IdDICT.pas 11 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399
  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.8 10/26/2004 8:59:34 PM JPMugaas
  18. Updated with new TStrings references for more portability.
  19. Rev 1.7 2004.10.26 11:47:54 AM czhower
  20. Changes to fix a conflict with aliaser.
  21. Rev 1.6 7/6/2004 4:55:22 PM DSiders
  22. Corrected spelling of Challenge.
  23. Rev 1.5 6/11/2004 9:34:08 AM DSiders
  24. Added "Do not Localize" comments.
  25. Rev 1.4 6/11/2004 6:16:44 AM DSiders
  26. Corrected spelling in class names, properties, and methods.
  27. Rev 1.3 3/8/2004 10:08:48 AM JPMugaas
  28. IdDICT now compiles with new code. IdDICT now added to palette.
  29. Rev 1.2 3/5/2004 7:23:56 AM JPMugaas
  30. Fix for one server that does not send a feature list in the banner as RFC
  31. 2229 requires.
  32. Rev 1.1 3/4/2004 3:55:02 PM JPMugaas
  33. Untested work with SASL.
  34. Fixed a problem with multiple entries using default. If AGetAll is true, a
  35. "*" is used for all of the databases. "!" is for just the first database an
  36. entry is found in.
  37. Rev 1.0 3/4/2004 2:44:16 PM JPMugaas
  38. RFC 2229 DICT client. This is a preliminary version that was tested at
  39. dict.org
  40. }
  41. unit IdDICT;
  42. interface
  43. {$I IdCompilerDefines.inc}
  44. uses
  45. Classes,
  46. IdAssignedNumbers, IdComponent,
  47. IdDICTCommon, IdSASLCollection, IdTCPClient, IdTCPConnection;
  48. // TODO: MIME should be integrated into this.
  49. type
  50. TIdDICTAuthenticationType = (datDefault, datSASL);
  51. const
  52. DICT_AUTHDEF = datDefault;
  53. DEF_TRYMIME = False;
  54. type
  55. TIdDICT = class(TIdTCPClient)
  56. protected
  57. FTryMIME: Boolean;
  58. FAuthType : TIdDICTAuthenticationType;
  59. FSASLMechanisms : TIdSASLEntries;
  60. FServer : String;
  61. FClient : String;
  62. //feature negotiation stuff
  63. FCapabilities : TStrings;
  64. procedure InitComponent; override;
  65. function IsCapaSupported(const ACapa : String) : Boolean;
  66. procedure SetClient(const AValue : String);
  67. procedure InternalGetList(const ACmd : String; AENtries : TCollection);
  68. procedure InternalGetStrs(const ACmd : String; AStrs : TStrings);
  69. public
  70. {$IFDEF WORKAROUND_INLINE_CONSTRUCTORS}
  71. constructor Create(AOwner: TComponent); reintroduce; overload;
  72. {$ENDIF}
  73. destructor Destroy; override;
  74. procedure Connect; override;
  75. procedure DisconnectNotifyPeer; override;
  76. procedure GetDictInfo(const ADict : String; AResults : TStrings);
  77. procedure GetSvrInfo(AResults : TStrings);
  78. procedure GetDBList(ADB : TIdDBList);
  79. procedure GetStrategyList(AStrats : TIdStrategyList);
  80. procedure Define(const AWord, ADBName : String; AResults : TIdDefinitions); overload;
  81. procedure Define(const AWord : String; AResults : TIdDefinitions; const AGetAll : Boolean = True); overload;
  82. procedure Match(const AWord, ADBName, AStrat : String; AResults : TIdMatchList); overload;
  83. procedure Match(const AWord, AStrat : String; AResults : TIdMatchList; const AGetAll : Boolean = True); overload;
  84. procedure Match(const AWord : String; AResults : TIdMatchList; const AGetAll : Boolean = True); overload;
  85. property Capabilities : TStrings read FCapabilities;
  86. property Server : String read FServer;
  87. published
  88. property TryMIME : Boolean read FTryMIME write FTryMIME default DEF_TRYMIME;
  89. property Client : String read FClient write SetClient;
  90. property AuthType : TIdDICTAuthenticationType read FAuthType write FAuthType default DICT_AUTHDEF;
  91. property SASLMechanisms : TIdSASLEntries read FSASLMechanisms write FSASLMechanisms;
  92. property Port default IdPORT_DICT;
  93. property Username;
  94. property Password;
  95. end;
  96. implementation
  97. uses
  98. IdFIPS,
  99. IdGlobal, IdGlobalProtocols, IdHash, IdHashMessageDigest, SysUtils;
  100. const
  101. DEF_CLIENT_FMT = 'Indy Library %s'; {do not localize}
  102. { TIdDICT }
  103. {$IFDEF WORKAROUND_INLINE_CONSTRUCTORS}
  104. constructor TIdDICT.Create(AOwner: TComponent);
  105. begin
  106. inherited Create(AOwner);
  107. end;
  108. {$ENDIF}
  109. procedure TIdDICT.Connect;
  110. var
  111. LBuf : String;
  112. LFeat : String;
  113. s : String;
  114. LMD5: TIdHashMessageDigest5;
  115. begin
  116. LBuf := '';
  117. FCapabilities.Clear;
  118. FServer := '';
  119. try
  120. inherited Connect;
  121. IOHandler.DefStringEncoding := IndyTextEncoding_UTF8;
  122. GetResponse(220);
  123. if LastCmdResult.Text.Count > 0 then begin
  124. // 220 pan.alephnull.com dictd 1.8.0/rf on Linux 2.4.18-14 <auth.mime> <[email protected]>
  125. LBuf := LastCmdResult.Text[0];
  126. //server
  127. FServer := TrimRight(Fetch(LBuf,'<'));
  128. //feature negotiation
  129. LFeat := Fetch(LBuf,'>');
  130. //One server I tested with has no feature negotiation at all and it returns something
  131. //like this:
  132. //220 dict.org Ho Ngoc Duc's DICT server 2.2 <[email protected]>
  133. if (IndyPos('@',LFeat)=0) and (IndyPos('<',LBuf)>0) then begin
  134. BreakApart ( LFeat, '.', FCapabilities );
  135. end else begin
  136. LBuf := '<'+LFeat+'>';
  137. end;
  138. //LBuf is now for the APOP3 like Challenge
  139. LBuf := Trim(LBuf);
  140. end;
  141. SendCmd('CLIENT '+FClient); {do not localize}
  142. if FAuthType = datDefault then begin
  143. if IsCapaSupported('auth') then begin {do not localize}
  144. // RLebeau: why does this require FIPS?
  145. if GetFIPSMode and (FPassword <> '') and (FUserName <> '') then begin
  146. LMD5 := TIdHashMessageDigest5.Create;
  147. try
  148. S := LowerCase(LMD5.HashStringAsHex(LBuf+Password));
  149. finally
  150. LMD5.Free;
  151. end;//try
  152. SendCmd('AUTH ' + Username + ' ' + S, 230); {do not localize}
  153. end;
  154. end;
  155. end else begin
  156. FSASLMechanisms.LoginSASL('SASLAUTH', FHost, FPort, 'dict', ['230'], ['330'], Self, FCapabilities, ''); {do not localize}
  157. end;
  158. if FTryMIME and IsCapaSupported('MIME') then begin {do not localize}
  159. SendCmd('OPTION MIME'); {do not localize}
  160. end;
  161. except
  162. Disconnect(False);
  163. raise;
  164. end;
  165. end;
  166. procedure TIdDICT.Define(const AWord, ADBName : String; AResults : TIdDefinitions);
  167. var LDef : TIdDefinition;
  168. LBuf : String;
  169. begin
  170. AResults.BeginUpdate;
  171. try
  172. AResults.Clear;
  173. SendCmd('DEFINE '+ ADBName + ' ' + AWord); {do not localize}
  174. repeat
  175. if (LastCmdResult.NumericCode div 100) = 1 then begin
  176. //Good, we got a response
  177. LBuf := LastCmdResult.Text[0];
  178. case LastCmdResult.NumericCode of
  179. 151 :
  180. begin
  181. LDef := AResults.Add;
  182. //151 "Stuart" wn "WordNet (r) 2.0"
  183. IOHandler.Capture(LDef.Definition);
  184. //Word
  185. Fetch(LBuf,'"');
  186. LDef.Word := Fetch(LBuf,'"');
  187. //db Name
  188. Fetch(LBuf);
  189. LDef.DB.Name := Fetch(LBuf);
  190. //DB Description
  191. Fetch(LBuf,'"');
  192. LDef.DB.Desc := Fetch(LBuf,'"');
  193. end;
  194. 150 :
  195. begin
  196. // not sure what to do with the number
  197. //get the defintions
  198. end;
  199. end;
  200. Self.GetInternalResponse;
  201. end else begin
  202. Break;
  203. end;
  204. until False;
  205. finally
  206. AResults.EndUpdate;
  207. end;
  208. end;
  209. procedure TIdDICT.Define(const AWord : String; AResults : TIdDefinitions; const AGetAll : Boolean = True);
  210. begin
  211. if AGetAll then begin
  212. Define(AWord,'*',AResults);
  213. end else begin
  214. Define(AWord,'!',AResults);
  215. end;
  216. end;
  217. destructor TIdDICT.Destroy;
  218. begin
  219. FreeAndNil(FSASLMechanisms);
  220. FreeAndNil(FCapabilities);
  221. inherited Destroy;
  222. end;
  223. procedure TIdDICT.DisconnectNotifyPeer;
  224. begin
  225. inherited DisconnectNotifyPeer;
  226. SendCmd('QUIT', 221); {Do not Localize}
  227. end;
  228. procedure TIdDICT.GetDBList(ADB: TIdDBList);
  229. begin
  230. InternalGetList('SHOW DB', ADB); {do not localize}
  231. end;
  232. procedure TIdDICT.GetDictInfo(const ADict: String; AResults: TStrings);
  233. begin
  234. InternalGetStrs('SHOW INFO ' + ADict, AResults); {do not localize}
  235. end;
  236. procedure TIdDICT.GetStrategyList(AStrats: TIdStrategyList);
  237. begin
  238. InternalGetList('SHOW STRAT', AStrats); {do not localize}
  239. end;
  240. procedure TIdDICT.GetSvrInfo(AResults: TStrings);
  241. begin
  242. InternalGetStrs('SHOW SERVER', AResults); {do not localize}
  243. end;
  244. procedure TIdDICT.InitComponent;
  245. begin
  246. inherited InitComponent;
  247. FCapabilities := TStringList.create;
  248. FSASLMechanisms := TIdSASLEntries.Create(Self);
  249. FPort := IdPORT_DICT;
  250. FAuthType := DICT_AUTHDEF;
  251. FHost := 'dict.org'; {do not localize}
  252. FClient := IndyFormat(DEF_CLIENT_FMT, [gsIdVersion]);
  253. end;
  254. procedure TIdDICT.InternalGetList(const ACmd: String; AENtries: TCollection);
  255. var
  256. LEnt : TIdGeneric;
  257. LS : TStrings;
  258. i : Integer;
  259. s : String;
  260. begin
  261. AEntries.BeginUpdate;
  262. try
  263. AEntries.Clear;
  264. LS := TStringList.Create;
  265. try
  266. InternalGetStrs(ACmd,LS);
  267. for i := 0 to LS.Count - 1 do begin
  268. LEnt := AENtries.Add as TIdGeneric;
  269. s := LS[i];
  270. LEnt.Name := Fetch(s);
  271. Fetch(s, '"');
  272. LEnt.Desc := Fetch(s, '"');
  273. end;
  274. finally
  275. FreeAndNil(LS);
  276. end;
  277. finally
  278. AEntries.EndUpdate;
  279. end;
  280. end;
  281. procedure TIdDICT.InternalGetStrs(const ACmd: String; AStrs: TStrings);
  282. begin
  283. AStrs.BeginUpdate;
  284. try
  285. AStrs.Clear;
  286. SendCmd(ACmd);
  287. if (LastCmdResult.NumericCode div 100) = 1 then begin
  288. IOHandler.Capture(AStrs);
  289. GetInternalResponse;
  290. end;
  291. finally
  292. AStrs.EndUpdate;
  293. end;
  294. end;
  295. function TIdDICT.IsCapaSupported(const ACapa: String): Boolean;
  296. var
  297. i : Integer;
  298. begin
  299. Result := False;
  300. for i := 0 to FCapabilities.Count-1 do begin
  301. Result := TextIsSame(ACapa, FCapabilities[i]);
  302. if Result then begin
  303. Break;
  304. end;
  305. end;
  306. end;
  307. procedure TIdDICT.Match(const AWord, ADBName, AStrat: String;
  308. AResults: TIdMatchList);
  309. var
  310. LS : TStrings;
  311. i : Integer;
  312. s : String;
  313. LM : TIdMatchItem;
  314. begin
  315. AResults.BeginUpdate;
  316. try
  317. AResults.Clear;
  318. LS := TStringList.Create;
  319. try
  320. InternalGetStrs('MATCH '+ADBName+' '+AStrat+' '+AWord,LS); {do not localize}
  321. for i := 0 to LS.Count -1 do begin
  322. s := LS[i];
  323. LM := AResults.Add;
  324. LM.DB := Fetch(s);
  325. Fetch(s, '"');
  326. LM.Word := Fetch(s, '"');
  327. end;
  328. finally
  329. FreeAndNil(LS);
  330. end;
  331. finally
  332. AResults.EndUpdate;
  333. end;
  334. end;
  335. procedure TIdDICT.Match(const AWord, AStrat: String;
  336. AResults: TIdMatchList; const AGetAll: Boolean);
  337. begin
  338. if AGetAll then begin
  339. Match(AWord,'*','.',AResults);
  340. end else begin
  341. Match(AWord,'!','.',AResults);
  342. end;
  343. end;
  344. procedure TIdDICT.Match(const AWord: String; AResults: TIdMatchList;
  345. const AGetAll: Boolean);
  346. begin
  347. Match(AWord,'.',AResults,AGetAll);
  348. end;
  349. procedure TIdDICT.SetClient(const AValue: String);
  350. //RFC 2229 says that a CLIENT command should always be
  351. //sent immediately after connection.
  352. begin
  353. if AValue <> '' then begin
  354. FClient := AValue;
  355. end;
  356. end;
  357. end.