IdIMAP4ServerDemo.pas 25 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569570571572573574575576577578579580581582583584585586587588589590591592593594595596597598599600601602603604605606607608609610611612613614615616617618619620621622623624625626627628629630631632633634635636637638639640641642643644645646647648649650651652653654655656657658659660661662663664665666667668669670
  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: 57357: IdIMAP4ServerDemo.pas
  11. {
  12. { Rev 1.3 03/03/2004 01:17:28 CCostelloe
  13. { Yet another check-in as part of continuing development
  14. }
  15. {
  16. { Rev 1.2 01/03/2004 23:33:48 CCostelloe
  17. { Further check-in as part of continuing development
  18. }
  19. {
  20. { Rev 1.1 26/02/2004 02:12:32 CCostelloe
  21. { Intermediate check-in - about half the functions now work
  22. }
  23. {
  24. { Rev 1.0 24/02/2004 10:17:02 CCostelloe
  25. { Implements demo storage mechanism for TIdIMAP4Server on Windows.
  26. }
  27. unit IdIMAP4ServerDemo;
  28. {STATUS OF CODE: This is still in test (Alpha code). The following is a list
  29. of tested and untested functions. See note at the end re known limitations of
  30. testing. Note that functions to add and delete users will be added at a later
  31. date.
  32. Tested:
  33. LOGIN username admin
  34. LOGOUT
  35. LIST "" *
  36. NOOP
  37. CAPABILITY (needs work to return the relevant answers)
  38. SELECT inbox
  39. EXAMINE inbox
  40. CREATE newmailbox
  41. DELETE mailbox
  42. RENAME oldmailboxname newmailboxname
  43. SUBSCRIBE mailbox
  44. UNSUBSCRIBE mailbox
  45. LSUB "" *
  46. CLOSE
  47. CHECK
  48. STATUS
  49. [UID]COPY
  50. [UID]SEARCH [FROM|TO|CC|BCC|SUBJECT] text
  51. AddUser
  52. DeleteUser
  53. Not tested:
  54. APPEND
  55. EXPUNGE
  56. [UID]FETCH
  57. [UID]STORE
  58. NOTE: The functions listed in "Tested" work at least in those cases where
  59. they should succeed, but have not yet been tested for all error conditions,
  60. e.g. deleting a non-existent directory.
  61. They have only been tested where folder name parameters are passed as single
  62. words, e.g. 'C2 CREATE MYFOLDER'. They have not been tested for 'C2 CREATE
  63. "MYFOLDER"' or 'C2 CREATE "MY FOLDER"', which will probably require the
  64. insertion of statements like:
  65. LMailBoxName := StripQuotesIfNecessary(AMailBoxName);
  66. They have not been tested for all combinations of the connection state,
  67. TIdIMAP4PeerContext(ASender.Context).FConnectionState (some commands are
  68. only allowed in certain connection states).
  69. They have not been tested when applied to both read-write and read-only
  70. mailboxes.
  71. }
  72. {
  73. IMPLEMENTATION NOTES:
  74. This is a functioning IMAP server, which at worst gives you a sample IMAP
  75. server that you can tailor to your needs: just change the command handlers
  76. you wish to modify.
  77. This is filesystem specific, i.e. it will only work on Windows-type filesystem
  78. though some untested attempts have been made to make it run on Linux.
  79. The default behaviour uses a directory structure \imapmail\username\mailbox\
  80. into which you should populate it with emails (you could use a TIdPOP3 client
  81. program, or maybe a TIdIMAP4 client via its APPEND command to do this).
  82. The filenames in the directory correspond to unique sequentially-assigned
  83. numbers which serve as UIDs, e.g. 123.txt.
  84. Note: In practice, you should NEVER re-use a previously-assigned UID
  85. unless you have to, and then you must increment the UIDValidity value,
  86. BUT this implementation does not implement the UIDValidity property, it
  87. always uses 9999 as the UIDValidity.
  88. The next free UID is recorded by creating a file whose filename is that
  89. number, followed by .uid, e.g. 234.uid.
  90. If you populate a mailbox using APPEND, this methodology is implemented for you.
  91. If you fill the mailbox through some other method, implement this methodology.
  92. The filenames of the emails are their UID followed by .txt, e.g. 1234.txt.
  93. You can override the default root path of \imapmail by setting RootPath (it
  94. defaults to /var/imapmail in Linux).
  95. Note this code uses PathDelim instead of \ for cross-platform support.
  96. To support this default behaviour, two X commands could be added:
  97. X ADDUSER UserName Password
  98. X DELETEUSER UserName
  99. These would add or remove the corresponding directories to support that user. In
  100. practice, you would probably call these from your server program, but the X
  101. commands would allow an IMAP client to call them.
  102. WARNING: \Seen is not really implemented, would need permanent storage on disk
  103. between sessions to record which messages have been viewed.
  104. The \Seen flag is automatically set in the default behaviour when a mailbox
  105. is selected.
  106. Note BODY.PEEK[] maps to BODY[] because we don't really support \Seen.
  107. }
  108. interface
  109. {$IFDEF INDY100}
  110. {$I Core\IdCompilerDefines.inc}
  111. {$IFDEF DOTNET}
  112. {$WARN UNIT_PLATFORM OFF}
  113. {$WARN SYMBOL_PLATFORM OFF}
  114. {$ENDIF}
  115. {$ENDIF}
  116. uses
  117. {
  118. Classes, SysUtils,
  119. IdAssignedNumbers, IdContext, IdException, IdExplicitTLSClientServerBase, IdServerIOHandler, IdCmdTCPServer,
  120. IdCommandHandlers, IdGlobal, IdResourceStrings, IdSSL,
  121. IdTCPConnection, IdYarn,
  122. IdIMAP4, //For some defines like TIdIMAP4ConnectionState
  123. IdReply, IdReplyIMAP4;
  124. }
  125. Classes,
  126. IdImap4Server,
  127. IdAssignedNumbers,
  128. IdCmdTCPServer,
  129. IdContext,
  130. IdCommandHandlers,
  131. IdException,
  132. IdExplicitTLSClientServerBase,
  133. IdIMAP4, //For some defines like TIdIMAP4ConnectionState
  134. IdMailBox,
  135. IdMessage,
  136. IdReply,
  137. IdReplyIMAP4,
  138. IdTCPConnection,
  139. IdYarn;
  140. type
  141. { TIdIMAP4ServerDemo }
  142. TIdIMAP4ServerDemo = class(TIdIMAP4Server)
  143. private
  144. protected
  145. // Default mechanism handlers...
  146. function NameAndMailBoxToPath (ALoginName, AMailbox: string): string;
  147. function DoesImapMailBoxExist (ALoginName, AMailbox: string): Boolean;
  148. function CreateMailBox (ALoginName, AMailbox: string): Boolean;
  149. function DeleteMailBox (ALoginName, AMailbox: string): Boolean;
  150. function IsMailBoxOpen (ALoginName, AMailbox: string): Boolean;
  151. function SetupMailbox (ALoginName, AMailBoxName: string; AMailBox: TIdMailBox): Boolean;
  152. function GetNextFreeUID (ALoginName, AMailbox: string): string;
  153. function RenameMailBox (ALoginName, AOldMailboxName, ANewMailboxName: string): Boolean;
  154. function ListMailBox (ALoginName, AMailBoxName: string; var AMailBoxNames: TStringList; var AMailBoxFlags: TStringList): Boolean;
  155. function DeleteMessage (ALoginName, AMailbox: string; AMessage: TIdMessage): Boolean;
  156. function CopyMessage (ALoginName, ASourceMailBox, AMessageUID, ADestinationMailbox: string): Boolean;
  157. function GetMessageSize (ALoginName, AMailbox: string; AMessage: TIdMessage): integer;
  158. function GetMessageHeader (ALoginName, AMailbox: string; AMessage, ATargetMessage: TIdMessage): Boolean;
  159. function GetMessageRaw (ALoginName, AMailbox: string; AMessage: TIdMessage; ALines: TStringList): Boolean;
  160. function OpenMailBox (ASender: TIdCommand; AReadOnly: Boolean): Boolean;
  161. function UpdateNextFreeUID (ALoginName, AMailBoxName, ANewUIDNext: string): Boolean;
  162. function GetFileNameToWriteAppendMessage(ALoginName, AMailBoxName, AUID: string): string;
  163. //Internally used functions...
  164. procedure OutputCurrentMailboxStats (ASender: TIdCommand);
  165. function GetMailBoxes (ADir, AMailBoxName: string; var AMailBoxNames: TStringList; var AMailBoxFlags: TStringList): Boolean;
  166. function LoadMailBox (ALoginName, AMailBoxName: string; AMailBox: TIdMailBox): Boolean;
  167. procedure InitComponent ; override;
  168. procedure RecursivelyEmptyDir (ADir: string);
  169. public
  170. //The following would be used by a server for user management...
  171. function AddUser (ALoginName: string): Boolean;
  172. function DeleteUser (ALoginName: string): Boolean;
  173. published
  174. end;
  175. implementation
  176. uses
  177. IdGlobal,
  178. IdGlobalProtocols,
  179. IdMessageCollection,
  180. IdResourceStrings,
  181. IdResourceStringsProtocols,
  182. IdSSL,
  183. IdStream,
  184. Dialogs,
  185. Windows,
  186. SysUtils;
  187. procedure TIdIMAP4ServerDemo.InitComponent;
  188. begin
  189. inherited;
  190. OnDefMechDoesImapMailBoxExist := DoesImapMailBoxExist;
  191. OnDefMechCreateMailBox := CreateMailBox;
  192. OnDefMechDeleteMailBox := DeleteMailBox;
  193. OnDefMechIsMailBoxOpen := IsMailBoxOpen;
  194. OnDefMechSetupMailbox := SetupMailbox;
  195. OnDefMechNameAndMailBoxToPath := NameAndMailBoxToPath;
  196. OnDefMechGetNextFreeUID := GetNextFreeUID;
  197. OnDefMechRenameMailBox := RenameMailBox;
  198. OnDefMechListMailBox := ListMailBox;
  199. OnDefMechDeleteMessage := DeleteMessage;
  200. OnDefMechCopyMessage := CopyMessage;
  201. OnDefMechGetMessageSize := GetMessageSize;
  202. OnDefMechGetMessageHeader := GetMessageHeader;
  203. OnDefMechGetMessageRaw := GetMessageRaw;
  204. OnDefMechOpenMailBox := OpenMailBox;
  205. OnDefMechReinterpretParamAsMailBox := ReinterpretParamAsMailBox;
  206. OnDefMechUpdateNextFreeUID := UpdateNextFreeUID;
  207. OnDefMechGetFileNameToWriteAppendMessage := GetFileNameToWriteAppendMessage;
  208. end;
  209. function TIdIMAP4ServerDemo.AddUser(ALoginName: string): Boolean;
  210. var
  211. LDir: string;
  212. begin
  213. Result := False;
  214. //INBOX must always exist.
  215. LDir := NameAndMailBoxToPath(ALoginName, 'INBOX'); {Do not Localize}
  216. if DirectoryExists(LDir) = True then begin
  217. ShowMessage('User already exists (i.e. directory '+LDir+' exists)'); {Do not Localize}
  218. Exit;
  219. end;
  220. LDir := FRootPath;
  221. if LDir[Length(LDir)] <> PathDelim then begin
  222. LDir := LDir + PathDelim;
  223. end;
  224. LDir := LDir + ALoginName;
  225. if ForceDirectories(LDir) = False then begin
  226. ShowMessage('Failed to create users directory '+LDir); {Do not Localize}
  227. Exit;
  228. end;
  229. if CreateMailBox(ALoginName, 'INBOX') = True then begin {Do not Localize}
  230. ShowMessage('Successfully added user and created INBOX for '+ALoginName); {Do not Localize}
  231. Result := True;
  232. end else begin
  233. ShowMessage('Failed to create INBOX for '+ALoginName); {Do not Localize}
  234. Result := False;
  235. end;
  236. end;
  237. function TIdIMAP4ServerDemo.DeleteUser(ALoginName: string): Boolean;
  238. var
  239. LDir: string;
  240. begin
  241. Result := False;
  242. LDir := NameAndMailBoxToPath(ALoginName, 'INBOX'); {Do not Localize}
  243. if DirectoryExists(LDir) = False then begin
  244. ShowMessage('User does not exist (i.e. directory '+LDir+' does not exist)'); {Do not Localize}
  245. Exit;
  246. end;
  247. LDir := FRootPath;
  248. if LDir[Length(LDir)] <> PathDelim then begin
  249. LDir := LDir + PathDelim;
  250. end;
  251. LDir := LDir + ALoginName;
  252. RecursivelyEmptyDir(LDir);
  253. ShowMessage('Successfully deleted user '+ALoginName); {Do not Localize}
  254. Result := True;
  255. end;
  256. procedure TIdIMAP4ServerDemo.RecursivelyEmptyDir(ADir: string);
  257. var
  258. LRet: integer;
  259. LSrchRec: TSearchRec;
  260. begin
  261. //Empty the dir first...
  262. LRet := FindFirst(ADir+PathDelim+'*.*', faDirectory, LSrchRec); {Do not Localize}
  263. while LRet = 0 do begin
  264. if ((LSrchRec.Name <> '.') and (LSrchRec.Name <> '..')) then begin {Do not Localize}
  265. if (LSrchRec.Attr and faDirectory) <> 0 then begin
  266. RecursivelyEmptyDir(ADir+PathDelim+LSrchRec.Name);
  267. end else begin
  268. if DeleteFile(ADir+PathDelim+LSrchRec.Name) = False then begin
  269. ShowMessage('Unable to delete file '+ADir+PathDelim+LSrchRec.Name+' (is it in use?)');
  270. Exit;
  271. end;
  272. end;
  273. end;
  274. LRet := FindNext(LSrchRec);
  275. end;
  276. FindClose(LSrchRec);
  277. //Now delete it...
  278. if RemoveDir(ADir) = False then begin
  279. ShowMessage('Unable to delete directory '+ADir+' (is it in use?)');
  280. Exit;
  281. end;
  282. end;
  283. function TIdIMAP4ServerDemo.DoesImapMailBoxExist(ALoginName, AMailbox: string): Boolean;
  284. var
  285. LDir: string;
  286. begin
  287. LDir := NameAndMailBoxToPath(ALoginName, AMailbox);
  288. Result := DirectoryExists(LDir);
  289. end;
  290. function TIdIMAP4ServerDemo.CreateMailBox(ALoginName, AMailbox: string): Boolean;
  291. var
  292. LDir: string;
  293. begin
  294. Result := False;
  295. LDir := NameAndMailBoxToPath(ALoginName, AMailbox);
  296. if CreateDir(LDir) = False then begin
  297. Exit;
  298. end;
  299. //if FileCreate(LDir + PathDelim + '1.uid') = -1 then begin
  300. if CreateEmptyFile (LDir + PathDelim + '1.uid') = False then begin {Do not Localize}
  301. Exit;
  302. end;
  303. Result := True;
  304. end;
  305. function TIdIMAP4ServerDemo.DeleteMailBox(ALoginName, AMailbox: string): Boolean;
  306. var
  307. LDir: string;
  308. LRet: integer;
  309. LSrchRec: TSearchRec;
  310. begin
  311. Result := False;
  312. LDir := NameAndMailBoxToPath(ALoginName, AMailbox);
  313. //Empty the dir first...
  314. LRet := FindFirst(LDir+PathDelim+'*.*', 0, LSrchRec); {Do not Localize}
  315. while LRet = 0 do begin
  316. if ((LSrchRec.Name <> '.') and (LSrchRec.Name <> '..')) then begin {Do not Localize}
  317. if DeleteFile(LDir+PathDelim+LSrchRec.Name) = False then begin
  318. Exit;
  319. end;
  320. end;
  321. LRet := FindNext(LSrchRec);
  322. end;
  323. FindClose(LSrchRec);
  324. //Now delete it...
  325. if RemoveDir(LDir) = False then begin
  326. Exit;
  327. end;
  328. Result := True;
  329. end;
  330. function TIdIMAP4ServerDemo.IsMailBoxOpen(ALoginName, AMailbox: string): Boolean;
  331. begin
  332. {You don't need to implement this if only one client will be connecting at any
  333. one time.
  334. One way to implement this is (a) in DoSelectMailbox, create a dummy file in the
  335. mailbox directory and delete it when you close the mailbox, and (b) in
  336. this routine, see if that file exists.}
  337. Result := False;
  338. end;
  339. function TIdIMAP4ServerDemo.SetupMailbox(ALoginName, AMailBoxName: string; AMailBox: TIdMailBox): Boolean;
  340. begin
  341. {The sample default mechanism has the messages stored with the UID as the
  342. filename. This also will set up the uid file if not present.}
  343. AMailBox.Clear;
  344. AMailBox.Name := AMailBoxName;
  345. LoadMailBox(ALoginName, AMailBoxName, AMailBox);
  346. AMailBox.TotalMsgs := AMailBox.MessageList.Count;
  347. AMailBox.UIDValidity := '9999'; //We don't maintain this {Do not Localize}
  348. AMailBox.UIDNext := GetNextFreeUID(ALoginName, AMailBoxName);
  349. Result := True;
  350. end;
  351. function TIdIMAP4ServerDemo.NameAndMailBoxToPath(ALoginName, AMailbox: string): string;
  352. //if AMailbox is '', we are really only checking if the user's dir exists...
  353. var
  354. LDir: string;
  355. LN: integer;
  356. LMailBox: string;
  357. begin
  358. LDir := FRootPath;
  359. if LDir[Length(LDir)] <> PathDelim then begin
  360. LDir := LDir + PathDelim;
  361. end;
  362. LDir := LDir + ALoginName;
  363. LMailBox := StripQuotesIfNecessary(AMailbox);
  364. if LMailbox <> '' then begin
  365. //Must replace mailbox delims with path delims...
  366. for LN := 1 to Length(LMailbox) do begin
  367. if LMailbox[LN] = MailBoxSeparator then begin
  368. LMailbox[LN] := PathDelim;
  369. end;
  370. end;
  371. LDir := LDir + PathDelim + LMailbox;
  372. end;
  373. Result := LDir;
  374. end;
  375. function TIdIMAP4ServerDemo.RenameMailBox(ALoginName, AOldMailboxName, ANewMailboxName: string): Boolean;
  376. var
  377. LDirOld: string;
  378. LDirNew: string;
  379. begin
  380. Result := False;
  381. LDirOld := NameAndMailBoxToPath(ALoginName, AOldMailboxName);
  382. LDirNew := NameAndMailBoxToPath(ALoginName, ANewMailboxName);
  383. if RenameFile(LDirOld, LDirNew) = False then begin
  384. Exit;
  385. end;
  386. Result := True;
  387. end;
  388. function TIdIMAP4ServerDemo.ListMailBox(ALoginName, AMailBoxName: string; var AMailBoxNames: TStringList; var AMailBoxFlags: TStringList): Boolean;
  389. var
  390. LDir: string;
  391. begin
  392. AMailBoxNames.Clear;
  393. AMailBoxFlags.Clear;
  394. LDir := NameAndMailBoxToPath(ALoginName, AMailBoxName);
  395. GetMailBoxes(LDir, AMailBoxName, AMailBoxNames, AMailBoxFlags);
  396. Result := True;
  397. end;
  398. function TIdIMAP4ServerDemo.DeleteMessage(ALoginName, AMailbox: string; AMessage: TIdMessage): Boolean;
  399. var
  400. LFile: string;
  401. begin
  402. LFile := NameAndMailBoxToPath(ALoginName, AMailbox) + PathDelim + AMessage.UID + '.txt'; {Do not Localize}
  403. Result := DeleteFile(LFile);
  404. end;
  405. function TIdIMAP4ServerDemo.CopyMessage(ALoginName, ASourceMailBox, AMessageUID, ADestinationMailbox: string): Boolean;
  406. //Note the destination mailbox is NEVER the currently-selected mailbox.
  407. var
  408. LSourceFile: string;
  409. LDestFile: string;
  410. LNewUID: string;
  411. begin
  412. Result := False;
  413. LSourceFile := NameAndMailBoxToPath(ALoginName, ASourceMailBox) + PathDelim + AMessageUID + '.txt'; {Do not Localize}
  414. //We need the next free UID in the destination dir...
  415. LNewUID := GetNextFreeUID(ALoginName, ADestinationMailBox);
  416. LDestFile := NameAndMailBoxToPath(ALoginName, ADestinationMailBox) + PathDelim + LNewUID + '.txt'; {Do not Localize}
  417. if IndyCopyFile(LSourceFile, LDestFile, True) = False then begin
  418. Exit;
  419. end;
  420. Result := UpdateNextFreeUID(ALoginName, ADestinationMailBox, IntToStr(StrToInt(LNewUID)+1));
  421. end;
  422. function TIdIMAP4ServerDemo.GetMessageSize(ALoginName, AMailbox: string; AMessage: TIdMessage): integer;
  423. //Return message size, or -1 on error.
  424. var
  425. LFile: string;
  426. LRet: integer;
  427. LSrchRec: TSearchRec;
  428. begin
  429. LFile := NameAndMailBoxToPath(ALoginName, AMailbox) + PathDelim + AMessage.UID + '.txt'; {Do not Localize}
  430. LRet := FindFirst(LFile, {FileAttrs} 0, LSrchRec);
  431. if LRet = 0 then begin
  432. Result := LSrchRec.Size;
  433. FindClose(LSrchRec);
  434. Exit;
  435. end;
  436. FindClose(LSrchRec);
  437. Result := -1;
  438. end;
  439. function TIdIMAP4ServerDemo.GetMessageHeader(ALoginName, AMailbox: string; AMessage, ATargetMessage: TIdMessage): Boolean;
  440. //We don't want to thrash UIDs and flags in AMessage, so load into ATargetMessage
  441. var
  442. LFile: string;
  443. begin
  444. LFile := NameAndMailBoxToPath(ALoginName, AMailbox) + PathDelim + AMessage.UID + '.txt'; {Do not Localize}
  445. ATargetMessage.LoadFromFile(LFile, True);
  446. Result := True;
  447. end;
  448. function TIdIMAP4ServerDemo.GetMessageRaw(ALoginName, AMailbox: string; AMessage: TIdMessage; ALines: TStringList): Boolean;
  449. var
  450. LFile: string;
  451. begin
  452. LFile := NameAndMailBoxToPath(ALoginName, AMailbox) + PathDelim + AMessage.UID + '.txt'; {Do not Localize}
  453. ALines.Clear;
  454. ALines.LoadFromFile(LFile);
  455. Result := True;
  456. end;
  457. //######### INTERNALLY USED FUNCTIONS #########
  458. function TIdIMAP4ServerDemo.LoadMailBox(ALoginName, AMailBoxName: string; AMailBox: TIdMailBox): Boolean;
  459. //This does the initial loading of a mailbox: it adds (empty) messages for every
  460. //message that exists in the mailbox and sets the UID of each message.
  461. //Because it does not really support \Seen (which would require disk storage of
  462. //the flags across sessions), it ALWAYS sets the \Seen flag.
  463. var
  464. LRet: integer;
  465. LSrchRec: TSearchRec;
  466. LDir: string;
  467. LMsgItem : TIdMessageItem;
  468. LName: string;
  469. begin
  470. LDir := NameAndMailBoxToPath(ALoginName, AMailBoxName)+PathDelim;
  471. LRet := FindFirst(LDir+'*.txt', {FileAttrs} 0, LSrchRec); {Do not Localize}
  472. while LRet = 0 do begin
  473. //Extract the UID from the filename...
  474. LName := ChangeFileExt(LSrchRec.Name, '');
  475. LMsgItem := AMailBox.MessageList.Add;
  476. LMsgItem.IdMessage.UID := LName;
  477. LMsgItem.IdMessage.Flags := [mfSeen];
  478. LRet := FindNext(LSrchRec);
  479. end;
  480. FindClose(LSrchRec);
  481. AMailBox.TotalMsgs := AMailBox.MessageList.Count;
  482. Result := True;
  483. end;
  484. procedure TIdIMAP4ServerDemo.OutputCurrentMailboxStats(ASender: TIdCommand);
  485. begin
  486. DoSendReply(ASender.Context, '* FLAGS (\Answered \Flagged \Draft \Deleted \Seen)'); {Do not Localize}
  487. DoSendReply(ASender.Context, '* OK [PERMANENTFLAGS (\Answered \Flagged \Draft \Deleted \Seen)]'); {Do not Localize}
  488. DoSendReply(ASender.Context, '* '+IntToStr(TIdIMAP4PeerContext(ASender.Context).MailBox.TotalMsgs)+' EXISTS'); {Do not Localize}
  489. DoSendReply(ASender.Context, '* '+IntToStr(TIdIMAP4PeerContext(ASender.Context).MailBox.RecentMsgs)+' RECENT'); {Do not Localize}
  490. DoSendReply(ASender.Context, '* OK [UNSEEN '+IntToStr(TIdIMAP4PeerContext(ASender.Context).MailBox.UnseenMsgs)+']'); {Do not Localize}
  491. DoSendReply(ASender.Context, '* OK [UIDVALIDITY '+TIdIMAP4PeerContext(ASender.Context).MailBox.UIDValidity+']'); {Do not Localize}
  492. DoSendReply(ASender.Context, '* OK [UIDNEXT '+TIdIMAP4PeerContext(ASender.Context).MailBox.UIDNext+']'); {Do not Localize}
  493. end;
  494. function TIdIMAP4ServerDemo.GetMailBoxes(ADir, AMailBoxName: string; var AMailBoxNames: TStringList; var AMailBoxFlags: TStringList): Boolean;
  495. //Return True if this MailBox has SubMailBoxes
  496. var
  497. LRet: integer;
  498. LSrchRec: TSearchRec;
  499. LTemp: string;
  500. LDoesMailBoxHaveSubMailBoxes: Boolean;
  501. LMailBoxName: string;
  502. begin
  503. Result := False;
  504. LMailBoxName := StripQuotesIfNecessary(AMailBoxName);
  505. LRet := FindFirst(ADir+PathDelim+'*.*', faDirectory, LSrchRec); {Do not Localize}
  506. while LRet = 0 do begin
  507. if (LSrchRec.Attr and faDirectory) <> 0 then begin
  508. //It is a directory...
  509. if ((LSrchRec.Name <> '.') and (LSrchRec.Name <> '..')) then begin {Do not Localize}
  510. Result := True; //Got at least one SubMailBox
  511. LTemp := '';
  512. if LMailBoxName <> '' then begin
  513. LTemp := LMailBoxName + MailBoxSeparator;
  514. end;
  515. LTemp := LTemp + LSrchRec.Name;
  516. LDoesMailBoxHaveSubMailBoxes := GetMailBoxes(ADir+PathDelim+LSrchRec.Name, LTemp, AMailBoxNames, AMailBoxFlags);
  517. AMailBoxNames.Add(LTemp);
  518. if LDoesMailBoxHaveSubMailBoxes = True then begin
  519. AMailBoxFlags.Add('\HasChildren'); {Do not Localize}
  520. end else begin
  521. AMailBoxFlags.Add('\HasNoChildren'); {Do not Localize}
  522. end;
  523. end;
  524. end;
  525. LRet := FindNext(LSrchRec);
  526. end;
  527. FindClose(LSrchRec);
  528. end;
  529. function TIdIMAP4ServerDemo.GetNextFreeUID(ALoginName, AMailbox: string): string;
  530. var
  531. LLargestUIDInUse: Integer;
  532. LRet: integer;
  533. LSrchRec: TSearchRec;
  534. LDir: string;
  535. LName: string;
  536. begin
  537. //Find (or set) the next free
  538. LDir := NameAndMailBoxToPath(ALoginName, AMailbox)+PathDelim;
  539. LRet := FindFirst(LDir+'*.uid', {FileAttrs} 0, LSrchRec); {Do not Localize}
  540. if LRet = 0 then begin
  541. LName := ChangeFileExt(LSrchRec.Name, '');
  542. Result := LName;
  543. Exit;
  544. end;
  545. FindClose(LSrchRec);
  546. //There is no .uid file present, so set one up (happens, for example,
  547. //with newly-created mailboxes)...
  548. LLargestUIDInUse := 0;
  549. LRet := FindFirst(LDir+'*.txt', {FileAttrs} 0, LSrchRec); {Do not Localize}
  550. while LRet = 0 do begin
  551. //Extract the UID from the filename...
  552. LName := ChangeFileExt(LSrchRec.Name, '');
  553. if StrToInt(LName) > LLargestUIDInUse then begin
  554. LLargestUIDInUse := StrToInt(LName);
  555. end;
  556. LRet := FindNext(LSrchRec);
  557. end;
  558. FindClose(LSrchRec);
  559. FileCreate(LDir + IntToStr(LLargestUIDInUse+1) + '.uid'); {Do not Localize}
  560. Result := IntToStr(LLargestUIDInUse+1);
  561. end;
  562. function TIdIMAP4ServerDemo.UpdateNextFreeUID(ALoginName, AMailBoxName, ANewUIDNext: string): Boolean;
  563. var
  564. LRet: integer;
  565. LSrchRec: TSearchRec;
  566. LDir: string;
  567. begin
  568. Result := False;
  569. //Delete any existing .uid file...
  570. LDir := NameAndMailBoxToPath(ALoginName, AMailBoxName)+PathDelim;
  571. LRet := FindFirst(LDir+'*.uid', {FileAttrs} 0, LSrchRec); {Do not Localize}
  572. if LRet = 0 then begin
  573. if DeleteFile(LDir+LSrchRec.Name) = False then begin
  574. Exit;
  575. end;
  576. end;
  577. FindClose(LSrchRec);
  578. //Create the new UID file...
  579. {if FileCreate(LDir + ANewUIDNext + '.uid') <> -1 then begin
  580. Result := True;
  581. end;}
  582. Result := CreateEmptyFile(LDir + ANewUIDNext + '.uid'); {Do not Localize}
  583. end;
  584. function TIdIMAP4ServerDemo.GetFileNameToWriteAppendMessage(ALoginName, AMailBoxName, AUID: string): string;
  585. var
  586. LDir: string;
  587. begin
  588. LDir := NameAndMailBoxToPath(ALoginName, AMailBoxName)+PathDelim;
  589. Result := LDir + AUID + '.txt'; {Do not Localize}
  590. end;
  591. function TIdIMAP4ServerDemo.OpenMailBox(ASender: TIdCommand; AReadOnly: Boolean): Boolean;
  592. var
  593. LParams: TStringList;
  594. begin
  595. Result := False;
  596. LParams := TStringList.Create;
  597. BreakApart(ASender.UnparsedParams, ' ', LParams); {Do not Localize}
  598. if ReinterpretParamAsMailBox(LParams, 0) = False then begin
  599. SendBadReply(ASender, 'Mailbox parameter is invalid.'); {Do not Localize}
  600. LParams.Free;
  601. Exit;
  602. end;
  603. if LParams.Count < 1 then begin
  604. //Incorrect number of params...
  605. SendIncorrectNumberOfParameters(ASender);
  606. LParams.Free;
  607. Exit;
  608. end;
  609. if DoesImapMailBoxExist(TIdIMAP4PeerContext(ASender.Context).LoginName, LParams[0]) = False then begin
  610. SendNoReply(ASender, 'Mailbox does not exist.'); {Do not Localize}
  611. LParams.Free;
  612. Exit;
  613. end;
  614. {Get everything you need for this mailbox...}
  615. SetupMailbox(TIdIMAP4PeerContext(ASender.Context).LoginName,
  616. LParams[0],
  617. TIdIMAP4PeerContext(ASender.Context).MailBox);
  618. LParams.Free;
  619. if AReadOnly = True then begin
  620. TIdIMAP4PeerContext(ASender.Context).MailBox.State := msReadOnly;
  621. end else begin
  622. TIdIMAP4PeerContext(ASender.Context).MailBox.State := msReadWrite;
  623. end;
  624. {Send the stats...}
  625. OutputCurrentMailboxStats(ASender);
  626. Result := True;
  627. end;
  628. end.