| 1234567891011121314151617181920212223242526272829303132333435363738394041424344454647484950515253545556575859606162636465666768697071727374757677787980818283848586878889909192939495969798991001011021031041051061071081091101111121131141151161171181191201211221231241251261271281291301311321331341351361371381391401411421431441451461471481491501511521531541551561571581591601611621631641651661671681691701711721731741751761771781791801811821831841851861871881891901911921931941951961971981992002012022032042052062072082092102112122132142152162172182192202212222232242252262272282292302312322332342352362372382392402412422432442452462472482492502512522532542552562572582592602612622632642652662672682692702712722732742752762772782792802812822832842852862872882892902912922932942952962972982993003013023033043053063073083093103113123133143153163173183193203213223233243253263273283293303313323333343353363373383393403413423433443453463473483493503513523533543553563573583593603613623633643653663673683693703713723733743753763773783793803813823833843853863873883893903913923933943953963973983994004014024034044054064074084094104114124134144154164174184194204214224234244254264274284294304314324334344354364374384394404414424434444454464474484494504514524534544554564574584594604614624634644654664674684694704714724734744754764774784794804814824834844854864874884894904914924934944954964974984995005015025035045055065075085095105115125135145155165175185195205215225235245255265275285295305315325335345355365375385395405415425435445455465475485495505515525535545555565575585595605615625635645655665675685695705715725735745755765775785795805815825835845855865875885895905915925935945955965975985996006016026036046056066076086096106116126136146156166176186196206216226236246256266276286296306316326336346356366376386396406416426436446456466476486496506516526536546556566576586596606616626636646656666676686696706716726736746756766776786796806816826836846856866876886896906916926936946956966976986997007017027037047057067077087097107117127137147157167177187197207217227237247257267277287297307317327337347357367377387397407417427437447457467477487497507517527537547557567577587597607617627637647657667677687697707717727737747757767777787797807817827837847857867877887897907917927937947957967977987998008018028038048058068078088098108118128138148158168178188198208218228238248258268278288298308318328338348358368378388398408418428438448458468478488498508518528538548558568578588598608618628638648658668678688698708718728738748758768778788798808818828838848858868878888898908918928938948958968978988999009019029039049059069079089099109119129139149159169179189199209219229239249259269279289299309319329339349359369379389399409419429439449459469479489499509519529539549559569579589599609619629639649659669679689699709719729739749759769779789799809819829839849859869879889899909919929939949959969979989991000100110021003100410051006100710081009101010111012101310141015101610171018101910201021102210231024102510261027102810291030103110321033103410351036103710381039104010411042104310441045104610471048104910501051105210531054105510561057105810591060106110621063106410651066106710681069107010711072107310741075107610771078107910801081108210831084108510861087108810891090109110921093109410951096109710981099110011011102110311041105110611071108110911101111111211131114111511161117111811191120112111221123112411251126112711281129113011311132113311341135113611371138113911401141114211431144114511461147114811491150115111521153115411551156115711581159116011611162116311641165116611671168116911701171117211731174117511761177117811791180118111821183118411851186118711881189119011911192119311941195119611971198119912001201120212031204120512061207120812091210121112121213121412151216121712181219122012211222122312241225122612271228122912301231123212331234123512361237123812391240124112421243124412451246124712481249125012511252125312541255125612571258125912601261126212631264126512661267126812691270127112721273127412751276127712781279128012811282128312841285128612871288128912901291129212931294129512961297129812991300130113021303130413051306130713081309131013111312131313141315131613171318131913201321132213231324132513261327132813291330133113321333133413351336133713381339134013411342134313441345134613471348134913501351135213531354135513561357135813591360136113621363136413651366136713681369137013711372137313741375137613771378137913801381138213831384138513861387138813891390139113921393139413951396139713981399140014011402140314041405140614071408140914101411141214131414141514161417141814191420142114221423142414251426142714281429143014311432143314341435143614371438143914401441144214431444144514461447144814491450145114521453145414551456145714581459146014611462146314641465146614671468146914701471147214731474147514761477147814791480148114821483148414851486148714881489149014911492149314941495149614971498149915001501150215031504150515061507150815091510151115121513151415151516151715181519152015211522152315241525152615271528152915301531153215331534153515361537153815391540154115421543154415451546154715481549155015511552155315541555155615571558155915601561156215631564156515661567156815691570157115721573157415751576157715781579158015811582158315841585158615871588158915901591159215931594159515961597159815991600160116021603160416051606160716081609161016111612161316141615161616171618161916201621162216231624162516261627162816291630163116321633163416351636163716381639164016411642164316441645164616471648164916501651165216531654165516561657165816591660166116621663166416651666166716681669167016711672167316741675167616771678167916801681168216831684168516861687168816891690169116921693169416951696169716981699170017011702170317041705170617071708170917101711171217131714171517161717171817191720172117221723172417251726172717281729173017311732173317341735173617371738173917401741174217431744174517461747174817491750175117521753175417551756175717581759176017611762176317641765176617671768176917701771177217731774177517761777177817791780178117821783178417851786178717881789179017911792179317941795179617971798179918001801180218031804180518061807180818091810181118121813181418151816181718181819182018211822182318241825182618271828182918301831183218331834183518361837183818391840184118421843184418451846184718481849185018511852185318541855185618571858185918601861186218631864186518661867186818691870187118721873187418751876187718781879188018811882188318841885188618871888188918901891189218931894189518961897189818991900190119021903190419051906190719081909191019111912191319141915191619171918191919201921192219231924192519261927192819291930193119321933193419351936193719381939194019411942194319441945194619471948194919501951195219531954195519561957195819591960196119621963196419651966196719681969197019711972197319741975197619771978197919801981198219831984198519861987198819891990199119921993199419951996199719981999200020012002200320042005200620072008200920102011201220132014201520162017201820192020202120222023202420252026202720282029203020312032203320342035203620372038203920402041204220432044204520462047204820492050205120522053205420552056205720582059206020612062206320642065206620672068206920702071207220732074207520762077207820792080208120822083208420852086208720882089209020912092209320942095209620972098209921002101210221032104210521062107210821092110211121122113211421152116211721182119212021212122212321242125212621272128212921302131213221332134213521362137213821392140214121422143214421452146214721482149215021512152215321542155215621572158215921602161216221632164216521662167216821692170217121722173217421752176217721782179218021812182218321842185218621872188218921902191219221932194219521962197219821992200220122022203220422052206220722082209221022112212221322142215221622172218221922202221222222232224222522262227222822292230223122322233223422352236223722382239224022412242224322442245224622472248224922502251225222532254225522562257225822592260226122622263226422652266226722682269227022712272227322742275227622772278227922802281228222832284228522862287228822892290229122922293229422952296229722982299230023012302230323042305230623072308230923102311231223132314231523162317231823192320232123222323232423252326232723282329233023312332233323342335233623372338233923402341234223432344234523462347234823492350235123522353235423552356235723582359236023612362236323642365236623672368236923702371237223732374237523762377237823792380238123822383238423852386238723882389239023912392239323942395239623972398239924002401240224032404240524062407240824092410241124122413241424152416241724182419242024212422242324242425242624272428242924302431243224332434243524362437243824392440244124422443244424452446244724482449245024512452245324542455245624572458245924602461246224632464246524662467246824692470247124722473247424752476247724782479248024812482248324842485248624872488248924902491249224932494249524962497249824992500250125022503250425052506250725082509251025112512251325142515251625172518251925202521252225232524252525262527252825292530253125322533253425352536253725382539254025412542254325442545254625472548254925502551255225532554255525562557255825592560256125622563256425652566256725682569257025712572257325742575257625772578257925802581258225832584258525862587258825892590259125922593259425952596259725982599260026012602260326042605260626072608260926102611261226132614261526162617261826192620262126222623262426252626262726282629263026312632263326342635263626372638263926402641264226432644264526462647264826492650265126522653265426552656265726582659266026612662266326642665266626672668266926702671267226732674267526762677267826792680268126822683268426852686268726882689269026912692269326942695269626972698 |
- {
- $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}
- 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);
- public
- constructor Create(AOwner: TComponent); override;
- 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;
- constructor TIdIMAP4Server.Create(AOwner: TComponent);
- begin
- inherited Create(AOwner);
- //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.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;
- 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
- FLastCommand.Free;
- FMailBox.Free;
- 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 AName <> '' 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
- LMessageTemp.Free;
- 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
- LMessageRaw.Free;
- 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
- LDataItems.Free;
- end;
- finally
- LMessageNumbers.Free;
- 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
- LMessageTemp.Free;
- 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
- LMessageNumbers.Free;
- 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
- LFlagList.Free;
- end;
- finally
- LMessageNumbers.Free;
- 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
- LParams.Free;
- 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
- LParams.Free;
- 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
- LParams.Free;
- 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
- LParams.Free;
- 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
- LMailBoxFlags.Free;
- end;
- finally
- LMailBoxNames.Free;
- end;
- finally
- LParams.Free;
- 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
- LMailBoxFlags.Free;
- end;
- finally
- LMailBoxNames.Free;
- end;
- finally
- LParams.Free;
- 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}
- SetLength(LTemp, 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}
- SetLength(LAnswer, Length(LAnswer)-1);
- end;
- LAnswer := LAnswer + ')'; {Do not Localize}
- DoSendReply(ASender.Context, LAnswer);
- SendOkReply(ASender, 'Completed'); {Do not Localize}
- finally
- LMailBox.Free;
- end;
- finally
- LParams.Free;
- 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;
- try
- LMessage.UID := LUID; //This is all we need for deletion
- OnDefMechDeleteMessage(LContext.LoginName, LContext.MailBox.Name, LMessage);
- finally
- LMessage.Free;
- end;
- Exit;
- end;
- finally
- LFlagsList.Free;
- end;
- finally
- LParams2.Free;
- 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
- LStream.Free;
- end;
- finally
- LParams.Free;
- 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
- LParams.Free;
- 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
- LParams.Free;
- 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
- LParams.Free;
- 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
- LParams.Free;
- 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
- LParams.Free;
- 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.
|