414 lines
12 KiB
Plaintext
414 lines
12 KiB
Plaintext
|
{
|
||
|
$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$
|
||
|
}
|
||
|
{
|
||
|
Rev 1.21 10/26/2004 10:39:54 PM JPMugaas
|
||
|
Updated refs.
|
||
|
|
||
|
Rev 1.20 5/17/04 9:50:52 AM RLebeau
|
||
|
Changed TIdRepliesPOP3 constructor to use 'reintroduce' instead
|
||
|
|
||
|
Rev 1.19 5/16/04 5:26:58 PM RLebeau
|
||
|
Added TIdRepliesPOP3 class
|
||
|
|
||
|
Rev 1.18 2004.04.15 12:49:46 PM czhower
|
||
|
Fixed bug in TIdReplyPOP3.IsEndMarker
|
||
|
|
||
|
Rev 1.17 2004.02.03 5:45:44 PM czhower
|
||
|
Name changes
|
||
|
|
||
|
Rev 1.16 2004.01.29 12:07:52 AM czhower
|
||
|
.Net constructor problem fix.
|
||
|
|
||
|
Rev 1.15 2004.01.22 5:52:54 PM czhower
|
||
|
Visibilty fix + TextIsSame
|
||
|
|
||
|
Rev 1.14 1/3/2004 8:05:50 PM JPMugaas
|
||
|
Bug fix: Sometimes, replies will appear twice due to the way functionality
|
||
|
was enherited.
|
||
|
|
||
|
Rev 1.13 22/12/2003 00:45:58 CCostelloe
|
||
|
.NET fixes
|
||
|
|
||
|
Rev 1.12 2003.10.18 9:42:12 PM czhower
|
||
|
Boatload of bug fixes to command handlers.
|
||
|
|
||
|
Rev 1.11 2003.09.20 10:38:40 AM czhower
|
||
|
Bug fix to allow clearing code field (Return to default value)
|
||
|
|
||
|
Rev 1.10 6/8/2003 03:26:00 AM JPMugaas
|
||
|
AssignTo added for object assignment.
|
||
|
|
||
|
Rev 1.9 6/8/2003 02:59:24 AM JPMugaas
|
||
|
RFC 2449 and RFC 3206 support.
|
||
|
|
||
|
Rev 1.8 6/5/2003 04:54:22 AM JPMugaas
|
||
|
Reworkings and minor changes for new Reply exception framework.
|
||
|
|
||
|
Rev 1.7 6/4/2003 04:06:52 PM JPMugaas
|
||
|
Started preliminary worki on RFC 3206 and RFC 2449.
|
||
|
|
||
|
Removed an old GetInternetResponse override that is no longer needed and
|
||
|
causes its own problems.
|
||
|
|
||
|
Now uses string reply codes using Kudzu's new overloaded methods so mapping
|
||
|
to integers is no longer needed. The integers used in mapping have been
|
||
|
removed.
|
||
|
|
||
|
Rev 1.6 5/30/2003 9:06:44 PM BGooijen
|
||
|
uses CheckIfCodeIsValid now
|
||
|
|
||
|
Rev 1.5 5/26/2003 04:28:28 PM JPMugaas
|
||
|
Removed GenerateReply and ParseResponse calls because those functions are
|
||
|
being removed.
|
||
|
|
||
|
Rev 1.4 2003.05.26 10:51:42 PM czhower
|
||
|
Removed RFC / non POP3 parsing
|
||
|
|
||
|
Rev 1.3 5/26/2003 12:22:06 PM JPMugaas
|
||
|
|
||
|
Rev 1.2 5/25/2003 02:40:56 AM JPMugaas
|
||
|
|
||
|
Rev 1.1 5/20/2003 10:58:28 AM JPMugaas
|
||
|
SetReplyExceptionCode now validated by TIdReplyPOP3. This way, it can only
|
||
|
accept our integer codes for +OK, -ERR, and +.
|
||
|
|
||
|
Rev 1.0 5/19/2003 04:28:10 PM JPMugaas
|
||
|
TIdReply decendant for POP3.
|
||
|
}
|
||
|
|
||
|
unit IdReplyPOP3;
|
||
|
|
||
|
interface
|
||
|
{$i IdCompilerDefines.inc}
|
||
|
|
||
|
uses
|
||
|
Classes,
|
||
|
IdException,
|
||
|
IdReply;
|
||
|
|
||
|
const
|
||
|
{do not change these strings unless you know what you are doing}
|
||
|
ST_OK = '+OK'; {Do not translate}
|
||
|
ST_ERR = '-ERR'; {Do not translate}
|
||
|
ST_SASLCONTINUE = '+'; {Do not translate}
|
||
|
|
||
|
//note that for extended codes, we do not put the ] ending as
|
||
|
//error code may be hierarchical in the future with a / separating levels
|
||
|
// RFC 2449
|
||
|
ST_ERR_IN_USE = 'IN-USE'; {Do not translate} //already in use by another program
|
||
|
ST_ERR_LOGIN_DELAY = 'LOGIN-DELAY'; {Do not translate} //login delay time
|
||
|
// RFC 3206
|
||
|
ST_ERR_SYS_TEMP = 'SYS/PERM'; {Do not translate} //system failure - permenent
|
||
|
ST_ERR_SYS_PERM = 'SYS/TEMP'; {Do not translate} //system failure - temporary
|
||
|
ST_ERR_AUTH = 'AUTH'; {Do not translate} //authentication credential problem
|
||
|
|
||
|
const
|
||
|
VALID_ENH_CODES : array[0..4] of string = (
|
||
|
ST_ERR_IN_USE,
|
||
|
ST_ERR_LOGIN_DELAY,
|
||
|
ST_ERR_SYS_TEMP,
|
||
|
ST_ERR_SYS_PERM,
|
||
|
ST_ERR_AUTH
|
||
|
);
|
||
|
|
||
|
type
|
||
|
TIdReplyPOP3 = class(TIdReply)
|
||
|
protected
|
||
|
FEnhancedCode : String;
|
||
|
//
|
||
|
class function FindCodeTextDelim(const AText : String) : Integer;
|
||
|
class function IsValidEnhancedCode(const AText : String; const AStrict : Boolean = False) : Boolean;
|
||
|
class function ExtractTextPosArray(const AStr : String):Integer;
|
||
|
function GetFormattedReply: TStrings; override;
|
||
|
procedure SetFormattedReply(const AValue: TStrings); override;
|
||
|
function CheckIfCodeIsValid(const ACode: string): Boolean; override;
|
||
|
procedure SetEnhancedCode(const AValue : String);
|
||
|
public
|
||
|
constructor CreateWithReplyTexts(
|
||
|
ACollection: TCollection = nil;
|
||
|
AReplyTexts: TIdReplies = nil
|
||
|
); override;
|
||
|
procedure Assign(ASource: TPersistent); override;
|
||
|
procedure RaiseReplyError; override;
|
||
|
class function IsEndMarker(const ALine: string): Boolean; override;
|
||
|
published
|
||
|
property EnhancedCode : String read FEnhancedCode write SetEnhancedCode;
|
||
|
end;
|
||
|
|
||
|
TIdRepliesPOP3 = class(TIdReplies)
|
||
|
public
|
||
|
constructor Create(AOwner: TPersistent); reintroduce;
|
||
|
end;
|
||
|
|
||
|
//This error is for POP3 Protocol reply exceptions
|
||
|
// SendCmd / GetResponse
|
||
|
EIdReplyPOP3Error = class(EIdReplyError)
|
||
|
protected
|
||
|
FErrorCode : String;
|
||
|
FEnhancedCode : String;
|
||
|
public
|
||
|
constructor CreateError(const AErrorCode: String;
|
||
|
const AReplyMessage: string; const AEnhancedCode : String = ''); reintroduce; virtual;
|
||
|
property ErrorCode : String read FErrorCode;
|
||
|
property EnhancedCode : String read FEnhancedCode;
|
||
|
end;
|
||
|
|
||
|
const
|
||
|
VALID_POP3_STR : Array [0..2] of String = (
|
||
|
ST_OK,
|
||
|
ST_ERR,
|
||
|
ST_SASLCONTINUE);
|
||
|
|
||
|
type
|
||
|
EIdPOP3ReplyException = class(EIdException);
|
||
|
EIdPOP3ReplyInvalidEnhancedCode = class(EIdPOP3ReplyException);
|
||
|
|
||
|
implementation
|
||
|
|
||
|
uses
|
||
|
IdGlobal,
|
||
|
IdGlobalProtocols,
|
||
|
IdResourceStringsProtocols,
|
||
|
SysUtils;
|
||
|
|
||
|
{ TIdReplyPOP3 }
|
||
|
|
||
|
procedure TIdReplyPOP3.Assign(ASource: TPersistent);
|
||
|
var
|
||
|
LR: TIdReplyPOP3;
|
||
|
begin
|
||
|
if ASource is TIdReplyPOP3 then begin
|
||
|
LR := TIdReplyPOP3(ASource);
|
||
|
//set code first as it possibly clears the reply
|
||
|
Code := LR.Code;
|
||
|
FEnhancedCode := LR.EnhancedCode;
|
||
|
FText.Assign(LR.Text);
|
||
|
end else begin
|
||
|
inherited Assign(ASource);
|
||
|
end;
|
||
|
end;
|
||
|
|
||
|
function TIdReplyPOP3.CheckIfCodeIsValid(const ACode: string): Boolean;
|
||
|
var
|
||
|
LOrd: Integer;
|
||
|
begin
|
||
|
LOrd := PosInStrArray(ACode, VALID_POP3_STR, False);
|
||
|
Result := (LOrd > -1) or (Trim(ACode) = '');
|
||
|
end;
|
||
|
|
||
|
constructor TIdReplyPOP3.CreateWithReplyTexts(ACollection: TCollection = nil; AReplyTexts: TIdReplies = nil);
|
||
|
begin
|
||
|
inherited CreateWithReplyTexts(ACollection, AReplyTexts);
|
||
|
FCode := ST_OK;
|
||
|
end;
|
||
|
|
||
|
class function TIdReplyPOP3.ExtractTextPosArray(const AStr: String): Integer;
|
||
|
begin
|
||
|
Result := PosInStrArray(Copy(AStr, 1, FindCodeTextDelim(AStr) - 1), VALID_POP3_STR, False);
|
||
|
end;
|
||
|
|
||
|
class function TIdReplyPOP3.FindCodeTextDelim(const AText: String): Integer;
|
||
|
var
|
||
|
LMin, LSpace: Integer;
|
||
|
LBuf: String;
|
||
|
LAddBackFlag: Boolean; //if we deleted a begging -, we need to add it back
|
||
|
begin
|
||
|
LAddBackFlag := False;
|
||
|
//we do things this way because a line can start with a minus as in
|
||
|
//-ERR [IN-USE] Mail box in use
|
||
|
LBuf := AText;
|
||
|
if TextStartsWith(LBuf, '-') then begin
|
||
|
Delete(LBuf, 1, 1);
|
||
|
LAddBackFlag := True;
|
||
|
end;
|
||
|
LMin := IndyPos(' ', LBuf);
|
||
|
LSpace := IndyPos('-', LBuf);
|
||
|
if LMin > 0 then begin
|
||
|
if (LSpace <> 0) and (LMin > LSpace) then begin
|
||
|
Result := LSpace;
|
||
|
end else begin
|
||
|
Result := LMin;
|
||
|
end;
|
||
|
end else begin
|
||
|
if LSpace <> 0 then begin
|
||
|
Result := LSpace;
|
||
|
end else begin
|
||
|
Result := Length(AText) + 1;
|
||
|
end;
|
||
|
end;
|
||
|
if LAddBackFlag then begin
|
||
|
Inc(Result);
|
||
|
end;
|
||
|
end;
|
||
|
|
||
|
function TIdReplyPOP3.GetFormattedReply: TStrings;
|
||
|
var
|
||
|
i: Integer;
|
||
|
begin
|
||
|
Result := GetFormattedReplyStrings;
|
||
|
if Code <> '' then begin
|
||
|
if FText.Count > 0 then begin
|
||
|
for i := 0 to FText.Count - 1 do begin
|
||
|
if i < FText.Count - 1 then begin
|
||
|
if (Code = ST_ERR) and (FEnhancedCode <> '') then begin
|
||
|
Result.Add(Code + '-' + FEnhancedCode + ' ' + FText[i]);
|
||
|
end else begin
|
||
|
Result.Add(Code + '-' + FText[i]);
|
||
|
end;
|
||
|
end else begin
|
||
|
if (Code = ST_ERR) and (FEnhancedCode <> '') then begin
|
||
|
Result.Add(Code + ' ' + FEnhancedCode + ' ' + FText[i]);
|
||
|
end else begin
|
||
|
Result.Add(Code + ' ' + FText[i]);
|
||
|
end;
|
||
|
end;
|
||
|
end;
|
||
|
end else begin
|
||
|
Result.Add(Code);
|
||
|
end;
|
||
|
end else if FText.Count > 0 then begin
|
||
|
Result.AddStrings(FText);
|
||
|
end;
|
||
|
end;
|
||
|
|
||
|
class function TIdReplyPOP3.IsEndMarker(const ALine: string): Boolean;
|
||
|
var
|
||
|
LPos: Integer;
|
||
|
begin
|
||
|
Result := False;
|
||
|
LPos := FindCodeTextDelim(ALine);
|
||
|
if LPos > 0 then begin
|
||
|
if LPos > Length(ALine) then begin
|
||
|
Result := True;
|
||
|
end else begin
|
||
|
Result := ALine[LPos] <> '-';
|
||
|
end;
|
||
|
end;
|
||
|
end;
|
||
|
|
||
|
class function TIdReplyPOP3.IsValidEnhancedCode(const AText : String; const AStrict : Boolean = False): Boolean;
|
||
|
var
|
||
|
LBuf : String;
|
||
|
i : integer;
|
||
|
begin
|
||
|
Result := Trim(AText) = '';
|
||
|
if not Result then begin
|
||
|
LBuf := AText;
|
||
|
if (LBuf <> '') and TextStartsWith(LBuf, '[') then begin
|
||
|
Delete(LBuf, 1, 1);
|
||
|
if (LBuf <> '') and TextEndsWith(LBuf, ']') then begin
|
||
|
LBuf := Fetch(LBuf, ']');
|
||
|
if AStrict then begin
|
||
|
Result := PosInStrArray(LBuf, VALID_ENH_CODES) > -1;
|
||
|
end else begin
|
||
|
{We don't use PosInStrArray because we only want the fist
|
||
|
charactors in our string to match. This is necessary because
|
||
|
the POP3 enhanced codes will be hierarchical as time goes on.
|
||
|
}
|
||
|
for i := Low(VALID_ENH_CODES) to High(VALID_ENH_CODES) do begin
|
||
|
if TextStartsWith(LBuf, VALID_ENH_CODES[i]) then begin
|
||
|
Result := True;
|
||
|
Exit;
|
||
|
end;
|
||
|
end;
|
||
|
end;
|
||
|
end;
|
||
|
end;
|
||
|
end;
|
||
|
end;
|
||
|
|
||
|
procedure TIdReplyPOP3.RaiseReplyError;
|
||
|
begin
|
||
|
raise EIdReplyPOP3Error.CreateError(Code, Text.Text);
|
||
|
end;
|
||
|
|
||
|
procedure TIdReplyPOP3.SetEnhancedCode(const AValue: String);
|
||
|
var
|
||
|
LBuf : String;
|
||
|
begin
|
||
|
LBuf := AValue;
|
||
|
if LBuf = '' then begin
|
||
|
FEnhancedCode := '';
|
||
|
end else begin
|
||
|
LBuf := UpperCase(LBuf);
|
||
|
if (LBuf[1] <> '[') then begin
|
||
|
LBuf := '[' + LBuf;
|
||
|
end;
|
||
|
if (LBuf[Length(LBuf)] <> ']') then begin
|
||
|
LBuf := LBuf + ']';
|
||
|
end;
|
||
|
if IsValidEnhancedCode(LBuf, True) then begin
|
||
|
FEnhancedCode := LBuf;
|
||
|
end else begin
|
||
|
raise EIdPOP3ReplyInvalidEnhancedCode.Create(RSPOP3ReplyInvalidEnhancedCode + AValue);
|
||
|
end;
|
||
|
end;
|
||
|
end;
|
||
|
|
||
|
procedure TIdReplyPOP3.SetFormattedReply(const AValue: TStrings);
|
||
|
var
|
||
|
i: Integer;
|
||
|
idx : Integer;
|
||
|
LOrd : Integer;
|
||
|
LBuf : String;
|
||
|
begin
|
||
|
Clear;
|
||
|
if AValue.Count > 0 then begin
|
||
|
LOrd := ExtractTextPosArray(AValue[0]);
|
||
|
if LOrd > -1 then begin
|
||
|
Code := VALID_POP3_STR[LOrd];
|
||
|
end;
|
||
|
for i := 0 to AValue.Count - 1 do begin
|
||
|
if LOrd = -1 then begin
|
||
|
LOrd := ExtractTextPosArray(AValue[i]);
|
||
|
end;
|
||
|
idx := FindCodeTextDelim(AValue[i]);
|
||
|
LBuf := Copy(AValue[i], idx+1, MaxInt);
|
||
|
if (Code = ST_ERR) and IsValidEnhancedCode(Fetch(LBuf,' ',False)) then begin
|
||
|
//don't use EnhancedCode property set method because that does
|
||
|
//a tighter validation than we should use for parsing replies
|
||
|
//from a server.
|
||
|
FEnhancedCode := Fetch(LBuf);
|
||
|
end;
|
||
|
Text.Add(LBuf);
|
||
|
end;
|
||
|
if LOrd = -1 then begin
|
||
|
Code := ST_ERR;
|
||
|
end;
|
||
|
end;
|
||
|
end;
|
||
|
|
||
|
{ TIdRepliesPOP3 }
|
||
|
|
||
|
constructor TIdRepliesPOP3.Create(AOwner: TPersistent);
|
||
|
begin
|
||
|
inherited Create(AOwner, TIdReplyPOP3);
|
||
|
end;
|
||
|
|
||
|
{ EIdReplyPOP3Error }
|
||
|
|
||
|
constructor EIdReplyPOP3Error.CreateError(const AErrorCode, AReplyMessage: string;
|
||
|
const AEnhancedCode : String = '');
|
||
|
begin
|
||
|
inherited Create(AReplyMessage);
|
||
|
FErrorCode := AErrorCode;
|
||
|
FEnhancedCode := AEnhancedCode;
|
||
|
end;
|
||
|
|
||
|
end.
|