| 12345678910111213141516171819202122232425262728293031323334353637383940414243444546474849505152535455565758596061626364656667686970717273747576777879808182838485868788899091929394959697989910010110210310410510610710810911011111211311411511611711811912012112212312412512612712812913013113213313413513613713813914014114214314414514614714814915015115215315415515615715815916016116216316416516616716816917017117217317417517617717817918018118218318418518618718818919019119219319419519619719819920020120220320420520620720820921021121221321421521621721821922022122222322422522622722822923023123223323423523623723823924024124224324424524624724824925025125225325425525625725825926026126226326426526626726826927027127227327427527627727827928028128228328428528628728828929029129229329429529629729829930030130230330430530630730830931031131231331431531631731831932032132232332432532632732832933033133233333433533633733833934034134234334434534634734834935035135235335435535635735835936036136236336436536636736836937037137237337437537637737837938038138238338438538638738838939039139239339439539639739839940040140240340440540640740840941041141241341441541641741841942042142242342442542642742842943043143243343443543643743843944044144244344444544644744844945045145245345445545645745845946046146246346446546646746846947047147247347447547647747847948048148248348448548648748848949049149249349449549649749849950050150250350450550650750850951051151251351451551651751851952052152252352452552652752852953053153253353453553653753853954054154254354454554654754854955055155255355455555655755855956056156256356456556656756856957057157257357457557657757857958058158258358458558658758858959059159259359459559659759859960060160260360460560660760860961061161261361461561661761861962062162262362462562662762862963063163263363463563663763863964064164264364464564664764864965065165265365465565665765865966066166266366466566666766866967067167267367467567667767867968068168268368468568668768868969069169269369469569669769869970070170270370470570670770870971071171271371471571671771871972072172272372472572672772872973073173273373473573673773873974074174274374474574674774874975075175275375475575675775875976076176276376476576676776876977077177277377477577677777877978078178278378478578678778878979079179279379479579679779879980080180280380480580680780880981081181281381481581681781881982082182282382482582682782882983083183283383483583683783883984084184284384484584684784884985085185285385485585685785885986086186286386486586686786886987087187287387487587687787887988088188288388488588688788888989089189289389489589689789889990090190290390490590690790890991091191291391491591691791891992092192292392492592692792892993093193293393493593693793893994094194294394494594694794894995095195295395495595695795895996096196296396496596696796896997097197297397497597697797897998098198298398498598698798898999099199299399499599699799899910001001100210031004100510061007100810091010101110121013101410151016101710181019102010211022102310241025102610271028102910301031103210331034103510361037103810391040104110421043104410451046104710481049105010511052105310541055105610571058105910601061106210631064106510661067106810691070107110721073107410751076107710781079108010811082108310841085108610871088108910901091109210931094109510961097109810991100110111021103110411051106110711081109111011111112111311141115111611171118111911201121112211231124112511261127112811291130113111321133113411351136113711381139114011411142114311441145114611471148114911501151115211531154115511561157115811591160116111621163116411651166116711681169117011711172117311741175117611771178117911801181118211831184118511861187118811891190119111921193119411951196119711981199120012011202120312041205120612071208120912101211121212131214121512161217121812191220122112221223122412251226122712281229123012311232123312341235123612371238123912401241124212431244124512461247124812491250125112521253125412551256125712581259126012611262126312641265126612671268126912701271127212731274127512761277127812791280128112821283128412851286128712881289129012911292129312941295129612971298129913001301130213031304130513061307130813091310131113121313131413151316131713181319132013211322132313241325132613271328132913301331133213331334133513361337133813391340134113421343134413451346134713481349135013511352135313541355135613571358135913601361136213631364136513661367136813691370137113721373137413751376137713781379138013811382138313841385138613871388138913901391139213931394139513961397139813991400140114021403140414051406140714081409141014111412141314141415141614171418141914201421142214231424142514261427142814291430143114321433143414351436143714381439144014411442144314441445144614471448144914501451145214531454145514561457145814591460146114621463146414651466146714681469147014711472147314741475147614771478147914801481148214831484148514861487148814891490149114921493149414951496149714981499150015011502150315041505150615071508150915101511151215131514151515161517151815191520152115221523152415251526152715281529153015311532153315341535153615371538153915401541154215431544154515461547154815491550155115521553155415551556155715581559156015611562156315641565156615671568156915701571157215731574157515761577157815791580158115821583158415851586158715881589159015911592159315941595159615971598159916001601160216031604160516061607160816091610161116121613161416151616161716181619162016211622162316241625162616271628162916301631163216331634163516361637163816391640164116421643164416451646164716481649165016511652165316541655165616571658165916601661166216631664166516661667166816691670167116721673167416751676167716781679168016811682168316841685168616871688168916901691169216931694169516961697169816991700170117021703170417051706170717081709171017111712171317141715171617171718171917201721172217231724172517261727172817291730173117321733173417351736173717381739174017411742174317441745174617471748174917501751175217531754175517561757175817591760176117621763176417651766176717681769177017711772177317741775177617771778177917801781178217831784178517861787178817891790179117921793179417951796179717981799180018011802180318041805180618071808180918101811181218131814181518161817181818191820182118221823182418251826182718281829183018311832183318341835183618371838183918401841184218431844184518461847184818491850185118521853185418551856185718581859186018611862186318641865186618671868186918701871187218731874187518761877187818791880188118821883188418851886188718881889189018911892189318941895189618971898189919001901190219031904190519061907190819091910191119121913191419151916191719181919192019211922192319241925192619271928192919301931193219331934193519361937193819391940194119421943194419451946194719481949195019511952195319541955195619571958195919601961196219631964196519661967196819691970197119721973197419751976197719781979198019811982198319841985198619871988198919901991199219931994199519961997199819992000200120022003200420052006200720082009201020112012201320142015201620172018201920202021202220232024202520262027202820292030203120322033203420352036203720382039204020412042204320442045204620472048204920502051205220532054205520562057205820592060206120622063206420652066206720682069207020712072207320742075207620772078207920802081208220832084208520862087208820892090209120922093209420952096209720982099210021012102210321042105210621072108210921102111211221132114211521162117211821192120212121222123212421252126212721282129213021312132213321342135213621372138213921402141214221432144214521462147214821492150215121522153215421552156215721582159216021612162216321642165216621672168216921702171217221732174217521762177217821792180218121822183218421852186218721882189219021912192219321942195219621972198219922002201220222032204220522062207220822092210221122122213221422152216221722182219222022212222222322242225222622272228222922302231223222332234223522362237223822392240224122422243224422452246224722482249225022512252225322542255225622572258225922602261226222632264226522662267226822692270227122722273227422752276227722782279228022812282228322842285228622872288228922902291229222932294229522962297229822992300230123022303230423052306230723082309231023112312231323142315231623172318231923202321232223232324232523262327232823292330233123322333233423352336233723382339234023412342234323442345234623472348234923502351235223532354235523562357235823592360236123622363236423652366236723682369237023712372237323742375237623772378237923802381238223832384238523862387238823892390239123922393239423952396239723982399240024012402240324042405240624072408240924102411241224132414241524162417241824192420242124222423242424252426242724282429243024312432243324342435243624372438243924402441244224432444244524462447244824492450245124522453245424552456245724582459246024612462246324642465246624672468246924702471247224732474247524762477247824792480248124822483248424852486248724882489249024912492249324942495249624972498249925002501250225032504250525062507250825092510251125122513251425152516251725182519252025212522252325242525252625272528252925302531253225332534253525362537253825392540254125422543254425452546254725482549255025512552255325542555255625572558255925602561256225632564256525662567256825692570257125722573257425752576257725782579258025812582258325842585258625872588258925902591259225932594259525962597259825992600260126022603260426052606260726082609261026112612261326142615261626172618261926202621262226232624262526262627262826292630263126322633263426352636263726382639264026412642264326442645264626472648264926502651265226532654265526562657265826592660266126622663266426652666266726682669267026712672267326742675267626772678267926802681268226832684268526862687268826892690269126922693269426952696269726982699270027012702270327042705270627072708270927102711271227132714 |
- {
- $Project$
- $Workfile$
- $Revision$
- $DateUTC$
- $Id$
- This file is part of the Indy (Internet Direct) project, and is offered
- under the dual-licensing agreement described on the Indy website.
- (http://www.indyproject.org/)
- Copyright:
- (c) 1993-2005, Chad Z. Hower and the Indy Pit Crew. All rights reserved.
- }
- {
- $Log$
- }
- {
- Prior revision history
- Rev 1.31 2/9/2005 11:44:20 AM JPMugaas
- Fixed compiler problem and removed some warnings about virtual
- methods hiding stuff in the base class.
- Rev 1.30 2/8/05 6:20:16 PM RLebeau
- Added additional overriden methods.
- Rev 1.29 10/26/2004 11:08:06 PM JPMugaas
- Updated refs.
- Rev 1.28 10/21/2004 1:49:12 PM BGooijen
- Raid 214213
- Rev 1.27 09/06/2004 09:54:56 CCostelloe
- Kylix 3 patch
- Rev 1.26 2004.05.20 11:37:34 AM czhower
- IdStreamVCL
- Rev 1.25 4/8/2004 11:49:56 AM BGooijen
- Fix for D5
- Rev 1.24 03/03/2004 01:16:20 CCostelloe
- Yet another check-in as part of continuing development
- Rev 1.23 01/03/2004 23:32:24 CCostelloe
- Another check-in as part of continuing development
- Rev 1.22 3/1/2004 12:55:28 PM JPMugaas
- Updated for problem with new code.
- Rev 1.21 26/02/2004 02:01:14 CCostelloe
- Another intermediate check-in, approx half of functions are debugged
- Rev 1.20 24/02/2004 10:34:50 CCostelloe
- Storage-specific code moved to IdIMAP4ServerDemo
- Rev 1.19 2/22/2004 12:09:54 AM JPMugaas
- Fixes for IMAP4Server compile failure in DotNET. This also fixes
- a potential problem where file handles can be leaked in the server
- needlessly.
- Rev 1.18 12/02/2004 02:40:56 CCostelloe
- Minor bugfix
- Rev 1.17 12/02/2004 02:24:30 CCostelloe
- Completed revision, apart from parts support and BODYSTRUCTURE, not
- yet debugged.
- Rev 1.16 05/02/2004 00:25:32 CCostelloe
- This version actually works!
- Rev 1.15 2/4/2004 2:37:38 AM JPMugaas
- Moved more units down to the implementation clause in the units to
- make them easier to compile.
- Rev 1.14 2/3/2004 4:12:42 PM JPMugaas
- Fixed up units so they should compile.
- Rev 1.13 1/29/2004 9:07:54 PM JPMugaas
- Now uses TIdExplicitTLSServer so it can take advantage of that framework.
- Rev 1.12 1/21/2004 3:11:02 PM JPMugaas
- InitComponent
- Rev 1.11 27/12/2003 22:28:48 ANeillans
- Design fix, Login event only passed the username (first param)
- Rev 1.10 2003.10.21 9:13:08 PM czhower
- Now compiles.
- Rev 1.9 10/19/2003 6:00:24 PM DSiders
- Added localization coimments.
- Rev 1.8 9/19/2003 03:29:58 PM JPMugaas
- Now should compile again.
- Rev 1.7 07/09/2003 12:29:08 CCostelloe
- Warning that variable LIO is declared but never used in
- TIdIMAP4Server.DoCommandSTARTTLS fixed.
- Rev 1.6 7/20/2003 6:20:06 PM SPerry
- Switched to IdCmdTCPServer, also some modifications
- Rev 1.5 3/14/2003 10:44:36 PM BGooijen
- Removed warnings, changed StartSSL to PassThrough:=false;
- Rev 1.4 3/14/2003 10:04:10 PM BGooijen
- Removed TIdServerIOHandlerSSLBase.PeerPassthrough, the ssl is now
- enabled in the server-protocol-files
- Rev 1.3 3/13/2003 09:49:20 AM JPMugaas
- Now uses an abstract SSL base class instead of OpenSSL so
- 3rd-party vendors can plug-in their products.
- Rev 1.2 2/24/2003 09:03:14 PM JPMugaas
- Rev 1.1 2/6/2003 03:18:14 AM JPMugaas
- Updated components that compile with Indy 10.
- Rev 1.0 11/13/2002 07:55:02 AM JPMugaas
- 2002-Apr-21 - J. Berg
- use fetch()
- 2000-May-18 - J. Peter Mugaas
- Ported to Indy
- 2000-Jan-13 - MTL
- Moved to new Palette Scheme (Winshoes Servers)
- 1999-Aug-26 - Ray Malone
- Started unit
- }
- unit IdIMAP4Server;
- {
- TODO (ex RFC 3501):
- Dont allow & to be used as a mailbox separator.
- Certain server data (unsolicited responses) MUST be recorded,
- see Server Responses section.
- UIDs must be unique to a mailbox AND any subsequent mailbox with
- the same name - record in a text file.
- \Recent cannot be changed by STORE or APPEND.
- COPY should preserve the date of the original message.
- TODO (ccostelloe):
- Add a file recording the UIDVALIDITY in each mailbox.
- Emails should be ordered in date order.
- Optional date/time param to be implemented in APPEND.
- Consider integrating IdUserAccounts into login mechanism
- (or per-user passwords).
- Implement utf mailbox encoding.
- Implement * in message numbers.
- Implement multiple-option FETCH commands (will need breaking out some
- options which are abbreviations into their subsets).
- Need some method of preserving flags permanently.
- }
- {
- IMPLEMENTATION NOTES:
- Major rewrite started 2nd February 2004, Ciaran Costelloe, [email protected].
- Prior to this, it was a simple wrapper class with a few problems.
- Note that IMAP servers should return BAD for an unknown command or
- invalid arguments (synthax errors and unsupported commands) and BAD
- if the command is valid but there was some problem in executing
- (e.g. trying a change an email's flag if it is a read-only mailbox).
- FUseDefaultMechanismsForUnassignedCommands defaults to True: if you
- set it to False, you need to implement command handlers for all the
- commands you need to implement. If True, this class implements a
- default mechanism and provides default behaviour for all commands.
- It does not include any filesystem-specific functions, which you
- need to implement.
- The default behaviour uses a default password of 'admin' - change this
- if you have any consideration for security!
- FSaferMode defaults to False: you should probably leave it False for
- testing, because this generates diagnostically-useful error messages.
- However, setting it True generates minimal responses for the greeting
- and for login failures, making life more difficult for a hacker.
- WARNING: you should also implement one of the Indy-provided more-secure
- logins than the default plaintext password login!
- You may want to assign handlers to the OnBeforeCmd and OnBeforeSend
- events to easily log data in & out of the server.
- WARNING: TIdIMAP4PeerContext has a TIdMailBox which holds various
- status info, including UIDs in its message collection. Do NOT use the
- message collection for loading messages into, or you may thrash message
- UIDs or flags!
- }
- interface
- {$i IdCompilerDefines.inc}
- {$IFDEF DOTNET}
- {$I IdUnitPlatformOff.inc}
- {$I IdSymbolPlatformOff.inc}
- {$ENDIF}
- uses
- Classes,
- IdAssignedNumbers,
- IdCustomTCPServer, //for TIdServerContext
- IdCmdTCPServer,
- IdContext,
- IdCommandHandlers,
- IdException,
- IdExplicitTLSClientServerBase,
- IdIMAP4, //For some defines like TIdIMAP4ConnectionState
- IdMailBox,
- IdMessage,
- IdReply,
- IdReplyIMAP4,
- IdTCPConnection,
- IdYarn;
- const
- DEF_IMAP4_IMPLICIT_TLS = False;
- type
- TIMAP4CommandEvent = procedure(AContext: TIdContext; const ATag, ACmd: String) of object;
- TIdIMAP4CommandBeforeEvent = procedure(ASender: TIdCommandHandlers; var AData: string; AContext: TIdContext) of object;
- TIdIMAP4CommandBeforeSendEvent = procedure(AContext: TIdContext; AData: string) of object;
- //For default mechanisms..
- TIdIMAP4DefMech1 = function(ALoginName, AMailbox: string): Boolean of object;
- TIdIMAP4DefMech2 = function(ALoginName, AMailBoxName: string; AMailBox: TIdMailBox): Boolean of object;
- TIdIMAP4DefMech3 = function(ALoginName, AMailbox: string): string of object;
- TIdIMAP4DefMech4 = function(ALoginName, AOldMailboxName, ANewMailboxName: string): Boolean of object;
- TIdIMAP4DefMech5 = function(ALoginName, AMailBoxName: string; AMailBoxNames: TStrings; AMailBoxFlags: TStrings): Boolean of object;
- TIdIMAP4DefMech6 = function(ALoginName, AMailbox: string; AMessage: TIdMessage): Boolean of object;
- TIdIMAP4DefMech7 = function(ALoginName, ASourceMailBox, AMessageUID, ADestinationMailbox: string): Boolean of object;
- TIdIMAP4DefMech8 = function(ALoginName, AMailbox: string; AMessage: TIdMessage): Int64 of object;
- TIdIMAP4DefMech9 = function(ALoginName, AMailbox: string; AMessage, ATargetMessage: TIdMessage): Boolean of object;
- TIdIMAP4DefMech10 = function(ALoginName, AMailbox: string; AMessage: TIdMessage; ALines: TStrings): Boolean of object;
- TIdIMAP4DefMech11 = function(ASender: TIdCommand; AReadOnly: Boolean): Boolean of object;
- TIdIMAP4DefMech12 = function(AParams: TStrings; AMailBoxParam: Integer): Boolean of object;
- TIdIMAP4DefMech13 = function(ALoginName, AMailBoxName, ANewUIDNext: string): Boolean of object;
- TIdIMAP4DefMech14 = function(ALoginName, AMailBoxName, AUID: string): string of object;
- EIdIMAP4ServerException = class(EIdException);
- EIdIMAP4ImplicitTLSRequiresSSL = class(EIdIMAP4ServerException);
- { custom IMAP4 context }
- TIdIMAP4PeerContext = class(TIdServerContext)
- protected
- FConnectionState : TIdIMAP4ConnectionState;
- FLoginName: string;
- FMailBox: TIdMailBox;
- FIMAP4Tag: String;
- FLastCommand: TIdReplyIMAP4; //Used to record the client command we are currently processing
- function GetUsingTLS: Boolean;
- public
- constructor Create(
- AConnection: TIdTCPConnection;
- AYarn: TIdYarn;
- AList: TIdContextThreadList = nil
- ); override;
- destructor Destroy; override;
- property ConnectionState: TIdIMAP4ConnectionState read FConnectionState;
- property UsingTLS : Boolean read GetUsingTLS;
- property IMAP4Tag: String read FIMAP4Tag;
- property MailBox: TIdMailBox read FMailBox;
- property LoginName: string read FLoginName write FLoginName;
- end;
- { TIdIMAP4Server }
- TIdIMAP4Server = class(TIdExplicitTLSServer)
- protected
- //
- FSaferMode: Boolean; //See IMPLEMENTATION NOTES above
- FUseDefaultMechanismsForUnassignedCommands: Boolean; //See IMPLEMENTATION NOTES above
- FRootPath: string; //See IMPLEMENTATION NOTES above
- FDefaultPassword: string; //See IMPLEMENTATION NOTES above
- FMailBoxSeparator: Char;
- //
- fOnDefMechDoesImapMailBoxExist: TIdIMAP4DefMech1;
- fOnDefMechCreateMailBox: TIdIMAP4DefMech1;
- fOnDefMechDeleteMailBox: TIdIMAP4DefMech1;
- fOnDefMechIsMailBoxOpen: TIdIMAP4DefMech1;
- fOnDefMechSetupMailbox: TIdIMAP4DefMech2;
- fOnDefMechNameAndMailBoxToPath: TIdIMAP4DefMech3;
- fOnDefMechGetNextFreeUID: TIdIMAP4DefMech3;
- fOnDefMechRenameMailBox: TIdIMAP4DefMech4;
- fOnDefMechListMailBox: TIdIMAP4DefMech5;
- fOnDefMechDeleteMessage: TIdIMAP4DefMech6;
- fOnDefMechCopyMessage: TIdIMAP4DefMech7;
- fOnDefMechGetMessageSize: TIdIMAP4DefMech8;
- fOnDefMechGetMessageHeader: TIdIMAP4DefMech9;
- fOnDefMechGetMessageRaw: TIdIMAP4DefMech10;
- fOnDefMechOpenMailBox: TIdIMAP4DefMech11;
- fOnDefMechReinterpretParamAsMailBox: TIdIMAP4DefMech12;
- fOnDefMechUpdateNextFreeUID: TIdIMAP4DefMech13;
- fOnDefMechGetFileNameToWriteAppendMessage: TIdIMAP4DefMech14;
- //
- fOnBeforeCmd: TIdIMAP4CommandBeforeEvent;
- fOnBeforeSend: TIdIMAP4CommandBeforeSendEvent;
- fOnCommandCAPABILITY: TIMAP4CommandEvent;
- fONCommandNOOP: TIMAP4CommandEvent;
- fONCommandLOGOUT: TIMAP4CommandEvent;
- fONCommandAUTHENTICATE: TIMAP4CommandEvent;
- fONCommandLOGIN: TIMAP4CommandEvent;
- fONCommandSELECT: TIMAP4CommandEvent;
- fONCommandEXAMINE: TIMAP4CommandEvent;
- fONCommandCREATE: TIMAP4CommandEvent;
- fONCommandDELETE: TIMAP4CommandEvent;
- fONCommandRENAME: TIMAP4CommandEvent;
- fONCommandSUBSCRIBE: TIMAP4CommandEvent;
- fONCommandUNSUBSCRIBE: TIMAP4CommandEvent;
- fONCommandLIST: TIMAP4CommandEvent;
- fONCommandLSUB: TIMAP4CommandEvent;
- fONCommandSTATUS: TIMAP4CommandEvent;
- fONCommandAPPEND: TIMAP4CommandEvent;
- fONCommandCHECK: TIMAP4CommandEvent;
- fONCommandCLOSE: TIMAP4CommandEvent;
- fONCommandEXPUNGE: TIMAP4CommandEvent;
- fONCommandSEARCH: TIMAP4CommandEvent;
- fONCommandFETCH: TIMAP4CommandEvent;
- fONCommandSTORE: TIMAP4CommandEvent;
- fONCommandCOPY: TIMAP4CommandEvent;
- fONCommandUID: TIMAP4CommandEvent;
- fONCommandX: TIMAP4CommandEvent;
- fOnCommandError: TIMAP4CommandEvent;
- //
- function CreateExceptionReply: TIdReply; override;
- function CreateGreeting: TIdReply; override;
- function CreateHelpReply: TIdReply; override;
- function CreateMaxConnectionReply: TIdReply; override;
- function CreateReplyUnknownCommand: TIdReply; override;
- //
- //The following are internal commands that help support the IMAP protocol...
- procedure InitializeCommandHandlers; override;
- function GetReplyClass:TIdReplyClass; override;
- function GetRepliesClass:TIdRepliesClass; override;
- procedure SendGreeting(AContext: TIdContext; AGreeting: TIdReply); override;
- procedure SendWrongConnectionState(ASender: TIdCommand);
- procedure SendUnsupportedCommand(ASender: TIdCommand);
- procedure SendIncorrectNumberOfParameters(ASender: TIdCommand);
- procedure SendUnassignedDefaultMechanism(ASender: TIdCommand);
- procedure DoReplyUnknownCommand(AContext: TIdContext; AText: string); override;
- procedure SendErrorOpenedReadOnly(ASender: TIdCommand);
- procedure SendOkReply(ASender: TIdCommand; const AText: string);
- procedure SendBadReply(ASender: TIdCommand; const AText: string); overload;
- procedure SendBadReply(ASender: TIdCommand; const AFormat: string; const Args: array of const); overload;
- procedure SendNoReply(ASender: TIdCommand; const AText: string = ''); overload;
- procedure SendNoReply(ASender: TIdCommand; const AFormat: string; const Args: array of const); overload;
- //
- //The following are used internally by the default mechanism...
- function ExpungeRecords(ASender: TIdCommand): Boolean;
- function MessageSetToMessageNumbers(AUseUID: Boolean; ASender: TIdCommand; AMessageNumbers: TStrings; AMessageSet: string): Boolean;
- function GetRecordForUID(const AUID: String; AMailBox: TIdMailBox): Int64;
- procedure ProcessFetch(AUseUID: Boolean; ASender: TIdCommand; AParams: TStrings);
- procedure ProcessCopy(AUseUID: Boolean; ASender: TIdCommand; AParams: TStrings);
- function ProcessStore(AUseUID: Boolean; ASender: TIdCommand; AParams: TStrings): Boolean;
- procedure ProcessSearch(AUseUID: Boolean; ASender: TIdCommand; AParams: TStrings);
- function FlagStringToFlagList(AFlagList: TStrings; AFlagString: string): Boolean;
- function StripQuotesIfNecessary(AName: string): string;
- function ReassembleParams(ASeparator: char; AParams: TStrings; AParamToReassemble: integer): Boolean;
- function ReinterpretParamAsMailBox(AParams: TStrings; AMailBoxParam: integer): Boolean;
- function ReinterpretParamAsFlags(AParams: TStrings; AFlagsParam: integer): Boolean;
- function ReinterpretParamAsQuotedStr(AParams: TStrings; AFlagsParam: integer): Boolean;
- function ReinterpretParamAsDataItems(AParams: TStrings; AFlagsParam: integer): Boolean;
- //
- //The following are used internally by our default mechanism and are copies of
- //the same function in TIdIMAP4 (move to a base class?)...
- function MessageFlagSetToStr(const AFlags: TIdMessageFlagsSet): String;
- //
- //DoBeforeCmd & DoSendReply are useful for a server to log all commands and
- //responses for debugging...
- procedure DoBeforeCmd(ASender: TIdCommandHandlers; var AData: string; AContext: TIdContext);
- procedure DoSendReply(AContext: TIdContext; const AData: string); overload;
- procedure DoSendReply(AContext: TIdContext; const AFormat: string; const Args: array of const); overload;
- //
- //Command handlers...
- procedure DoCmdHandlersException(ACommand: String; AContext: TIdContext);
- procedure DoCommandCAPABILITY(ASender: TIdCommand);
- procedure DoCommandNOOP(ASender: TIdCommand);
- procedure DoCommandLOGOUT(ASender: TIdCommand);
- procedure DoCommandAUTHENTICATE(ASender: TIdCommand);
- procedure DoCommandLOGIN(ASender: TIdCommand);
- procedure DoCommandSELECT(ASender: TIdCommand);
- procedure DoCommandEXAMINE(ASender: TIdCommand);
- procedure DoCommandCREATE(ASender: TIdCommand);
- procedure DoCommandDELETE(ASender: TIdCommand);
- procedure DoCommandRENAME(ASender: TIdCommand);
- procedure DoCommandSUBSCRIBE(ASender: TIdCommand);
- procedure DoCommandUNSUBSCRIBE(ASender: TIdCommand);
- procedure DoCommandLIST(ASender: TIdCommand);
- procedure DoCommandLSUB(ASender: TIdCommand);
- procedure DoCommandSTATUS(ASender: TIdCommand);
- procedure DoCommandAPPEND(ASender: TIdCommand);
- procedure DoCommandCHECK(ASender: TIdCommand);
- procedure DoCommandCLOSE(ASender: TIdCommand);
- procedure DoCommandEXPUNGE(ASender: TIdCommand);
- procedure DoCommandSEARCH(ASender: TIdCommand);
- procedure DoCommandFETCH(ASender: TIdCommand);
- procedure DoCommandSTORE(ASender: TIdCommand);
- procedure DoCommandCOPY(ASender: TIdCommand);
- procedure DoCommandUID(ASender: TIdCommand);
- procedure DoCommandX(ASender: TIdCommand);
- procedure DoCommandSTARTTLS(ASender: TIdCommand);
- // common code for command handlers
- procedure MustUseTLS(ASender: TIdCommand);
- //
- procedure InitComponent; override;
- public
- {$IFDEF WORKAROUND_INLINE_CONSTRUCTORS}
- constructor Create(AOwner: TComponent); reintroduce; overload;
- {$ENDIF}
- destructor Destroy; override;
- published
- property DefaultPort default IdPORT_IMAP4;
- property SaferMode: Boolean read FSaferMode write FSaferMode default False;
- property UseDefaultMechanismsForUnassignedCommands: Boolean read FUseDefaultMechanismsForUnassignedCommands write FUseDefaultMechanismsForUnassignedCommands default True;
- property RootPath: string read FRootPath write FRootPath;
- property DefaultPassword: string read FDefaultPassword write FDefaultPassword;
- property MailBoxSeparator: Char read FMailBoxSeparator;
- {Default mechansisms}
- property OnDefMechDoesImapMailBoxExist: TIdIMAP4DefMech1 read fOnDefMechDoesImapMailBoxExist write fOnDefMechDoesImapMailBoxExist;
- property OnDefMechCreateMailBox: TIdIMAP4DefMech1 read fOnDefMechCreateMailBox write fOnDefMechCreateMailBox;
- property OnDefMechDeleteMailBox: TIdIMAP4DefMech1 read fOnDefMechDeleteMailBox write fOnDefMechDeleteMailBox;
- property OnDefMechIsMailBoxOpen: TIdIMAP4DefMech1 read fOnDefMechIsMailBoxOpen write fOnDefMechIsMailBoxOpen;
- property OnDefMechSetupMailbox: TIdIMAP4DefMech2 read fOnDefMechSetupMailbox write fOnDefMechSetupMailbox;
- property OnDefMechNameAndMailBoxToPath: TIdIMAP4DefMech3 read fOnDefMechNameAndMailBoxToPath write fOnDefMechNameAndMailBoxToPath;
- property OnDefMechGetNextFreeUID: TIdIMAP4DefMech3 read fOnDefMechGetNextFreeUID write fOnDefMechGetNextFreeUID;
- property OnDefMechRenameMailBox: TIdIMAP4DefMech4 read fOnDefMechRenameMailBox write fOnDefMechRenameMailBox;
- property OnDefMechListMailBox: TIdIMAP4DefMech5 read fOnDefMechListMailBox write fOnDefMechListMailBox;
- property OnDefMechDeleteMessage: TIdIMAP4DefMech6 read fOnDefMechDeleteMessage write fOnDefMechDeleteMessage;
- property OnDefMechCopyMessage: TIdIMAP4DefMech7 read fOnDefMechCopyMessage write fOnDefMechCopyMessage;
- property OnDefMechGetMessageSize: TIdIMAP4DefMech8 read fOnDefMechGetMessageSize write fOnDefMechGetMessageSize;
- property OnDefMechGetMessageHeader: TIdIMAP4DefMech9 read fOnDefMechGetMessageHeader write fOnDefMechGetMessageHeader;
- property OnDefMechGetMessageRaw: TIdIMAP4DefMech10 read fOnDefMechGetMessageRaw write fOnDefMechGetMessageRaw;
- property OnDefMechOpenMailBox: TIdIMAP4DefMech11 read fOnDefMechOpenMailBox write fOnDefMechOpenMailBox;
- property OnDefMechReinterpretParamAsMailBox: TIdIMAP4DefMech12 read fOnDefMechReinterpretParamAsMailBox write fOnDefMechReinterpretParamAsMailBox;
- property OnDefMechUpdateNextFreeUID: TIdIMAP4DefMech13 read fOnDefMechUpdateNextFreeUID write fOnDefMechUpdateNextFreeUID;
- property OnDefMechGetFileNameToWriteAppendMessage: TIdIMAP4DefMech14 read fOnDefMechGetFileNameToWriteAppendMessage write fOnDefMechGetFileNameToWriteAppendMessage;
- { Events }
- property OnBeforeCmd: TIdIMAP4CommandBeforeEvent read fOnBeforeCmd write fOnBeforeCmd;
- property OnBeforeSend: TIdIMAP4CommandBeforeSendEvent read fOnBeforeSend write fOnBeforeSend;
- property OnCommandCAPABILITY: TIMAP4CommandEvent read fOnCommandCAPABILITY write fOnCommandCAPABILITY;
- property OnCommandNOOP: TIMAP4CommandEvent read fONCommandNOOP write fONCommandNOOP;
- property OnCommandLOGOUT: TIMAP4CommandEvent read fONCommandLOGOUT write fONCommandLOGOUT;
- property OnCommandAUTHENTICATE: TIMAP4CommandEvent read fONCommandAUTHENTICATE write fONCommandAUTHENTICATE;
- property OnCommandLOGIN: TIMAP4CommandEvent read fONCommandLOGIN write fONCommandLOGIN;
- property OnCommandSELECT: TIMAP4CommandEvent read fONCommandSELECT write fONCommandSELECT;
- property OnCommandEXAMINE:TIMAP4CommandEvent read fOnCommandEXAMINE write fOnCommandEXAMINE;
- property OnCommandCREATE: TIMAP4CommandEvent read fONCommandCREATE write fONCommandCREATE;
- property OnCommandDELETE: TIMAP4CommandEvent read fONCommandDELETE write fONCommandDELETE;
- property OnCommandRENAME: TIMAP4CommandEvent read fOnCommandRENAME write fOnCommandRENAME;
- property OnCommandSUBSCRIBE: TIMAP4CommandEvent read fONCommandSUBSCRIBE write fONCommandSUBSCRIBE;
- property OnCommandUNSUBSCRIBE: TIMAP4CommandEvent read fONCommandUNSUBSCRIBE write fONCommandUNSUBSCRIBE;
- property OnCommandLIST: TIMAP4CommandEvent read fONCommandLIST write fONCommandLIST;
- property OnCommandLSUB: TIMAP4CommandEvent read fOnCommandLSUB write fOnCommandLSUB;
- property OnCommandSTATUS: TIMAP4CommandEvent read fONCommandSTATUS write fONCommandSTATUS;
- property OnCommandAPPEND: TIMAP4CommandEvent read fOnCommandAPPEND write fOnCommandAPPEND;
- property OnCommandCHECK: TIMAP4CommandEvent read fONCommandCHECK write fONCommandCHECK;
- property OnCommandCLOSE: TIMAP4CommandEvent read fOnCommandCLOSE write fOnCommandCLOSE;
- property OnCommandEXPUNGE: TIMAP4CommandEvent read fONCommandEXPUNGE write fONCommandEXPUNGE;
- property OnCommandSEARCH: TIMAP4CommandEvent read fOnCommandSEARCH write fOnCommandSEARCH;
- property OnCommandFETCH: TIMAP4CommandEvent read fONCommandFETCH write fONCommandFETCH;
- property OnCommandSTORE: TIMAP4CommandEvent read fOnCommandSTORE write fOnCommandSTORE;
- property OnCommandCOPY: TIMAP4CommandEvent read fOnCommandCOPY write fOnCommandCOPY;
- property OnCommandUID: TIMAP4CommandEvent read fONCommandUID write fONCommandUID;
- property OnCommandX: TIMAP4CommandEvent read fOnCommandX write fOnCommandX;
- property OnCommandError: TIMAP4CommandEvent read fOnCommandError write fOnCommandError;
- end;
- implementation
- uses
- IdGlobal,
- IdGlobalProtocols,
- IdMessageCollection,
- IdResourceStringsProtocols,
- IdSSL,
- SysUtils;
- function TIdIMAP4Server.GetReplyClass: TIdReplyClass;
- begin
- Result := TIdReplyIMAP4;
- end;
- function TIdIMAP4Server.GetRepliesClass: TIdRepliesClass;
- begin
- Result := TIdRepliesIMAP4;
- end;
- procedure TIdIMAP4Server.SendGreeting(AContext: TIdContext; AGreeting: TIdReply);
- begin
- if FSaferMode then begin
- DoSendReply(AContext, '* OK'); {Do not Localize}
- end else begin
- DoSendReply(AContext, '* OK Indy IMAP server version ' + GetIndyVersion); {Do not Localize}
- end;
- end;
- procedure TIdIMAP4Server.SendWrongConnectionState(ASender: TIdCommand);
- begin
- SendNoReply(ASender, 'Wrong connection state'); {Do not Localize}
- end;
- procedure TIdIMAP4Server.SendErrorOpenedReadOnly(ASender: TIdCommand);
- begin
- SendNoReply(ASender, 'Mailbox was opened read-only'); {Do not Localize}
- end;
- procedure TIdIMAP4Server.SendUnsupportedCommand(ASender: TIdCommand);
- begin
- SendBadReply(ASender, 'Unsupported command'); {Do not Localize}
- end;
- procedure TIdIMAP4Server.SendIncorrectNumberOfParameters(ASender: TIdCommand);
- begin
- SendBadReply(ASender, 'Incorrect number of parameters'); {Do not Localize}
- end;
- procedure TIdIMAP4Server.SendUnassignedDefaultMechanism(ASender: TIdCommand);
- begin
- SendBadReply(ASender, 'Server internal error: unassigned procedure'); {Do not Localize}
- end;
- procedure TIdIMAP4Server.SendOkReply(ASender: TIdCommand; const AText: string);
- begin
- DoSendReply(ASender.Context, TIdIMAP4PeerContext(ASender.Context).FLastCommand.SequenceNumber + ' OK ' + AText); {Do not Localize}
- end;
- procedure TIdIMAP4Server.SendBadReply(ASender: TIdCommand; const AText: string);
- begin
- DoSendReply(ASender.Context, TIdIMAP4PeerContext(ASender.Context).FLastCommand.SequenceNumber + ' BAD ' + AText); {Do not Localize}
- end;
- procedure TIdIMAP4Server.SendBadReply(ASender: TIdCommand; const AFormat: string; const Args: array of const);
- begin
- SendBadReply(ASender, IndyFormat(AFormat, Args));
- end;
- procedure TIdIMAP4Server.SendNoReply(ASender: TIdCommand; const AText: string = '');
- begin
- if AText <> '' then begin
- DoSendReply(ASender.Context, TIdIMAP4PeerContext(ASender.Context).FLastCommand.SequenceNumber + ' NO ' + AText); {Do not Localize}
- end else begin
- DoSendReply(ASender.Context, TIdIMAP4PeerContext(ASender.Context).FLastCommand.SequenceNumber + ' NO'); {Do not Localize}
- end;
- end;
- procedure TIdIMAP4Server.SendNoReply(ASender: TIdCommand; const AFormat: string; const Args: array of const);
- begin
- SendNoReply(ASender, IndyFormat(AFormat, Args));
- end;
- {$IFDEF WORKAROUND_INLINE_CONSTRUCTORS}
- constructor TIdIMAP4Server.Create(AOwner: TComponent);
- begin
- inherited Create(AOwner);
- end;
- {$ENDIF}
- procedure TIdIMAP4Server.InitComponent;
- begin
- inherited InitComponent;
- //Todo: Not sure which number is appropriate. Should be tested
- FRegularProtPort := IdPORT_IMAP4;
- FImplicitTLSProtPort := IdPORT_IMAP4S; //Id_PORT_imap4_ssl_dp;
- FExplicitTLSProtPort := IdPORT_IMAP4;
- DefaultPort := IdPORT_IMAP4;
- ContextClass := TIdIMAP4PeerContext;
- FSaferMode := False;
- FUseDefaultMechanismsForUnassignedCommands := True;
- {$IFDEF UNIX}
- FRootPath := GPathDelim + 'var' + GPathDelim + 'imapmail'; {Do not Localize}
- {$ELSE}
- FRootPath := GPathDelim + 'imapmail'; {Do not Localize}
- {$ENDIF}
- FDefaultPassword := 'admin'; {Do not Localize}
- FMailBoxSeparator := '.'; {Do not Localize}
- end;
- destructor TIdIMAP4Server.Destroy;
- begin
- inherited Destroy;
- end;
- function TIdIMAP4Server.CreateExceptionReply: TIdReply;
- begin
- Result := TIdReplyIMAP4.CreateWithReplyTexts(nil, ReplyTexts);
- Result.SetReply(IMAP_BAD, 'Unknown Internal Error'); {do not localize}
- end;
- function TIdIMAP4Server.CreateGreeting: TIdReply;
- begin
- Result := TIdReplyIMAP4.CreateWithReplyTexts(nil, ReplyTexts);
- Result.SetReply(IMAP_OK, 'Welcome'); {do not localize}
- end;
- function TIdIMAP4Server.CreateHelpReply: TIdReply;
- begin
- Result := TIdReplyIMAP4.CreateWithReplyTexts(nil, ReplyTexts);
- Result.SetReply(IMAP_OK, 'Help follows'); {do not localize}
- end;
- function TIdIMAP4Server.CreateMaxConnectionReply: TIdReply;
- begin
- Result := TIdReplyIMAP4.CreateWithReplyTexts(nil, ReplyTexts);
- Result.SetReply(IMAP_BAD, 'Too many connections. Try again later.'); {do not localize}
- end;
- function TIdIMAP4Server.CreateReplyUnknownCommand: TIdReply;
- begin
- Result := TIdReplyIMAP4.CreateWithReplyTexts(nil, ReplyTexts);
- Result.SetReply(IMAP_BAD, 'Unknown command'); {do not localize}
- end;
- constructor TIdIMAP4PeerContext.Create(AConnection: TIdTCPConnection; AYarn: TIdYarn; AList: TIdContextThreadList = nil);
- begin
- inherited Create(AConnection, AYarn, AList);
- FMailBox := TIdMailBox.Create;
- FLastCommand := TIdReplyIMAP4.Create(nil);
- FConnectionState := csAny;
- end;
- destructor TIdIMAP4PeerContext.Destroy;
- begin
- FreeAndNil(FLastCommand);
- FreeAndNil(FMailBox);
- inherited Destroy;
- end;
- function TIdIMAP4PeerContext.GetUsingTLS: Boolean;
- begin
- if Connection.IOHandler is TIdSSLIOHandlerSocketBase then begin
- Result := not TIdSSLIOHandlerSocketBase(Connection.IOHandler).PassThrough;
- end else begin
- Result := False;
- end;
- end;
- procedure TIdIMAP4Server.DoReplyUnknownCommand(AContext: TIdContext; AText: string);
- //AText is ignored by TIdIMAP4Server
- var
- LText: string;
- begin
- LText := TIdIMAP4PeerContext(AContext).FLastCommand.SequenceNumber;
- if LText = '' then begin
- //This should not happen!
- LText := '*'; {Do not Localize}
- end;
- DoSendReply(AContext, LText + ' NO Unknown command'); {Do not Localize}
- end;
- function TIdIMAP4Server.ExpungeRecords(ASender: TIdCommand): Boolean;
- var
- LN: integer;
- LMessage: TIdMessage;
- LContext: TIdIMAP4PeerContext;
- begin
- LContext := TIdIMAP4PeerContext(ASender.Context);
- //Delete all records that have the deleted flag set...
- LN := 0;
- Result := True;
- while LN < LContext.MailBox.MessageList.Count do begin
- LMessage := LContext.MailBox.MessageList.Messages[LN];
- if mfDeleted in LMessage.Flags then begin
- if not OnDefMechDeleteMessage(LContext.LoginName, LContext.MailBox.Name, LMessage) then
- begin
- Result := False;
- end;
- LContext.MailBox.MessageList.Delete(LN);
- LContext.MailBox.TotalMsgs := LContext.MailBox.TotalMsgs - 1;
- end else begin
- Inc(LN);
- end;
- end;
- end;
- function TIdIMAP4Server.MessageSetToMessageNumbers(AUseUID: Boolean; ASender: TIdCommand;
- AMessageNumbers: TStrings; AMessageSet: string): Boolean;
- {AMessageNumbers may be '7' or maybe '2:4' (2, 3 & 4) or maybe '2,4,6' (2, 4 & 6)
- or maybe '1:*'}
- var
- LPos: integer;
- LStart: Int64;
- LN: Int64;
- LEnd: Int64;
- LTemp: string;
- LContext: TIdIMAP4PeerContext;
- begin
- LContext := TIdIMAP4PeerContext(ASender.Context);
- AMessageNumbers.BeginUpdate;
- try
- AMessageNumbers.Clear;
- //See is it a sequence like 2:4 ...
- LPos := IndyPos(':', AMessageSet); {Do not Localize}
- if LPos > 0 then begin
- LTemp := Copy(AMessageSet, 1, LPos-1);
- LStart := IndyStrToInt64(LTemp);
- LTemp := Copy(AMessageSet, LPos+1, MAXINT);
- if LTemp = '*' then begin {Do not Localize}
- if AUseUID then begin
- LEnd := IndyStrToInt64(LContext.MailBox.UIDNext)-1;
- end else begin
- LEnd := LContext.MailBox.MessageList.Count;
- end;
- end else begin
- LEnd := IndyStrToInt64(LTemp);
- end;
- // RLebeau 2/4/2020: using a 'while' loop instead of a 'for' loop, because the
- // LN variable is an Int64 and Delphi prior to XE8 will fail to compile on it
- // with a "For loop control variable must have ordinal type" error...
- {
- for LN := LStart to LEnd do begin
- AMessageNumbers.Add(IntToStr(LN));
- end;
- }
- LN := LStart;
- while LN <= LEnd do begin
- AMessageNumbers.Add(IntToStr(LN));
- Inc(LN);
- end;
- end else begin
- //See is it a comma-separated list...
- LPos := IndyPos(',', AMessageSet); {Do not Localize}
- if LPos = 0 then begin
- AMessageNumbers.Add(AMessageSet);
- end else begin
- BreakApart(AMessageSet, ',', AMessageNumbers); {Do not Localize}
- end;
- end;
- finally
- AMessageNumbers.EndUpdate;
- end;
- Result := True;
- end;
- //Return -1 if not found
- function TIdIMAP4Server.GetRecordForUID(const AUID: String; AMailBox: TIdMailBox): Int64;
- var
- LN: Integer;
- LUID: Int64;
- begin
- // TODO: do string comparisons instead so that conversions are not needed?
- LUID := IndyStrToInt64(AUID);
- for LN := 0 to AMailBox.MessageList.Count-1 do begin
- if IndyStrToInt64(AMailBox.MessageList.Messages[LN].UID) = LUID then begin
- Result := LN;
- Exit;
- end;
- end;
- Result := -1;
- end;
- function TIdIMAP4Server.StripQuotesIfNecessary(AName: string): string;
- begin
- if Length(AName) > 0 then begin
- if (AName[1] = '"') and (AName[Length(Result)] = '"') then begin {Do not Localize}
- Result := Copy(AName, 2, Length(AName)-2);
- Exit;
- end;
- end;
- Result := AName;
- end;
- function TIdIMAP4Server.ReassembleParams(ASeparator: Char; AParams: TStrings;
- AParamToReassemble: Integer): Boolean;
- var
- LEndSeparator: char;
- LTemp: string;
- LN: integer;
- LReassembledParam: string;
- begin
- Result := False;
- case ASeparator of
- '(': LEndSeparator := ')'; {Do not Localize}
- '[': LEndSeparator := ']'; {Do not Localize}
- else LEndSeparator := ASeparator;
- end;
- LTemp := AParams[AParamToReassemble];
- if (LTemp = '') or (LTemp[1] <> ASeparator) then begin
- Exit;
- end;
- if LTemp[Length(LTemp)] = LEndSeparator then begin
- AParams[AParamToReassemble] := Copy(LTemp, 2, Length(LTemp)-2);
- Result := True;
- Exit;
- end;
- LReassembledParam := Copy(LTemp, 2, MAXINT);
- LN := AParamToReassemble + 1;
- repeat
- if LN >= AParams.Count - 1 then begin
- Result := False;
- Exit; //Error
- end;
- LTemp := AParams[LN];
- AParams.Delete(LN);
- if LTemp[Length(LTemp)] = LEndSeparator then begin
- AParams[AParamToReassemble] := LReassembledParam + ' ' + Copy(LTemp, 1, Length(LTemp)-1); {Do not Localize}
- Result := True;
- Exit; //This is example 1
- end;
- LReassembledParam := LReassembledParam + ' ' + LTemp; {Do not Localize}
- until False;
- end;
- //This reorganizes the parameter list on the basis that AMailBoxParam is a
- //mailbox name, which may (if enclosed in quotes) be in more than one param.
- //Example 1: '43' '"My' 'Documents"' '5' -> '43' 'My Documents' '5'
- //Example 2: '43' '"MyDocs"' '5' -> '43' 'MyDocs' '5'
- //Example 3: '43' 'MyDocs' '5' -> '43' 'MyDocs' '5'
- function TIdIMAP4Server.ReinterpretParamAsMailBox(AParams: TStrings; AMailBoxParam: Integer): Boolean;
- var
- LTemp: string;
- begin
- if (AMailBoxParam < 0) or (AMailBoxParam >= AParams.Count) then begin
- Result := False;
- Exit;
- end;
- LTemp := AParams[AMailBoxParam];
- if LTemp = '' then begin
- Result := False;
- Exit;
- end;
- if LTemp[1] <> '"' then begin {Do not Localize}
- Result := True;
- Exit; //This is example 3, no change.
- end;
- Result := ReassembleParams('"', AParams, AMailBoxParam); {Do not Localize}
- end;
- function TIdIMAP4Server.ReinterpretParamAsFlags(AParams: TStrings; AFlagsParam: Integer): Boolean;
- begin
- Result := ReassembleParams('(', AParams, AFlagsParam); {Do not Localize}
- end;
- function TIdIMAP4Server.ReinterpretParamAsQuotedStr(AParams: TStrings; AFlagsParam: integer): Boolean;
- begin
- Result := ReassembleParams('"', AParams, AFlagsParam); {Do not Localize}
- end;
- function TIdIMAP4Server.ReinterpretParamAsDataItems(AParams: TStrings; AFlagsParam: Integer): Boolean;
- begin
- Result := ReassembleParams('(', AParams, AFlagsParam); {Do not Localize}
- end;
- function TIdIMAP4Server.FlagStringToFlagList(AFlagList: TStrings; AFlagString: string): Boolean;
- var
- LTemp: string;
- begin
- AFlagList.BeginUpdate;
- try
- AFlagList.Clear;
- if (AFlagString <> '') and (AFlagString[1] = '(') and (AFlagString[Length(AFlagString)] = ')') then begin {Do not Localize}
- LTemp := Copy(AFlagString, 2, Length(AFlagString)-2);
- BreakApart(LTemp, ' ', AFlagList); {Do not Localize}
- Result := True;
- end else begin
- Result := False;
- end;
- finally
- AFlagList.EndUpdate;
- end;
- end;
- procedure TIdIMAP4Server.ProcessFetch(AUseUID: Boolean; ASender: TIdCommand; AParams: TStrings);
- //There are a pile of options for this.
- var
- LMessageNumbers: TStringList;
- LDataItems: TStringList;
- LM: integer;
- LN: integer;
- LLO: integer;
- LRecord: Int64;
- LSize: Int64;
- LMessageToCheck, LMessageTemp: TIdMessage;
- LMessageRaw: TStringList;
- LTemp: string;
- LContext: TIdIMAP4PeerContext;
- begin
- LContext := TIdIMAP4PeerContext(ASender.Context);
- //First param is a message set, e.g. 41 or 2:5 (which is 2, 3, 4 & 5)
- LMessageNumbers := TStringList.Create;
- try
- if not MessageSetToMessageNumbers(AUseUID, ASender, LMessageNumbers, AParams[0]) then begin
- SendBadReply(ASender, 'Error in syntax of message set parameter'); {Do not Localize}
- Exit;
- end;
- if not ReinterpretParamAsDataItems(AParams, 1) then begin
- SendBadReply(ASender, 'Fetch data items parameter is invalid.'); {Do not Localize}
- Exit;
- end;
- LDataItems := TStringList.Create;
- try
- BreakApart(AParams[1], ' ', LDataItems);
- for LN := 0 to LMessageNumbers.Count-1 do begin
- if AUseUID then begin
- LRecord := GetRecordForUID(LMessageNumbers[LN], LContext.MailBox);
- if LRecord = -1 then begin //It is OK to skip non-existent UID records
- Continue;
- end;
- end else begin
- LRecord := IndyStrToInt64(LMessageNumbers[LN])-1;
- end;
- if (LRecord < 0) or (LRecord > LContext.MailBox.MessageList.Count) then begin
- SendBadReply(ASender, 'Message number %d does not exist', [LRecord+1]); {Do not Localize}
- Exit;
- end;
- LMessageToCheck := LContext.MailBox.MessageList.Messages[LRecord];
- for LLO := 0 to LDataItems.Count-1 do begin
- if TextIsSame(LDataItems[LLO], 'UID') then begin {Do not Localize}
- //Format:
- //C9 FETCH 490 (UID)
- //* 490 FETCH (UID 6545)
- //C9 OK Completed
- DoSendReply(ASender.Context, '* FETCH (UID %s)', [LMessageToCheck.UID]); {Do not Localize}
- end
- else if TextIsSame(LDataItems[LLO], 'FLAGS') then begin {Do not Localize}
- //Format:
- //C10 UID FETCH 6545 (FLAGS)
- //* 490 FETCH (FLAGS (\Recent) UID 6545)
- //C10 OK Completed
- if AUseUID then begin
- DoSendReply(ASender.Context, '* %d FETCH (FLAGS (%s) UID %s)', {Do not Localize}
- [LRecord+1, MessageFlagSetToStr(LMessageToCheck.Flags), LMessageNumbers[LN]]);
- end else begin
- DoSendReply(ASender.Context, '* %d FETCH (FLAGS (%s))', {Do not Localize}
- [LRecord+1, MessageFlagSetToStr(LMessageToCheck.Flags)]);
- end;
- end
- else if TextIsSame(LDataItems[LLO], 'RFC822.HEADER') then begin {Do not Localize}
- //Format:
- //C11 UID FETCH 6545 (RFC822.HEADER)
- //* 490 FETCH (UID 6545 RFC822.HEADER {1654}
- //Return-Path: <[email protected]>
- //...
- //Content-Type: multipart/alternative;
- // boundary="----=_NextPart_000_70BE_C8606D03.F4EA24EE"
- //C10 OK Completed
- //We don't want to thrash UIDs and flags in MailBox message, so load into LMessage
- LMessageTemp := TIdMessage.Create;
- try
- if not OnDefMechGetMessageHeader(LContext.LoginName, LContext.MailBox.Name, LMessageToCheck, LMessageTemp) then begin
- SendNoReply(ASender, 'Failed to get message header'); {Do not Localize}
- Exit;
- end;
- //Need to calculate the size of the headers...
- LSize := 0;
- for LM := 0 to LMessageTemp.Headers.Count-1 do begin
- Inc(LSize, Length(LMessageTemp.Headers.Strings[LM]) + 2); //Allow for CR+LF
- end;
- if AUseUID then begin
- DoSendReply(ASender.Context, '* %d FETCH (UID %s RFC822.HEADER {%d}', {Do not Localize}
- [LRecord+1, LMessageNumbers[LN], LSize]);
- end else begin
- DoSendReply(ASender.Context, '* %d FETCH (RFC822.HEADER {%d}', {Do not Localize}
- [LRecord+1, LSize]);
- end;
- for LM := 0 to LMessageTemp.Headers.Count-1 do begin
- DoSendReply(ASender.Context, LMessageTemp.Headers.Strings[LM]);
- end;
- DoSendReply(ASender.Context, ')'); {Do not Localize}
- //Finished with the headers, free the memory...
- finally
- FreeAndNil(LMessageTemp);
- end;
- end
- else if TextIsSame(LDataItems[LLO], 'RFC822.SIZE') then begin {Do not Localize}
- //Format:
- //C12 UID FETCH 6545 (RFC822.SIZE)
- //* 490 FETCH (UID 6545 RFC822.SIZE 3447)
- //C12 OK Completed
- LSize := OnDefMechGetMessageSize(LContext.LoginName, LContext.MailBox.Name, LMessageToCheck);
- if LSize = -1 then begin
- SendNoReply(ASender, 'Failed to get message size'); {Do not Localize}
- Exit;
- end;
- if AUseUID then begin
- DoSendReply(ASender.Context, '* %d FETCH (UID %s RFC822.SIZE %d)', {Do not Localize}
- [LRecord+1, LMessageNumbers[LN], LSize]);
- end else begin
- DoSendReply(ASender.Context, '* %d FETCH (RFC822.SIZE %d)', {Do not Localize}
- [LRecord+1, LSize]);
- end;
- end
- else if PosInStrArray(LDataItems[LLO], ['BODY.PEEK[]', 'BODY[]', 'RFC822', 'RFC822.PEEK'], False) <> -1 then {Do not Localize}
- begin
- //All are the same, except the return string is different...
- LMessageRaw := TStringList.Create;
- try
- if not OnDefMechGetMessageRaw(LContext.LoginName, LContext.MailBox.Name, LMessageToCheck, LMessageRaw) then
- begin
- SendNoReply(ASender, 'Failed to get raw message'); {Do not Localize}
- Exit;
- end;
- LSize := 0;
- for LM := 0 to LMessageToCheck.Headers.Count-1 do begin
- Inc(LSize, Length(LMessageRaw.Strings[LM]) + 2); //Allow for CR+LF
- end;
- Inc(LSize, 3); //The message terminator '.CRLF'
- LTemp := Copy(AParams[1], 2, Length(AParams[1])-2);
- if AUseUID then begin
- DoSendReply(ASender.Context, '* %d FETCH (FLAGS (%s) UID %s %s {%d}', {Do not Localize}
- [LRecord+1, MessageFlagSetToStr(LMessageToCheck.Flags), LMessageNumbers[LN], LTemp, LSize]);
- end else begin
- DoSendReply(ASender.Context, '* %d FETCH (FLAGS (%s) %s {%d}', {Do not Localize}
- [LRecord+1, MessageFlagSetToStr(LMessageToCheck.Flags), LTemp, LSize]);
- end;
- for LM := 0 to LMessageToCheck.Headers.Count-1 do begin
- DoSendReply(ASender.Context, LMessageRaw.Strings[LM]);
- end;
- DoSendReply(ASender.Context, '.'); {Do not Localize}
- DoSendReply(ASender.Context, ')'); {Do not Localize}
- //Free the memory...
- finally
- FreeAndNil(LMessageRaw);
- end;
- end
- else if TextIsSame(LDataItems[LLO], 'BODYSTRUCTURE') then begin {Do not Localize}
- //Format:
- //C49 UID FETCH 6545 (BODYSTRUCTURE)
- //* 490 FETCH (UID 6545 BODYSTRUCTURE (("TEXT" "PLAIN" ("CHARSET" "iso-8859-1") NIL NIL "7BIT" 290 8 NIL NIL NIL)("TEXT" "HTML" ("CHARSET" "iso-8859-1") NIL NIL "7BIT" 1125 41 NIL NIL NIL) "ALTERNATIVE" ("BOUNDARY"
- //C12 OK Completed
- SendBadReply(ASender, 'Parameter not supported: ' + AParams[1]); {Do not Localize}
- end
- else if TextStartsWith(LDataItems[LLO], 'BODY[') or TextStartsWith(LDataItems[LLO], 'BODY.PEEK[') then begin {Do not Localize}
- //Format:
- //C50 UID FETCH 6545 (BODY[1])
- //* 490 FETCH (FLAGS (\Recent \Seen) UID 6545 BODY[1] {290}
- //...
- //)
- //C50 OK Completed
- SendBadReply(ASender, 'Parameter not supported: ' + AParams[1]); {Do not Localize}
- end
- else begin
- SendBadReply(ASender, 'Parameter not supported: ' + AParams[1]); {Do not Localize}
- Exit;
- end;
- end;
- end;
- finally
- FreeAndNil(LDataItems);
- end;
- finally
- FreeAndNil(LMessageNumbers);
- end;
- SendOkReply(ASender, 'Completed'); {Do not Localize}
- end;
- procedure TIdIMAP4Server.ProcessSearch(AUseUID: Boolean; ASender: TIdCommand; AParams: TStrings);
- //if AUseUID is True, return UIDs rather than relative message numbers.
- var
- LSearchString: string;
- LN: Integer;
- LM: Integer;
- LItem: Integer;
- LMessageToCheck, LMessageTemp: TIdMessage;
- LHits: string;
- LContext: TIdIMAP4PeerContext;
- begin
- LContext := TIdIMAP4PeerContext(ASender.Context);
- //Watch out: you could become an old man trying to implement all the IMAP
- //search options, just do a subset.
- //Format:
- //C1065 UID SEARCH FROM "visible"
- //* SEARCH 5769 5878
- //C1065 OK Completed (2 msgs in 0.010 secs)
- if AParams.Count < 2 then begin //The only search options we support are 2-param ones
- SendIncorrectNumberOfParameters(ASender);
- //LParams.Free;
- Exit;
- end;
- LItem := PosInStrArray(AParams[0], ['FROM', 'TO', 'CC', 'BCC', 'SUBJECT'], False);
- if LItem = -1 then begin {Do not Localize}
- SendBadReply(ASender, 'Unsupported search method'); {Do not Localize}
- Exit;
- end;
- //Reassemble the other params into a line, because "Ciaran Costelloe" will be params 1 & 2...
- LSearchString := AParams[1];
- for LN := 2 to AParams.Count-1 do begin
- LSearchString := LSearchString + ' ' + AParams[LN]; {Do not Localize}
- end;
- if (LSearchString[1] = '"') and (LSearchString[Length(LSearchString)] = '"') then begin {Do not Localize}
- LSearchString := Copy(LSearchString, 2, Length(LSearchString)-2);
- end;
- LHits := '';
- LMessageTemp := TIdMessage.Create;
- try
- for LN := 0 to LContext.MailBox.MessageList.Count-1 do begin
- LMessageToCheck := LContext.MailBox.MessageList.Messages[LN];
- if not OnDefMechGetMessageHeader(LContext.LoginName, LContext.MailBox.Name, LMessageToCheck, LMessageTemp) then
- begin
- SendNoReply(ASender, 'Failed to get message header'); {Do not Localize}
- Exit;
- end;
- case LItem of
- 0: // FROM {Do not Localize}
- begin
- if Pos(UpperCase(LSearchString), UpperCase(LMessageTemp.From.Address)) > 0 then begin
- if AUseUID then begin
- LHits := LHits + LMessageToCheck.UID + ' '; {Do not Localize}
- end else begin
- LHits := LHits + IntToStr(LN+1) + ' '; {Do not Localize}
- end;
- end;
- end;
- 1: // TO {Do not Localize}
- begin
- for LM := 0 to LMessageTemp.Recipients.Count-1 do begin
- if Pos(UpperCase(LSearchString), UpperCase(LMessageTemp.Recipients.Items[LM].Address)) > 0 then begin
- if AUseUID then begin
- LHits := LHits + LMessageToCheck.UID + ' '; {Do not Localize}
- end else begin
- LHits := LHits + IntToStr(LN+1) + ' '; {Do not Localize}
- end;
- Break; //Don't want more than 1 hit on this record
- end;
- end;
- end;
- 2: // CC {Do not Localize}
- begin
- for LM := 0 to LMessageTemp.Recipients.Count-1 do begin
- if Pos(UpperCase(LSearchString), UpperCase(LMessageTemp.CCList.Items[LM].Address)) > 0 then begin
- if AUseUID then begin
- LHits := LHits + LMessageToCheck.UID + ' '; {Do not Localize}
- end else begin
- LHits := LHits + IntToStr(LN+1) + ' '; {Do not Localize}
- end;
- Break; //Don't want more than 1 hit on this record
- end;
- end;
- end;
- 3: // BCC {Do not Localize}
- begin
- for LM := 0 to LMessageTemp.Recipients.Count-1 do begin
- if Pos(UpperCase(LSearchString), UpperCase(LMessageTemp.BCCList.Items[LM].Address)) > 0 then begin
- if AUseUID then begin
- LHits := LHits + LMessageToCheck.UID + ' '; {Do not Localize}
- end else begin
- LHits := LHits + IntToStr(LN+1) + ' '; {Do not Localize}
- end;
- Break; //Don't want more than 1 hit on this record
- end;
- end;
- end;
- else // SUBJECT {Do not Localize}
- begin
- if Pos(UpperCase(LSearchString), UpperCase(LMessageTemp.Subject)) > 0 then begin
- if AUseUID then begin
- LHits := LHits + LMessageToCheck.UID + ' '; {Do not Localize}
- end else begin
- LHits := LHits + IntToStr(LN+1) + ' '; {Do not Localize}
- end;
- end;
- end;
- end;
- end;
- finally
- FreeAndNil(LMessageTemp);
- end;
- DoSendReply(ASender.Context, '* SEARCH ' + TrimRight(LHits)); {Do not Localize}
- SendOkReply(ASender, 'Completed'); {Do not Localize}
- end;
- procedure TIdIMAP4Server.ProcessCopy(AUseUID: Boolean; ASender: TIdCommand; AParams: TStrings);
- var
- LMessageNumbers: TStringList;
- LN: Integer;
- LRecord: Int64;
- LResult: Boolean;
- LContext: TIdIMAP4PeerContext;
- begin
- LContext := TIdIMAP4PeerContext(ASender.Context);
- //Format is "C1 COPY 2:4 MEETINGFOLDER"
- if AParams.Count < 2 then begin
- SendIncorrectNumberOfParameters(ASender);
- Exit;
- end;
- if not OnDefMechReinterpretParamAsMailBox(AParams, 1) then begin
- SendBadReply(ASender, 'Mailbox parameter is invalid.'); {Do not Localize}
- Exit;
- end;
- //First param is a message set, e.g. 41 or 2:5 (which is 2, 3, 4 & 5)
- LMessageNumbers := TStringList.Create;
- try
- if not MessageSetToMessageNumbers(AUseUID, ASender, LMessageNumbers, AParams[0]) then begin
- SendBadReply(ASender, 'Error in syntax of message set parameter'); {Do not Localize}
- Exit;
- end;
- if not Assigned(OnDefMechDoesImapMailBoxExist) then begin
- SendUnassignedDefaultMechanism(ASender);
- Exit;
- end;
- if not OnDefMechDoesImapMailBoxExist(LContext.LoginName, AParams[1]) then begin
- SendNoReply(ASender, 'Mailbox does not exist.'); {Do not Localize}
- Exit;
- end;
- LResult := True;
- for LN := 0 to LMessageNumbers.Count-1 do begin
- if AUseUID then begin
- LRecord := GetRecordForUID(LMessageNumbers[LN], LContext.MailBox);
- if LRecord = -1 then begin //It is OK to skip non-existent UID records
- Continue;
- end;
- end else begin
- LRecord := IndyStrToInt64(LMessageNumbers[LN])-1;
- end;
- if (LRecord < 0) or (LRecord >= LContext.MailBox.MessageList.Count) then begin
- LResult := False;
- end
- else if not OnDefMechCopyMessage(LContext.LoginName, LContext.MailBox.Name,
- LContext.MailBox.MessageList.Messages[LRecord].UID, AParams[1]) then
- begin
- LResult := False;
- end;
- end;
- if LResult then begin
- SendOkReply(ASender, 'Completed'); {Do not Localize}
- end else begin
- SendNoReply(ASender, 'Copy failed for one or more messages'); {Do not Localize}
- end;
- finally
- FreeAndNil(LMessageNumbers);
- end;
- end;
- function TIdIMAP4Server.ProcessStore(AUseUID: Boolean; ASender: TIdCommand; AParams: TStrings): Boolean;
- const
- LCMsgFlags: array[0..4] of TIdMessageFlags = ( mfAnswered, mfFlagged, mfDeleted, mfDraft, mfSeen );
- var
- LMessageNumbers: TStringList;
- LFlagList: TStringList;
- LN: integer;
- LM: integer;
- LRecord: Int64;
- LFlag: integer;
- LTemp: string;
- LStoreMethod: TIdIMAP4StoreDataItem;
- LSilent: Boolean;
- LMessage: TIdMessage;
- LContext: TIdIMAP4PeerContext;
- begin
- LContext := TIdIMAP4PeerContext(ASender.Context);
- //Format is:
- //C53 UID STORE 6545,6544 +FLAGS.SILENT (\Deleted)
- //C53 OK Completed
- Result := False;
- if AParams.Count < 3 then begin
- SendIncorrectNumberOfParameters(ASender);
- Exit;
- end;
- //First param is a message set, e.g. 41 or 2:5 (which is 2, 3, 4 & 5)
- LMessageNumbers := TStringList.Create;
- try
- if not MessageSetToMessageNumbers(AUseUID, ASender, LMessageNumbers, AParams[0]) then begin
- SendBadReply(ASender, 'Error in syntax of message set parameter'); {Do not Localize}
- Exit;
- end;
- LTemp := AParams[1];
- if LTemp[1] = '+' then begin {Do not Localize}
- LStoreMethod := sdAdd;
- LTemp := Copy(LTemp, 2, MaxInt);
- end else if LTemp[1] = '-' then begin {Do not Localize}
- LStoreMethod := sdRemove;
- LTemp := Copy(LTemp, 2, MaxInt);
- end else begin
- LStoreMethod := sdReplace;
- end;
- if TextIsSame(LTemp, 'FLAGS') then begin {Do not Localize}
- LSilent := False;
- end else if TextIsSame(LTemp, 'FLAGS.SILENT') then begin {Do not Localize}
- LSilent := True;
- end else begin
- SendBadReply(ASender, 'Error in syntax of FLAGS parameter'); {Do not Localize}
- Exit;
- end;
- LFlagList := TStringList.Create;
- try
- //Assemble remaining flags back into a string...
- LTemp := AParams[2];
- for LN := 3 to AParams.Count-1 do begin
- LTemp := LTemp + ' ' + AParams[LN]; {Do not Localize}
- end;
- if not FlagStringToFlagList(LFlagList, LTemp) then begin
- SendBadReply(ASender, 'Error in syntax of flag set parameter'); {Do not Localize}
- Exit;
- end;
- for LN := 0 to LMessageNumbers.Count-1 do begin
- if AUseUID then begin
- LRecord := GetRecordForUID(LMessageNumbers[LN], LContext.MailBox);
- if LRecord = -1 then begin //It is OK to skip non-existent UID records
- Continue;
- end;
- end else begin
- LRecord := IndyStrToInt64(LMessageNumbers[LN])-1;
- end;
- if (LRecord < 0) or (LRecord > LContext.MailBox.MessageList.Count) then begin
- SendBadReply(ASender, 'Message number %d does not exist', [LRecord+1]); {Do not Localize}
- Exit;
- end;
- LMessage := LContext.MailBox.MessageList.Messages[LRecord];
- if LStoreMethod = sdReplace then begin
- LMessage.Flags := [];
- end;
- for LM := 0 to LFlagList.Count-1 do begin
- //Support \Answered \Flagged \Deleted \Draft \Seen
- LFlag := PosInStrArray(LFlagList[LM], ['\Answered', '\Flagged', '\Deleted', '\Draft', '\Seen'], False); {Do not Localize}
- if LFlag = -1 then begin
- Continue;
- end;
- case LStoreMethod of
- sdAdd, sdReplace:
- begin
- LMessage.Flags := LMessage.Flags + [LCMsgFlags[LFlag]];
- end;
- sdRemove:
- begin
- LMessage.Flags := LMessage.Flags - [LCMsgFlags[LFlag]];
- end;
- end;
- end;
- if not LSilent then begin
- //In this case, send to the client the current flags.
- //The response is '* 43 FETCH (FLAGS (\Seen))' with the UID version
- //being '* 43 FETCH (FLAGS (\Seen) UID 1234)'. Note the first number is the
- //relative message number in BOTH cases.
- if AUseUID then begin
- DoSendReply(ASender.Context, '* %d FETCH (FLAGS (%s) UID %s)', {Do not Localize}
- [LRecord+1, MessageFlagSetToStr(LMessage.Flags), LMessageNumbers[LN]]);
- end else begin
- DoSendReply(ASender.Context, '* %d FETCH (FLAGS (%s))', {Do not Localize}
- [LRecord+1, MessageFlagSetToStr(LMessage.Flags)]);
- end;
- end;
- end;
- SendOkReply(ASender, 'STORE Completed'); {Do not Localize}
- finally
- FreeAndNil(LFlagList);
- end;
- finally
- FreeAndNil(LMessageNumbers);
- end;
- Result := True;
- end;
- procedure TIdIMAP4Server.InitializeCommandHandlers;
- var
- LCommandHandler: TIdCommandHandler;
- begin
- LCommandHandler := CommandHandlers.Add;
- LCommandHandler.Command := 'CAPABILITY'; {do not localize}
- LCommandHandler.OnCommand := DoCommandCAPABILITY;
- LCommandHandler.NormalReply.Code := IMAP_OK;
- LCommandHandler := CommandHandlers.Add;
- LCommandHandler.Command := 'NOOP'; {do not localize}
- LCommandHandler.OnCommand := DoCommandNOOP;
- LCommandHandler.NormalReply.Code := IMAP_OK;
- LCommandHandler := CommandHandlers.Add;
- LCommandHandler.Command := 'LOGOUT'; {do not localize}
- LCommandHandler.OnCommand := DoCommandLOGOUT;
- LCommandHandler.NormalReply.Code := IMAP_OK;
- LCommandHandler := CommandHandlers.Add;
- LCommandHandler.Command := 'AUTHENTICATE'; {do not localize}
- LCommandHandler.OnCommand := DoCommandAUTHENTICATE;
- LCommandHandler.NormalReply.Code := IMAP_OK;
- LCommandHandler := CommandHandlers.Add;
- LCommandHandler.Command := 'LOGIN'; {do not localize}
- LCommandHandler.OnCommand := DoCommandLOGIN;
- LCommandHandler.NormalReply.Code := IMAP_OK;
- LCommandHandler := CommandHandlers.Add;
- LCommandHandler.Command := 'SELECT'; {do not localize}
- LCommandHandler.OnCommand := DoCommandSELECT;
- LCommandHandler.NormalReply.Code := IMAP_OK;
- LCommandHandler := CommandHandlers.Add;
- LCommandHandler.Command := 'EXAMINE'; {do not localize}
- LCommandHandler.OnCommand := DoCommandEXAMINE;
- LCommandHandler.NormalReply.Code := IMAP_OK;
- LCommandHandler := CommandHandlers.Add;
- LCommandHandler.Command := 'CREATE'; {do not localize}
- LCommandHandler.OnCommand := DoCommandCREATE;
- LCommandHandler.NormalReply.Code := IMAP_OK;
- LCommandHandler := CommandHandlers.Add;
- LCommandHandler.Command := 'DELETE'; {do not localize}
- LCommandHandler.OnCommand := DoCommandDELETE;
- LCommandHandler.NormalReply.Code := IMAP_OK;
- LCommandHandler := CommandHandlers.Add;
- LCommandHandler.Command := 'RENAME'; {do not localize}
- LCommandHandler.OnCommand := DoCommandRENAME;
- LCommandHandler.NormalReply.Code := IMAP_OK;
- LCommandHandler := CommandHandlers.Add;
- LCommandHandler.Command := 'SUBSCRIBE'; {do not localize}
- LCommandHandler.OnCommand := DoCommandSUBSCRIBE;
- LCommandHandler.NormalReply.Code := IMAP_OK;
- LCommandHandler := CommandHandlers.Add;
- LCommandHandler.Command := 'UNSUBSCRIBE'; {do not localize}
- LCommandHandler.OnCommand := DoCommandUNSUBSCRIBE;
- LCommandHandler.NormalReply.Code := IMAP_OK;
- LCommandHandler := CommandHandlers.Add;
- LCommandHandler.Command := 'LIST'; {do not localize}
- LCommandHandler.OnCommand := DoCommandLIST;
- LCommandHandler.NormalReply.Code := IMAP_OK;
- LCommandHandler := CommandHandlers.Add;
- LCommandHandler.Command := 'LSUB'; {do not localize}
- LCommandHandler.OnCommand := DoCommandLSUB;
- LCommandHandler.NormalReply.Code := IMAP_OK;
- LCommandHandler := CommandHandlers.Add;
- LCommandHandler.Command := 'STATUS'; {do not localize}
- LCommandHandler.OnCommand := DoCommandSTATUS;
- LCommandHandler.NormalReply.Code := IMAP_OK;
- LCommandHandler := CommandHandlers.Add;
- LCommandHandler.Command := 'APPEND'; {do not localize}
- LCommandHandler.OnCommand := DoCommandAPPEND;
- LCommandHandler.NormalReply.Code := IMAP_OK;
- LCommandHandler := CommandHandlers.Add;
- LCommandHandler.Command := 'CHECK'; {do not localize}
- LCommandHandler.OnCommand := DoCommandCHECK;
- LCommandHandler.NormalReply.Code := IMAP_OK;
- LCommandHandler := CommandHandlers.Add;
- LCommandHandler.Command := 'CLOSE'; {do not localize}
- LCommandHandler.OnCommand := DoCommandCLOSE;
- LCommandHandler.NormalReply.Code := IMAP_OK;
- LCommandHandler := CommandHandlers.Add;
- LCommandHandler.Command := 'EXPUNGE'; {do not localize}
- LCommandHandler.OnCommand := DoCommandEXPUNGE;
- LCommandHandler.NormalReply.Code := IMAP_OK;
- LCommandHandler := CommandHandlers.Add;
- LCommandHandler.Command := 'SEARCH'; {do not localize}
- LCommandHandler.OnCommand := DoCommandSEARCH;
- LCommandHandler.NormalReply.Code := IMAP_OK;
- LCommandHandler := CommandHandlers.Add;
- LCommandHandler.Command := 'FETCH'; {do not localize}
- LCommandHandler.OnCommand := DoCommandFETCH;
- LCommandHandler.NormalReply.Code := IMAP_OK;
- LCommandHandler := CommandHandlers.Add;
- LCommandHandler.Command := 'STORE'; {do not localize}
- LCommandHandler.OnCommand := DoCommandSTORE;
- LCommandHandler.NormalReply.Code := IMAP_OK;
- LCommandHandler := CommandHandlers.Add;
- LCommandHandler.Command := 'COPY'; {do not localize}
- LCommandHandler.OnCommand := DoCommandCOPY;
- LCommandHandler.NormalReply.Code := IMAP_OK;
- LCommandHandler := CommandHandlers.Add;
- LCommandHandler.Command := 'UID'; {do not localize}
- LCommandHandler.OnCommand := DoCommandUID;
- LCommandHandler.NormalReply.Code := IMAP_OK;
- LCommandHandler := CommandHandlers.Add;
- LCommandHandler.Command := 'X'; {do not localize}
- LCommandHandler.OnCommand := DoCommandX;
- LCommandHandler.NormalReply.Code := IMAP_OK;
- LCommandHandler := CommandHandlers.Add;
- LCommandHandler.Command := 'STARTTLS'; {do not localize}
- LCommandHandler.OnCommand := DoCommandSTARTTLS;
- LCommandHandler.NormalReply.Code := IMAP_OK;
- FCommandHandlers.OnBeforeCommandHandler := DoBeforeCmd;
- FCommandHandlers.OnCommandHandlersException := DoCmdHandlersException;
- end;
- //Command handlers
- procedure TIdIMAP4Server.DoBeforeCmd(ASender: TIdCommandHandlers; var AData: string;
- AContext: TIdContext);
- begin
- TIdIMAP4PeerContext(AContext).FLastCommand.ParseRequest(AData); //Main purpose is to get sequence number, like C11 from 'C11 CAPABILITY'
- TIdIMAP4PeerContext(AContext).FIMAP4Tag := Fetch(AData, ' ');
- AData := Trim(AData);
- if Assigned(FOnBeforeCmd) then begin
- FOnBeforeCmd(ASender, AData, AContext);
- end;
- end;
- procedure TIdIMAP4Server.DoSendReply(AContext: TIdContext; const AData: string);
- begin
- if Assigned(FOnBeforeSend) then begin
- FOnBeforeSend(AContext, AData);
- end;
- AContext.Connection.IOHandler.WriteLn(AData);
- end;
- procedure TIdIMAP4Server.DoSendReply(AContext: TIdContext; const AFormat: string; const Args: array of const);
- begin
- DoSendReply(AContext, IndyFormat(AFormat, Args));
- end;
- procedure TIdIMAP4Server.DoCmdHandlersException(ACommand: String; AContext: TIdContext);
- var
- LTag, LCmd: String;
- begin
- if Assigned(FOnCommandError) then begin
- LTag := Fetch(ACommand, ' ');
- LCmd := Fetch(ACommand, ' ');
- OnCommandError(AContext, LTag, LCmd);
- end;
- end;
- procedure TIdIMAP4Server.DoCommandCAPABILITY(ASender: TIdCommand);
- begin
- if Assigned(FOnCommandCAPABILITY) then begin
- OnCommandCAPABILITY(ASender.Context, TIdIMAP4PeerContext(ASender.Context).IMAP4Tag, ASender.UnparsedParams);
- Exit;
- end;
- if not FUseDefaultMechanismsForUnassignedCommands then begin
- Exit;
- end;
- {Tell the client our capabilities...}
- DoSendReply(ASender.Context, '* CAPABILITY IMAP4rev1 AUTH=PLAIN'); {Do not Localize}
- SendOkReply(ASender, 'Completed'); {Do not Localize}
- end;
- procedure TIdIMAP4Server.DoCommandNOOP(ASender: TIdCommand);
- begin
- if Assigned(FOnCommandNOOP) then begin
- OnCommandNOOP(ASender.Context, TIdIMAP4PeerContext(ASender.Context).IMAP4Tag, ASender.UnparsedParams);
- Exit;
- end;
- if not FUseDefaultMechanismsForUnassignedCommands then begin
- Exit;
- end;
- {On most servers, this does nothing (they use a timeout to disconnect users,
- irrespective of NOOP commands, so they always return OK. If you really
- want to implement it, use a countdown timer to force disconnects but reset
- the counter if ANY command received, including NOOP.}
- SendOkReply(ASender, 'Completed'); {Do not Localize}
- end;
- procedure TIdIMAP4Server.DoCommandLOGOUT(ASender: TIdCommand);
- var
- LContext: TIdIMAP4PeerContext;
- begin
- LContext := TIdIMAP4PeerContext(ASender.Context);
- if Assigned(FOnCommandLOGOUT) then begin
- OnCommandLOGOUT(ASender.Context, LContext.IMAP4Tag, ASender.UnparsedParams);
- Exit;
- end;
- if not FUseDefaultMechanismsForUnassignedCommands then begin
- Exit;
- end;
- {Be nice and say ByeBye first...}
- DoSendReply(ASender.Context, '* BYE May your God go with you.'); {Do not Localize}
- SendOkReply(ASender, 'Completed'); {Do not Localize}
- LContext.Connection.Disconnect(False);
- LContext.MailBox.Clear;
- LContext.RemoveFromList;
- end;
- procedure TIdIMAP4Server.DoCommandAUTHENTICATE(ASender: TIdCommand);
- begin
- if Assigned(FOnCommandAUTHENTICATE) then begin
- {
- Important, when usng TLS and FUseTLS=utUseRequireTLS, do not accept any authentication
- information until TLS negotiation is completed. This insistance is a security feature.
- Some networks should choose security over interoperability while other places may
- sacrafice interoperability over security. It comes down to sensible administrative
- judgement.
- }
- if (FUseTLS = utUseRequireTLS) and (not TIdIMAP4PeerContext(ASender.Context).UsingTLS) then begin
- MustUseTLS(ASender);
- end else begin
- OnCommandAUTHENTICATE(ASender.Context, TIdIMAP4PeerContext(ASender.Context).IMAP4Tag, ASender.UnparsedParams);
- end;
- end;
- end;
- procedure TIdIMAP4Server.MustUseTLS(ASender: TIdCommand);
- begin
- DoSendReply(ASender.Context, 'NO ' + RSSMTPSvrReqSTARTTLS); {Do not Localize}
- ASender.Disconnect := True;
- end;
- procedure TIdIMAP4Server.DoCommandLOGIN(ASender: TIdCommand);
- var
- LParams: TStringList;
- LContext: TIdIMAP4PeerContext;
- begin
- LContext := TIdIMAP4PeerContext(ASender.Context);
- if Assigned(fOnCommandLOGIN) then begin
- {
- Important, when using TLS and FUseTLS=utUseRequireTLS, do not accept any authentication
- information until TLS negotiation is completed. This insistance is a security feature.
- Some networks should choose security over interoperability while other places may
- sacrafice interoperability over security. It comes down to sensible administrative
- judgement.
- }
- if (FUseTLS = utUseRequireTLS) and (not TIdIMAP4PeerContext(ASender.Context).UsingTLS) then begin
- MustUseTLS(ASender);
- end else begin
- OnCommandLOGIN(ASender.Context, LContext.IMAP4Tag, ASender.UnparsedParams);
- end;
- Exit;
- end;
- if not FUseDefaultMechanismsForUnassignedCommands then begin
- Exit;
- end;
- if not Assigned(OnDefMechDoesImapMailBoxExist) then begin
- SendUnassignedDefaultMechanism(ASender);
- Exit;
- end;
- LParams := TStringList.Create;
- try
- BreakApart(ASender.UnparsedParams, ' ', LParams); {Do not Localize}
- if LParams.Count < 2 then begin
- //Incorrect number of params...
- if FSaferMode then begin
- SendNoReply(ASender);
- end else begin
- SendIncorrectNumberOfParameters(ASender);
- end;
- Exit;
- end;
- //See if we have a directory under FRootPath of that user's name...
- //if DoesImapMailBoxExist(LParams[0], '') = False then begin
- if not OnDefMechDoesImapMailBoxExist(LParams[0], '') then begin
- if FSaferMode then begin
- SendNoReply(ASender);
- end else begin
- SendNoReply(ASender, 'Unknown username'); {Do not Localize}
- end;
- Exit;
- end;
- //See is it the correct password...
- if not TextIsSame(FDefaultPassword, LParams[1]) then begin
- if FSaferMode then begin
- SendNoReply(ASender);
- end else begin
- SendNoReply(ASender, 'Incorrect password'); {Do not Localize}
- end;
- Exit;
- end;
- //Successful login, change context's state to logged in...
- LContext.LoginName := LParams[0];
- LContext.FConnectionState := csAuthenticated;
- SendOkReply(ASender, 'Completed'); {Do not Localize}
- finally
- FreeAndNil(LParams);
- end;
- end;
- //SELECT and EXAMINE are the same except EXAMINE opens the mailbox read-only
- procedure TIdIMAP4Server.DoCommandSELECT(ASender: TIdCommand);
- var
- LContext: TIdIMAP4PeerContext;
- begin
- LContext := TIdIMAP4PeerContext(ASender.Context);
- if LContext.ConnectionState = csSelected then begin
- LContext.MailBox.Clear;
- LContext.FConnectionState := csAuthenticated;
- end;
- if LContext.ConnectionState <> csAuthenticated then begin
- SendWrongConnectionState(ASender);
- Exit;
- end;
- if Assigned(FOnCommandSELECT) then begin
- OnCommandSELECT(ASender.Context, LContext.IMAP4Tag, ASender.UnparsedParams);
- Exit;
- end;
- if not FUseDefaultMechanismsForUnassignedCommands then begin
- Exit;
- end;
- if not Assigned(OnDefMechOpenMailBox) then begin
- SendUnassignedDefaultMechanism(ASender);
- Exit;
- end;
- if OnDefMechOpenMailBox(ASender, False) then begin //SELECT opens the mailbox read-write
- LContext.FConnectionState := csSelected;
- SendOkReply(ASender, '[READ-WRITE] Completed'); {Do not Localize}
- end;
- end;
- //SELECT and EXAMINE are the same except EXAMINE opens the mailbox read-only
- procedure TIdIMAP4Server.DoCommandEXAMINE(ASender: TIdCommand);
- var
- LContext: TIdIMAP4PeerContext;
- begin
- LContext := TIdIMAP4PeerContext(ASender.Context);
- if not (LContext.ConnectionState in [csAuthenticated, csSelected]) then begin
- SendWrongConnectionState(ASender);
- Exit;
- end;
- if Assigned(FOnCommandEXAMINE) then begin
- OnCommandEXAMINE(ASender.Context, LContext.IMAP4Tag, ASender.UnparsedParams);
- Exit;
- end;
- if not FUseDefaultMechanismsForUnassignedCommands then begin
- Exit;
- end;
- if not Assigned(OnDefMechOpenMailBox) then begin
- SendUnassignedDefaultMechanism(ASender);
- Exit;
- end;
- if OnDefMechOpenMailBox(ASender, True) then begin //EXAMINE opens the mailbox read-only
- LContext.FConnectionState := csSelected;
- SendOkReply(ASender, '[READ-ONLY] Completed'); {Do not Localize}
- end;
- end;
- procedure TIdIMAP4Server.DoCommandCREATE(ASender: TIdCommand);
- var
- LParams: TStringList;
- LContext: TIdIMAP4PeerContext;
- begin
- LContext := TIdIMAP4PeerContext(ASender.Context);
- if not (LContext.ConnectionState in [csAuthenticated, csSelected]) then begin
- SendWrongConnectionState(ASender);
- Exit;
- end;
- {
- if LContext.MailBox.State = msReadOnly then begin
- SendErrorOpenedReadOnly(ASender);
- Exit;
- end;
- }
- if Assigned(FOnCommandCREATE) then begin
- OnCommandCREATE(ASender.Context, LContext.IMAP4Tag, ASender.UnparsedParams);
- Exit;
- end;
- if not FUseDefaultMechanismsForUnassignedCommands then begin
- Exit;
- end;
- if (not Assigned(OnDefMechReinterpretParamAsMailBox))
- or (not Assigned(OnDefMechDoesImapMailBoxExist))
- or (not Assigned(OnDefMechCreateMailBox)) then
- begin
- SendUnassignedDefaultMechanism(ASender);
- Exit;
- end;
- LParams := TStringList.Create;
- try
- BreakApart(ASender.UnparsedParams, ' ', LParams); {Do not Localize}
- if LParams.Count < 1 then begin
- //Incorrect number of params...
- SendIncorrectNumberOfParameters(ASender);
- Exit;
- end;
- if not OnDefMechReinterpretParamAsMailBox(LParams, 0) then begin
- SendBadReply(ASender, 'Mailbox parameter is invalid.'); {Do not Localize}
- Exit;
- end;
- if OnDefMechDoesImapMailBoxExist(LContext.LoginName, LParams[0]) then begin
- SendBadReply(ASender, 'Mailbox already exists.'); {Do not Localize}
- Exit;
- end;
- if OnDefMechCreateMailBox(LContext.LoginName, LParams[0]) then begin
- SendOkReply(ASender, 'Completed'); {Do not Localize}
- end else begin
- SendNoReply(ASender, 'Create failed'); {Do not Localize}
- end;
- finally
- FreeAndNil(LParams);
- end;
- end;
- procedure TIdIMAP4Server.DoCommandDELETE(ASender: TIdCommand);
- var
- LParams: TStringList;
- LContext: TIdIMAP4PeerContext;
- begin
- LContext := TIdIMAP4PeerContext(ASender.Context);
- if not (LContext.ConnectionState in [csAuthenticated, csSelected]) then begin
- SendWrongConnectionState(ASender);
- Exit;
- end;
- {
- if LContext.MailBox.State = msReadOnly then begin
- SendErrorOpenedReadOnly(ASender);
- Exit;
- end;
- }
- if Assigned(FOnCommandDELETE) then begin
- OnCommandDELETE(ASender.Context, LContext.IMAP4Tag, ASender.UnparsedParams);
- Exit;
- end;
- if not FUseDefaultMechanismsForUnassignedCommands then begin
- Exit;
- end;
- if (not Assigned(OnDefMechDoesImapMailBoxExist))
- or (not Assigned(OnDefMechReinterpretParamAsMailBox))
- or (not Assigned(OnDefMechDeleteMailBox))
- or (not Assigned(OnDefMechIsMailBoxOpen)) then
- begin
- SendUnassignedDefaultMechanism(ASender);
- Exit;
- end;
- //Make sure we don't have the mailbox open by anyone
- LParams := TStringList.Create;
- try
- BreakApart(ASender.UnparsedParams, ' ', LParams); {Do not Localize}
- if LParams.Count < 1 then begin
- //Incorrect number of params...
- SendIncorrectNumberOfParameters(ASender);
- Exit;
- end;
- if not OnDefMechReinterpretParamAsMailBox(LParams, 0) then begin
- SendBadReply(ASender, 'Mailbox parameter is invalid.'); {Do not Localize}
- Exit;
- end;
- if OnDefMechIsMailBoxOpen(LContext.LoginName, LParams[0]) then begin
- SendNoReply(ASender, 'Mailbox is in use.'); {Do not Localize}
- Exit;
- end;
- if not OnDefMechDoesImapMailBoxExist(LContext.LoginName, LParams[0]) then begin
- SendNoReply(ASender, 'Mailbox does not exist.'); {Do not Localize}
- Exit;
- end;
- if OnDefMechDeleteMailBox(LContext.LoginName, LParams[0]) then begin
- SendOkReply(ASender, 'Completed'); {Do not Localize}
- end else begin
- SendNoReply(ASender, 'Delete failed'); {Do not Localize}
- end;
- finally
- FreeAndNil(LParams);
- end;
- end;
- procedure TIdIMAP4Server.DoCommandRENAME(ASender: TIdCommand);
- var
- LParams: TStringList;
- LContext: TIdIMAP4PeerContext;
- begin
- LContext := TIdIMAP4PeerContext(ASender.Context);
- if not (LContext.ConnectionState in [csAuthenticated, csSelected]) then begin
- SendWrongConnectionState(ASender);
- Exit;
- end;
- {
- if LContext.MailBox.State = msReadOnly then begin
- SendErrorOpenedReadOnly(ASender);
- Exit;
- end;
- }
- if Assigned(FOnCommandRENAME) then begin
- OnCommandRENAME(ASender.Context, LContext.IMAP4Tag, ASender.UnparsedParams);
- Exit;
- end;
- if not FUseDefaultMechanismsForUnassignedCommands then begin
- Exit;
- end;
- if (not Assigned(OnDefMechDoesImapMailBoxExist))
- or (not Assigned(OnDefMechReinterpretParamAsMailBox))
- or (not Assigned(OnDefMechRenameMailBox))
- or (not Assigned(OnDefMechIsMailBoxOpen)) then
- begin
- SendUnassignedDefaultMechanism(ASender);
- Exit;
- end;
- //Make sure we don't have the mailbox open by anyone
- LParams := TStringList.Create;
- try
- BreakApart(ASender.UnparsedParams, ' ', LParams); {Do not Localize}
- if LParams.Count < 2 then begin
- //Incorrect number of params...
- SendIncorrectNumberOfParameters(ASender);
- Exit;
- end;
- if not OnDefMechReinterpretParamAsMailBox(LParams, 0) then begin
- SendBadReply(ASender, 'First mailbox parameter is invalid.'); {Do not Localize}
- Exit;
- end;
- if OnDefMechIsMailBoxOpen(LContext.LoginName, LParams[0]) then begin
- SendNoReply(ASender, 'Mailbox is in use.'); {Do not Localize}
- Exit;
- end;
- if not OnDefMechReinterpretParamAsMailBox(LParams, 1) then begin
- SendBadReply(ASender, 'Second mailbox parameter is invalid.'); {Do not Localize}
- Exit;
- end;
- if not OnDefMechDoesImapMailBoxExist(LContext.LoginName, LParams[0]) then begin
- SendNoReply(ASender, 'Mailbox to be renamed does not exist.'); {Do not Localize}
- Exit;
- end;
- if OnDefMechDoesImapMailBoxExist(LContext.LoginName, LParams[1]) then begin
- SendNoReply(ASender, 'Destination mailbox already exists.'); {Do not Localize}
- Exit;
- end;
- if OnDefMechRenameMailBox(LContext.LoginName, LParams[0], LParams[1]) then begin
- SendOkReply(ASender, 'Completed'); {Do not Localize}
- end else begin
- SendNoReply(ASender, 'Delete failed'); {Do not Localize}
- end;
- finally
- FreeAndNil(LParams);
- end;
- end;
- procedure TIdIMAP4Server.DoCommandSUBSCRIBE(ASender: TIdCommand);
- var
- LContext: TIdIMAP4PeerContext;
- begin
- LContext := TIdIMAP4PeerContext(ASender.Context);
- if LContext.MailBox.State = msReadOnly then begin
- SendErrorOpenedReadOnly(ASender);
- Exit;
- end;
- if Assigned(FOnCommandSUBSCRIBE) then begin
- OnCommandSUBSCRIBE(ASender.Context, LContext.IMAP4Tag, ASender.UnparsedParams);
- Exit;
- end;
- if not FUseDefaultMechanismsForUnassignedCommands then begin
- Exit;
- end;
- {Not clear exactly what this would do in this sample mechanism...}
- SendUnsupportedCommand(ASender);
- end;
- procedure TIdIMAP4Server.DoCommandUNSUBSCRIBE(ASender: TIdCommand);
- var
- LContext: TIdIMAP4PeerContext;
- begin
- LContext := TIdIMAP4PeerContext(ASender.Context);
- if LContext.MailBox.State = msReadOnly then begin
- SendErrorOpenedReadOnly(ASender);
- Exit;
- end;
- if Assigned(FOnCommandUNSUBSCRIBE) then begin
- OnCommandUNSUBSCRIBE(ASender.Context, LContext.IMAP4Tag, ASender.UnparsedParams);
- Exit;
- end;
- if not FUseDefaultMechanismsForUnassignedCommands then begin
- Exit;
- end;
- {Not clear exactly what this would do in this sample mechanism...}
- SendUnsupportedCommand(ASender);
- end;
- procedure TIdIMAP4Server.DoCommandLIST(ASender: TIdCommand);
- var
- LParams: TStringList;
- LMailBoxNames: TStringList;
- LMailBoxFlags: TStringList;
- LN: integer;
- LEntry: string;
- LContext: TIdIMAP4PeerContext;
- begin
- LContext := TIdIMAP4PeerContext(ASender.Context);
- if not (LContext.ConnectionState in [csAuthenticated, csSelected]) then begin
- SendWrongConnectionState(ASender);
- Exit;
- end;
- if Assigned(FOnCommandLIST) then begin
- OnCommandLIST(ASender.Context, LContext.IMAP4Tag, ASender.UnparsedParams);
- Exit;
- end;
- if not FUseDefaultMechanismsForUnassignedCommands then begin
- Exit;
- end;
- if not Assigned(OnDefMechListMailBox) then begin
- SendUnassignedDefaultMechanism(ASender);
- Exit;
- end;
- //The default mechanism only supports the following format:
- // LIST "" *
- LParams := TStringList.Create;
- try
- BreakApart(ASender.UnparsedParams, ' ', LParams); {Do not Localize}
- if LParams.Count < 2 then begin
- //Incorrect number of params...
- SendIncorrectNumberOfParameters(ASender);
- Exit;
- end;
- if LParams[1] <> '*' then begin {Do not Localize}
- SendBadReply(ASender, 'Parameter not supported, 2nd (last) parameter must be *'); {Do not Localize}
- Exit;
- end;
- LMailBoxNames := TStringList.Create;
- try
- LMailBoxFlags := TStringList.Create;
- try
- if OnDefMechListMailBox(LContext.LoginName, LParams[0], LMailBoxNames, LMailBoxFlags) then begin
- for LN := 0 to LMailBoxNames.Count-1 do begin
- //Replies are of the form:
- //* LIST (\HasNoChildren) "." "INBOX.CreatedFolder"
- LEntry := '* LIST ('; {Do not Localize}
- if LMailBoxFlags[LN] <> '' then begin
- LEntry := LEntry + LMailBoxFlags[LN];
- end;
- LEntry := LEntry + ') "' + MailBoxSeparator + '" "' + LMailBoxNames[LN] + '"'; {Do not Localize}
- DoSendReply(ASender.Context, LEntry); {Do not Localize}
- end;
- SendOkReply(ASender, 'Completed'); {Do not Localize}
- end else begin
- SendNoReply(ASender, 'List failed'); {Do not Localize}
- end;
- finally
- FreeAndNil(LMailBoxFlags);
- end;
- finally
- FreeAndNil(LMailBoxNames);
- end;
- finally
- FreeAndNil(LParams);
- end;
- end;
- procedure TIdIMAP4Server.DoCommandLSUB(ASender: TIdCommand);
- var
- LParams: TStringList;
- LMailBoxNames: TStringList;
- LMailBoxFlags: TStringList;
- LN: integer;
- LEntry: string;
- LContext: TIdIMAP4PeerContext;
- begin
- LContext := TIdIMAP4PeerContext(ASender.Context);
- if not (LContext.ConnectionState in [csAuthenticated, csSelected]) then begin
- SendWrongConnectionState(ASender);
- Exit;
- end;
- if Assigned(FOnCommandLSUB) then begin
- OnCommandLSUB(ASender.Context, LContext.IMAP4Tag, ASender.UnparsedParams);
- Exit;
- end;
- if not FUseDefaultMechanismsForUnassignedCommands then begin
- Exit;
- end;
- if not Assigned(OnDefMechListMailBox) then begin
- SendUnassignedDefaultMechanism(ASender);
- Exit;
- end;
- //Treat this the same as LIST...
- LParams := TStringList.Create;
- try
- BreakApart(ASender.UnparsedParams, ' ', LParams); {Do not Localize}
- if LParams.Count < 2 then begin
- //Incorrect number of params...
- SendIncorrectNumberOfParameters(ASender);
- Exit;
- end;
- if LParams[1] <> '*' then begin {Do not Localize}
- SendBadReply(ASender, 'Parameter not supported, 2nd (last) parameter must be *'); {Do not Localize}
- Exit;
- end;
- LMailBoxNames := TStringList.Create;
- try
- LMailBoxFlags := TStringList.Create;
- try
- if OnDefMechListMailBox(LContext.LoginName, LParams[0], LMailBoxNames, LMailBoxFlags) then begin
- for LN := 0 to LMailBoxNames.Count-1 do begin
- //Replies are of the form:
- //* LIST (\HasNoChildren) "." "INBOX.CreatedFolder"
- LEntry := '* LIST ('; {Do not Localize}
- if LMailBoxFlags[LN] <> '' then begin
- LEntry := LEntry + LMailBoxFlags[LN];
- end;
- LEntry := LEntry + ') "' + MailBoxSeparator + '" "' + LMailBoxNames[LN] + '"'; {Do not Localize}
- DoSendReply(ASender.Context, LEntry); {Do not Localize}
- end;
- SendOkReply(ASender, 'Completed'); {Do not Localize}
- end else begin
- SendNoReply(ASender, 'List failed'); {Do not Localize}
- end;
- finally
- FreeAndNil(LMailBoxFlags);
- end;
- finally
- FreeAndNil(LMailBoxNames);
- end;
- finally
- FreeAndNil(LParams);
- end;
- end;
- procedure TIdIMAP4Server.DoCommandSTATUS(ASender: TIdCommand);
- var
- LMailBox: TIdMailBox;
- LN: integer;
- LParams: TStringList;
- LTemp: string;
- LAnswer: string;
- LContext: TIdIMAP4PeerContext;
- begin
- LContext := TIdIMAP4PeerContext(ASender.Context);
- if not (LContext.ConnectionState in [csAuthenticated, csSelected]) then begin
- SendWrongConnectionState(ASender);
- Exit;
- end;
- if Assigned(FOnCommandSTATUS) then begin
- OnCommandSTATUS(ASender.Context, LContext.IMAP4Tag, ASender.UnparsedParams);
- Exit;
- end;
- if not FUseDefaultMechanismsForUnassignedCommands then begin
- Exit;
- end;
- if (not Assigned(OnDefMechDoesImapMailBoxExist))
- or (not Assigned(OnDefMechReinterpretParamAsMailBox))
- or (not Assigned(OnDefMechSetupMailbox)) then
- begin
- SendUnassignedDefaultMechanism(ASender);
- Exit;
- end;
- //This can be issued for ANY mailbox, not just the currently selected one.
- //The format is:
- //C5 STATUS "INBOX" (MESSAGES RECENT UIDNEXT UIDVALIDITY UNSEEN)
- //* STATUS INBOX (MESSAGES 490 RECENT 132 UIDNEXT 6546 UIDVALIDITY 1065090323 UNSEEN 167)
- //C5 OK Completed
- LParams := TStringList.Create;
- try
- BreakApart(ASender.UnparsedParams, ' ', LParams); {Do not Localize}
- if LParams.Count < 1 then begin
- //Incorrect number of params...
- SendIncorrectNumberOfParameters(ASender);
- Exit;
- end;
- if not OnDefMechReinterpretParamAsMailBox(LParams, 0) then begin
- SendBadReply(ASender, 'Mailbox parameter is invalid.'); {Do not Localize}
- Exit;
- end;
- if not OnDefMechDoesImapMailBoxExist(LContext.LoginName, LParams[0]) then begin
- SendNoReply(ASender, 'Mailbox does not exist.'); {Do not Localize}
- Exit;
- end;
- {Get everything you need for this mailbox...}
- LMailBox := TIdMailBox.Create;
- try
- OnDefMechSetupMailbox(LContext.LoginName, LParams[0], LMailBox);
- {Send the stats...}
- LAnswer := '* STATUS ' + LParams[0] + ' ('; {Do not Localize}
- for LN := 1 to LParams.Count-1 do begin
- LTemp := LParams[LN];
- if LTemp <> '' then begin
- //Strip brackets (will be on 1st & last param)
- if LTemp[1] = '(' then begin {Do not Localize}
- LTemp := Copy(LTemp, 2, MaxInt);
- end;
- if (LTemp <> '') and (LTemp[Length(LTemp)] = ')') then begin {Do not Localize}
- LTemp := Copy(LTemp, 1, Length(LTemp)-1);
- end;
- case PosInStrArray(LTemp, ['MESSAGES', 'RECENT', 'UIDNEXT', 'UIDVALIDITY', 'UNSEEN'], False) of
- 0: // MESSAGES {Do not Localize}
- begin
- LAnswer := LAnswer + LTemp + ' ' + IntToStr(LMailBox.TotalMsgs) + ' '; {Do not Localize}
- end;
- 1: // RECENT {Do not Localize}
- begin
- LAnswer := LAnswer + LTemp + ' ' + IntToStr(LMailBox.RecentMsgs) + ' '; {Do not Localize}
- end;
- 2: // UIDNEXT {Do not Localize}
- begin
- LAnswer := LAnswer + LTemp + ' ' + LMailBox.UIDNext + ' '; {Do not Localize}
- end;
- 3: // UIDVALIDITY {Do not Localize}
- begin
- LAnswer := LAnswer + LTemp + ' ' + LMailBox.UIDValidity + ' '; {Do not Localize}
- end;
- 4: // UNSEEN {Do not Localize}
- begin
- LAnswer := LAnswer + LTemp + ' ' + IntToStr(LMailBox.UnseenMsgs) + ' '; {Do not Localize}
- end;
- else
- begin
- SendBadReply(ASender, 'Parameter not supported: ' + LTemp); {Do not Localize}
- Exit;
- end;
- end;
- end;
- end;
- if LAnswer[Length(LAnswer)] = ' ' then begin {Do not Localize}
- LAnswer := Copy(LAnswer, 1, Length(LAnswer)-1);
- end;
- LAnswer := LAnswer + ')'; {Do not Localize}
- DoSendReply(ASender.Context, LAnswer);
- SendOkReply(ASender, 'Completed'); {Do not Localize}
- finally
- FreeAndNil(LMailBox);
- end;
- finally
- FreeAndNil(LParams);
- end;
- end;
- procedure TIdIMAP4Server.DoCommandAPPEND(ASender: TIdCommand);
- var
- LUID: string;
- LStream: TStream;
- LFile: string;
- LTemp: string;
- LParams: TStringList;
- LParams2: TStringList;
- LFlagsList: TStringList;
- LSize: Int64;
- LFlags, LInternalDateTime: string;
- LN: integer;
- LMessage: TIdMessage;
- LContext: TIdIMAP4PeerContext;
- begin
- LContext := TIdIMAP4PeerContext(ASender.Context);
- //You do NOT need to be in selected state for this.
- if LContext.ConnectionState <> csAuthenticated then begin
- SendWrongConnectionState(ASender);
- Exit;
- end;
- if LContext.MailBox.State = msReadOnly then begin
- SendErrorOpenedReadOnly(ASender);
- Exit;
- end;
- if Assigned(FOnCommandAPPEND) then begin
- OnCommandAPPEND(ASender.Context, LContext.IMAP4Tag, ASender.UnparsedParams);
- Exit;
- end;
- if not FUseDefaultMechanismsForUnassignedCommands then begin
- Exit;
- end;
- if (not Assigned(OnDefMechGetNextFreeUID))
- or (not Assigned(OnDefMechReinterpretParamAsMailBox))
- or (not Assigned(OnDefMechUpdateNextFreeUID))
- or (not Assigned(OnDefMechDeleteMessage)) //Needed to reverse out a save if setting flags fail
- or (not Assigned(OnDefMechGetFileNameToWriteAppendMessage)) then
- begin
- SendUnassignedDefaultMechanism(ASender);
- Exit;
- end;
- //Format (the flags and date/time are optional):
- //C323 APPEND "INBOX.Sent" (\Seen) "internal date/time" {1876}
- //+ go ahead
- //...
- //C323 OK [APPENDUID 1065095982 105] Completed
- LParams := TStringList.Create;
- try
- BreakApart(ASender.UnparsedParams, ' ', LParams); {Do not Localize}
- if LParams.Count < 2 then begin
- //Incorrect number of params...
- SendIncorrectNumberOfParameters(ASender);
- Exit;
- end;
- if not OnDefMechReinterpretParamAsMailBox(LParams, 0) then begin
- SendBadReply(ASender, 'Mailbox parameter is invalid.'); {Do not Localize}
- Exit;
- end;
- LFlags := '';
- LInternalDateTime := '';
- LN := 1;
- LTemp := LParams[Ln];
- if TextStartsWith(LTemp, '(') then begin {Do not Localize}
- if not ReinterpretParamAsFlags(LParams, Ln) then begin
- SendBadReply(ASender, 'Flags parameter is invalid.'); {Do not Localize}
- Exit;
- end;
- LFlags := LParams[Ln];
- Inc(Ln);
- end
- else if TextIsSame(LTemp, 'NIL') then begin {Do not Localize}
- Inc(Ln);
- end;
- LTemp := LParams[Ln];
- if TextStartsWith(LTemp, '"') then begin {Do not Localize}
- if not ReinterpretParamAsQuotedStr(LParams, Ln) then begin
- SendBadReply(ASender, 'InternalDateTime parameter is invalid.'); {Do not Localize}
- Exit;
- end;
- LInternalDateTime := LParams[Ln];
- end;
- LTemp := LParams[LParams.Count-1];
- if not TextStartsWith(LTemp, '{') then begin {Do not Localize}
- SendBadReply(ASender, 'Size parameter is invalid.'); {Do not Localize}
- Exit;
- end;
- LSize := IndyStrToInt64(Copy(LTemp, 2, Length(LTemp)-2));
- //Grab the next UID...
- LUID := OnDefMechGetNextFreeUID(LContext.LoginName, LParams[0]);
- //Get the message...
- LFile := OnDefMechGetFileNameToWriteAppendMessage(LContext.LoginName, LContext.MailBox.Name, LUID);
- LStream := TIdFileCreateStream.Create(LFile);
- try
- ASender.Context.Connection.IOHandler.ReadStream(LStream, LSize);
- if LFlags = '' then begin
- SendOkReply(ASender, 'Completed'); {Do not Localize}
- end else begin
- //Update the (optional) flags...
- LParams2 := TStringList.Create;
- try
- LParams2.Add(LUID);
- LParams2.Add('FLAGS.SILENT'); {Do not Localize}
- {
- for LN := 1 to LParams.Count-2 do begin
- LParams2.Add(LParams[LN]);
- end;
- }
- //The flags are in a string, need to reassemble...
- LFlagsList := TStringList.Create;
- try
- BreakApart(LFlags, ' ', LFlagsList); {Do not Localize}
- for LN := 0 to LFlagsList.Count-1 do begin
- LTemp := LFlagsList[LN];
- if LN = 0 then begin
- LTemp := '(' + LTemp; {Do not Localize}
- end;
- if LN = LFlagsList.Count-1 then begin
- LTemp := LTemp + ')'; {Do not Localize}
- end;
- LParams2.Add(LTemp);
- end;
- if not ProcessStore(True, ASender, LParams2) then begin
- //Have to reverse out our changes if ANYTHING fails..
- LMessage := TIdMessage.Create(Self);
- try
- LMessage.UID := LUID; //This is all we need for deletion
- OnDefMechDeleteMessage(LContext.LoginName, LContext.MailBox.Name, LMessage);
- finally
- FreeAndNil(LMessage);
- end;
- Exit;
- end;
- finally
- FreeAndNil(LFlagsList);
- end;
- finally
- FreeAndNil(LParams2);
- end;
- end;
- //Update the next free UID in the .uid file...
- OnDefMechUpdateNextFreeUID(LContext.LoginName, LContext.MailBox.Name, IntToStr(IndyStrToInt64(LUID)+1));
- // TODO: implement this
- {
- if LInternalDateTime <> '' then
- begin
- // what to do here?
- end;
- }
- finally
- FreeAndNil(LStream);
- end;
- finally
- FreeAndNil(LParams);
- end;
- end;
- procedure TIdIMAP4Server.DoCommandCHECK(ASender: TIdCommand);
- var
- LContext: TIdIMAP4PeerContext;
- begin
- LContext := TIdIMAP4PeerContext(ASender.Context);
- if LContext.ConnectionState <> csSelected then begin
- SendWrongConnectionState(ASender);
- Exit;
- end;
- if Assigned(fOnCommandCHECK) then begin
- OnCommandCHECK(ASender.Context, LContext.IMAP4Tag, ASender.UnparsedParams);
- Exit;
- end;
- if not FUseDefaultMechanismsForUnassignedCommands then begin
- Exit;
- end;
- {On most servers, this does nothing, they always return OK...}
- SendOkReply(ASender, 'Completed'); {Do not Localize}
- end;
- procedure TIdIMAP4Server.DoCommandCLOSE(ASender: TIdCommand);
- var
- LResult: Boolean;
- LContext: TIdIMAP4PeerContext;
- begin
- LContext := TIdIMAP4PeerContext(ASender.Context);
- if LContext.ConnectionState <> csSelected then begin
- SendWrongConnectionState(ASender);
- Exit;
- end;
- if Assigned(fOnCommandCLOSE) then begin
- OnCommandCLOSE(ASender.Context, LContext.IMAP4Tag, ASender.UnparsedParams);
- Exit;
- end;
- if not FUseDefaultMechanismsForUnassignedCommands then begin
- Exit;
- end;
- if not Assigned(OnDefMechDeleteMessage) then begin //Used by ExpungeRecords
- SendUnassignedDefaultMechanism(ASender);
- Exit;
- end;
- {This is an implicit expunge...}
- LResult := ExpungeRecords(ASender);
- {Now close it...}
- LContext.MailBox.Clear;
- LContext.FConnectionState := csAuthenticated;
- if LResult then begin
- SendOkReply(ASender, 'Completed'); {Do not Localize}
- end else begin
- SendNoReply(ASender, 'Implicit expunge failed for one or more messages'); {Do not Localize}
- end;
- end;
- procedure TIdIMAP4Server.DoCommandEXPUNGE(ASender: TIdCommand);
- var
- LContext: TIdIMAP4PeerContext;
- begin
- LContext := TIdIMAP4PeerContext(ASender.Context);
- if LContext.ConnectionState <> csSelected then begin
- SendWrongConnectionState(ASender);
- Exit;
- end;
- if LContext.MailBox.State = msReadOnly then begin
- SendErrorOpenedReadOnly(ASender);
- Exit;
- end;
- if Assigned(FOnCommandEXPUNGE) then begin
- OnCommandEXPUNGE(ASender.Context, LContext.IMAP4Tag, ASender.UnparsedParams);
- Exit;
- end;
- if not FUseDefaultMechanismsForUnassignedCommands then begin
- Exit;
- end;
- if not Assigned(OnDefMechDeleteMessage) then begin //Used by ExpungeRecords
- SendUnassignedDefaultMechanism(ASender);
- Exit;
- end;
- if ExpungeRecords(ASender) then begin
- SendOkReply(ASender, 'Completed'); {Do not Localize}
- end else begin
- SendNoReply(ASender, 'Expunge failed for one or more messages'); {Do not Localize}
- end;
- end;
- procedure TIdIMAP4Server.DoCommandSEARCH(ASender: TIdCommand);
- var
- LParams: TStringList;
- LContext: TIdIMAP4PeerContext;
- begin
- LContext := TIdIMAP4PeerContext(ASender.Context);
- if LContext.ConnectionState <> csSelected then begin
- SendWrongConnectionState(ASender);
- Exit;
- end;
- if Assigned(fOnCommandSEARCH) then begin
- OnCommandSEARCH(ASender.Context, LContext.IMAP4Tag, ASender.UnparsedParams);
- Exit;
- end;
- if not FUseDefaultMechanismsForUnassignedCommands then begin
- Exit;
- end;
- if not Assigned(OnDefMechGetMessageHeader) then begin //Used by ProcessSearch
- SendUnassignedDefaultMechanism(ASender);
- Exit;
- end;
- LParams := TStringList.Create;
- try
- BreakApart(ASender.UnparsedParams, ' ', LParams); {Do not Localize}
- ProcessSearch(False, ASender, LParams);
- finally
- FreeAndNil(LParams);
- end;
- end;
- procedure TIdIMAP4Server.DoCommandFETCH(ASender: TIdCommand);
- var
- LParams: TStringList;
- LContext: TIdIMAP4PeerContext;
- begin
- LContext := TIdIMAP4PeerContext(ASender.Context);
- if LContext.ConnectionState <> csSelected then begin
- SendWrongConnectionState(ASender);
- Exit;
- end;
- if Assigned(FOnCommandFETCH) then begin
- OnCommandFETCH(ASender.Context, LContext.IMAP4Tag, ASender.UnparsedParams);
- Exit;
- end;
- if not FUseDefaultMechanismsForUnassignedCommands then begin
- Exit;
- end;
- if (not Assigned(OnDefMechGetMessageHeader)) //Used by ProcessFetch
- or (not Assigned(OnDefMechGetMessageSize))
- or (not Assigned(OnDefMechGetMessageRaw)) then
- begin
- SendUnassignedDefaultMechanism(ASender);
- Exit;
- end;
- LParams := TStringList.Create;
- try
- BreakApart(ASender.UnparsedParams, ' ', LParams); {Do not Localize}
- ProcessFetch(False, ASender, LParams);
- finally
- FreeAndNil(LParams);
- end;
- end;
- procedure TIdIMAP4Server.DoCommandSTORE(ASender: TIdCommand);
- var
- LParams: TStringList;
- LContext: TIdIMAP4PeerContext;
- begin
- LContext := TIdIMAP4PeerContext(ASender.Context);
- if LContext.ConnectionState <> csSelected then begin
- SendWrongConnectionState(ASender);
- Exit;
- end;
- if LContext.MailBox.State = msReadOnly then begin
- SendErrorOpenedReadOnly(ASender);
- Exit;
- end;
- if Assigned(fOnCommandSTORE) then begin
- OnCommandSTORE(ASender.Context, LContext.IMAP4Tag, ASender.UnparsedParams);
- Exit;
- end;
- if not FUseDefaultMechanismsForUnassignedCommands then begin
- Exit;
- end;
- LParams := TStringList.Create;
- try
- BreakApart(ASender.UnparsedParams, ' ', LParams); {Do not Localize}
- ProcessStore(False, ASender, LParams);
- finally
- FreeAndNil(LParams);
- end;
- end;
- function TIdIMAP4Server.MessageFlagSetToStr(const AFlags: TIdMessageFlagsSet): String;
- begin
- Result := '';
- if mfAnswered in AFlags then begin
- Result := Result + MessageFlags[mfAnswered] + ' '; {Do not Localize}
- end;
- if mfFlagged in AFlags then begin
- Result := Result + MessageFlags[mfFlagged] + ' '; {Do not Localize}
- end;
- if mfDeleted in AFlags then begin
- Result := Result + MessageFlags[mfDeleted] + ' '; {Do not Localize}
- end;
- if mfDraft in AFlags then begin
- Result := Result + MessageFlags[mfDraft] + ' '; {Do not Localize}
- end;
- if mfSeen in AFlags then begin
- Result := Result + MessageFlags[mfSeen] + ' '; {Do not Localize}
- end;
- if Result <> '' then begin
- Result := TrimRight(Result);
- end;
- end;
- procedure TIdIMAP4Server.DoCommandCOPY(ASender: TIdCommand);
- var
- LParams: TStringList;
- LContext: TIdIMAP4PeerContext;
- begin
- LContext := TIdIMAP4PeerContext(ASender.Context);
- if LContext.ConnectionState <> csSelected then begin
- SendWrongConnectionState(ASender);
- Exit;
- end;
- if LContext.MailBox.State = msReadOnly then begin
- SendErrorOpenedReadOnly(ASender);
- Exit;
- end;
- if Assigned(FOnCommandCOPY) then begin
- OnCommandCOPY(ASender.Context, LContext.IMAP4Tag, ASender.UnparsedParams);
- Exit;
- end;
- if not FUseDefaultMechanismsForUnassignedCommands then begin
- Exit;
- end;
- //Format is COPY 2:4 DestinationMailBoxName
- if (not Assigned(OnDefMechReinterpretParamAsMailBox))
- or (not Assigned(OnDefMechCopyMessage)) then //Needed for ProcessCopy
- begin
- SendUnassignedDefaultMechanism(ASender);
- Exit;
- end;
- LParams := TStringList.Create;
- try
- BreakApart(ASender.UnparsedParams, ' ', LParams); {Do not Localize}
- ProcessCopy(False, ASender, LParams);
- finally
- FreeAndNil(LParams);
- end;
- end;
- {UID before COPY, FETCH or STORE means the record numbers are UIDs.
- UID before SEARCH means SEARCH is to _return_ UIDs rather than relative numbers.}
- procedure TIdIMAP4Server.DoCommandUID(ASender: TIdCommand);
- var
- LParams: TStringList;
- LContext: TIdIMAP4PeerContext;
- begin
- LContext := TIdIMAP4PeerContext(ASender.Context);
- if LContext.ConnectionState <> csSelected then begin
- SendWrongConnectionState(ASender);
- Exit;
- end;
- if Assigned(fOnCommandUID) then begin
- OnCommandUID(ASender.Context, LContext.IMAP4Tag, ASender.UnparsedParams);
- Exit;
- end;
- if not FUseDefaultMechanismsForUnassignedCommands then begin
- Exit;
- end;
- LParams := TStringList.Create;
- try
- BreakApart(ASender.UnparsedParams, ' ', LParams); {Do not Localize}
- if LParams.Count < 1 then begin
- //Incorrect number of params...
- SendIncorrectNumberOfParameters(ASender);
- Exit;
- end;
- //Map the commands to the general handler but remove the FETCH or whatever...
- case PosInStrArray(LParams[0], ['FETCH', 'COPY', 'STORE', 'SEARCH'], False) of
- 0: // FETCH {Do not Localize}
- begin
- if (not Assigned(OnDefMechGetMessageHeader)) //Used by ProcessFetch
- or (not Assigned(OnDefMechGetMessageSize))
- or (not Assigned(OnDefMechGetMessageRaw)) then
- begin
- SendUnassignedDefaultMechanism(ASender);
- Exit;
- end;
- LParams.Delete(0);
- ProcessFetch(True, ASender, LParams);
- end;
- 1: // COPY {Do not Localize}
- begin
- if (not Assigned(OnDefMechReinterpretParamAsMailBox))
- or (not Assigned(OnDefMechCopyMessage)) then //Needed for ProcessCopy
- begin
- SendUnassignedDefaultMechanism(ASender);
- Exit;
- end;
- LParams.Delete(0);
- ProcessCopy(True, ASender, LParams);
- end;
- 2: // STORE {Do not Localize}
- begin
- LParams.Delete(0);
- ProcessStore(True, ASender, LParams);
- end;
- 3: // SEARCH {Do not Localize}
- begin
- if not Assigned(OnDefMechGetMessageHeader) then begin //Used by ProcessSearch
- SendUnassignedDefaultMechanism(ASender);
- Exit;
- end;
- LParams.Delete(0);
- ProcessSearch(True, ASender, LParams);
- end;
- else
- begin
- SendUnsupportedCommand(ASender);
- end;
- end;
- finally
- FreeAndNil(LParams);
- end;
- end;
- procedure TIdIMAP4Server.DoCommandX(ASender: TIdCommand);
- begin
- if not Assigned(fOnCommandX) then begin
- OnCommandX(ASender.Context, TIdIMAP4PeerContext(ASender.Context).IMAP4Tag, ASender.UnparsedParams);
- end else if FUseDefaultMechanismsForUnassignedCommands then begin
- SendUnsupportedCommand(ASender);
- end;
- end;
- procedure TIdIMAP4Server.DoCommandSTARTTLS(ASender: TIdCommand);
- var
- LContext: TIdIMAP4PeerContext;
- begin
- LContext := TIdIMAP4PeerContext(ASender.Context);
- if (not (IOHandler is TIdServerIOHandlerSSLBase)) or (not (FUseTLS in ExplicitTLSVals)) then begin
- OnCommandError(ASender.Context, LContext.IMAP4Tag, ASender.UnparsedParams);
- Exit;
- end;
- if LContext.UsingTLS then begin // we are already using TLS
- DoSendReply(ASender.Context, 'BAD %s', [RSIMAP4SvrNotPermittedWithTLS]); {do not localize}
- Exit;
- end;
- // TODO: STARTTLS may only be issued in auth-state
- DoSendReply(ASender.Context, 'OK %s', [RSIMAP4SvrBeginTLSNegotiation]); {do not localize}
- (ASender.Context.Connection.IOHandler as TIdSSLIOHandlerSocketBase).PassThrough := False;
- end;
- end.
|