618 lines
19 KiB
Plaintext
618 lines
19 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.9 10.10.2004 13:46:00 ARybin
|
||
|
dont add default port to URI
|
||
|
|
||
|
Rev 1.8 2004.02.03 5:44:40 PM czhower
|
||
|
Name changes
|
||
|
|
||
|
Rev 1.7 2004.01.22 5:27:24 PM czhower
|
||
|
Fixed compile errors.
|
||
|
|
||
|
Rev 1.6 1/22/2004 4:06:56 PM SPerry
|
||
|
fixed set problems
|
||
|
|
||
|
Rev 1.5 10/5/2003 11:44:24 PM GGrieve
|
||
|
Use IsLeadChar
|
||
|
|
||
|
Rev 1.4 6/9/2003 9:35:58 PM BGooijen
|
||
|
%00 is valid now too
|
||
|
|
||
|
Rev 1.3 2003.05.09 10:30:16 PM czhower
|
||
|
|
||
|
Rev 1.2 2003.04.11 9:41:34 PM czhower
|
||
|
|
||
|
Rev 1.1 29/11/2002 9:56:10 AM SGrobety Version: 1.1
|
||
|
Changed URL encoding
|
||
|
|
||
|
Rev 1.0 21/11/2002 12:42:52 PM SGrobety Version: Indy 10
|
||
|
|
||
|
Rev 1.0 11/13/2002 08:04:10 AM JPMugaas
|
||
|
}
|
||
|
|
||
|
unit IdURI;
|
||
|
|
||
|
{Details of implementation
|
||
|
-------------------------
|
||
|
2002-Apr-14 Peter Mee
|
||
|
- Fixed reset. Now resets FParams as well - wasn't before.
|
||
|
2001-Nov Doychin Bondzhev
|
||
|
- Fixes in URLEncode. There is difference when encoding Path+Doc and Params
|
||
|
2001-Oct-17 Peter Mee
|
||
|
- Minor speed improvement - removed use of NormalizePath in SetURI.
|
||
|
- Fixed bug that was cutting off the first two chars of the host when a
|
||
|
username / password present.
|
||
|
- Fixed bug that prevented username and password being updated.
|
||
|
- Fixed bug that was leaving the bookmark in the document when no ? or =
|
||
|
parameters existed.
|
||
|
2001-Feb-18 Doychin Bondzhev
|
||
|
- Added UserName and Password to support URI's like
|
||
|
http://username:password@hostname:port/path/document#bookmark
|
||
|
}
|
||
|
|
||
|
interface
|
||
|
|
||
|
{$i IdCompilerDefines.inc}
|
||
|
|
||
|
uses
|
||
|
IdException,
|
||
|
IdGlobal;
|
||
|
|
||
|
type
|
||
|
TIdURIOptionalFields = (ofAuthInfo, ofBookmark);
|
||
|
TIdURIOptionalFieldsSet = set of TIdURIOptionalFields;
|
||
|
|
||
|
TIdURI = class
|
||
|
protected
|
||
|
FDocument: string;
|
||
|
FProtocol: string;
|
||
|
FURI: String;
|
||
|
FPort: string;
|
||
|
Fpath: string;
|
||
|
FHost: string;
|
||
|
FBookmark: string;
|
||
|
FUserName: string;
|
||
|
FPassword: string;
|
||
|
FParams: string;
|
||
|
FIPVersion: TIdIPVersion;
|
||
|
//
|
||
|
procedure SetURI(const Value: String);
|
||
|
function GetURI: String;
|
||
|
public
|
||
|
constructor Create(const AURI: string = ''); virtual; {Do not Localize}
|
||
|
function GetFullURI(const AOptionalFields: TIdURIOptionalFieldsSet = [ofAuthInfo, ofBookmark]): String;
|
||
|
function GetPathAndParams: String;
|
||
|
class procedure NormalizePath(var APath: string);
|
||
|
class function URLDecode(ASrc: string; AByteEncoding: IIdTextEncoding = nil
|
||
|
{$IFDEF STRING_IS_ANSI}; ADestEncoding: IIdTextEncoding = nil{$ENDIF}
|
||
|
): string;
|
||
|
class function URLEncode(const ASrc: string; AByteEncoding: IIdTextEncoding = nil
|
||
|
{$IFDEF STRING_IS_ANSI}; ASrcEncoding: IIdTextEncoding = nil{$ENDIF}
|
||
|
): string;
|
||
|
class function ParamsEncode(const ASrc: string; AByteEncoding: IIdTextEncoding = nil
|
||
|
{$IFDEF STRING_IS_ANSI}; ASrcEncoding: IIdTextEncoding = nil{$ENDIF}
|
||
|
): string;
|
||
|
class function PathEncode(const ASrc: string; AByteEncoding: IIdTextEncoding = nil
|
||
|
{$IFDEF STRING_IS_ANSI}; ASrcEncoding: IIdTextEncoding = nil{$ENDIF}
|
||
|
): string;
|
||
|
//
|
||
|
property Bookmark : string read FBookmark write FBookMark;
|
||
|
property Document: string read FDocument write FDocument;
|
||
|
property Host: string read FHost write FHost;
|
||
|
property Password: string read FPassword write FPassword;
|
||
|
property Path: string read FPath write FPath;
|
||
|
property Params: string read FParams write FParams;
|
||
|
property Port: string read FPort write FPort;
|
||
|
property Protocol: string read FProtocol write FProtocol;
|
||
|
property URI: string read GetURI write SetURI;
|
||
|
property Username: string read FUserName write FUserName;
|
||
|
property IPVersion : TIdIPVersion read FIPVersion write FIPVersion;
|
||
|
end;
|
||
|
|
||
|
EIdURIException = class(EIdException);
|
||
|
|
||
|
implementation
|
||
|
|
||
|
uses
|
||
|
IdGlobalProtocols, IdResourceStringsProtocols, IdUriUtils,
|
||
|
SysUtils;
|
||
|
|
||
|
{ TIdURI }
|
||
|
|
||
|
constructor TIdURI.Create(const AURI: string = ''); {Do not Localize}
|
||
|
begin
|
||
|
inherited Create;
|
||
|
if length(AURI) > 0 then begin
|
||
|
URI := AURI;
|
||
|
end;
|
||
|
end;
|
||
|
|
||
|
class procedure TIdURI.NormalizePath(var APath: string);
|
||
|
var
|
||
|
i: Integer;
|
||
|
LChar: Char;
|
||
|
{$IFDEF STRING_IS_IMMUTABLE}
|
||
|
LSB: TIdStringBuilder;
|
||
|
{$ENDIF}
|
||
|
begin
|
||
|
{$IFDEF STRING_IS_IMMUTABLE}
|
||
|
LSB := nil;
|
||
|
{$ENDIF}
|
||
|
|
||
|
// Normalize the directory delimiters to follow the UNIX syntax
|
||
|
|
||
|
// RLebeau 8/10/2010: only normalize within the actual path,
|
||
|
// nothing outside of it...
|
||
|
|
||
|
i := Pos(':', APath); {do not localize}
|
||
|
if i > 0 then begin
|
||
|
Inc(i);
|
||
|
// if the path does not already begin with '//', then do not
|
||
|
// normalize the first two characters if they would produce
|
||
|
// '//', as that will change the semantics of the URL...
|
||
|
if CharIsInSet(APath, I, '\/') and CharIsInSet(APath, I+1, '\/') then begin
|
||
|
Inc(i, 2);
|
||
|
end;
|
||
|
end else begin
|
||
|
i := 1;
|
||
|
end;
|
||
|
|
||
|
while i <= Length(APath) do begin
|
||
|
LChar := APath[i];
|
||
|
{$IFDEF STRING_IS_ANSI}
|
||
|
if IsLeadChar(LChar) then begin
|
||
|
Inc(i, 2);
|
||
|
Continue;
|
||
|
end;
|
||
|
{$ENDIF}
|
||
|
if (LChar = '?') or (LChar = '#') then begin {Do not Localize}
|
||
|
// stop normalizing at query/fragment portion of the URL
|
||
|
Break;
|
||
|
end;
|
||
|
if LChar = '\' then begin {Do not Localize}
|
||
|
{$IFDEF STRING_IS_IMMUTABLE}
|
||
|
if LSB = nil then begin
|
||
|
LSB := TIdStringBuilder.Create(APath);
|
||
|
end;
|
||
|
LSB[i-1] := '/'; {Do not Localize}
|
||
|
{$ELSE}
|
||
|
APath[i] := '/'; {Do not Localize}
|
||
|
{$ENDIF}
|
||
|
end;
|
||
|
Inc(i);
|
||
|
end;
|
||
|
|
||
|
{$IFDEF STRING_IS_IMMUTABLE}
|
||
|
if LSB <> nil then begin
|
||
|
APath := LSB.ToString;
|
||
|
end;
|
||
|
{$ENDIF}
|
||
|
end;
|
||
|
|
||
|
procedure TIdURI.SetURI(const Value: String);
|
||
|
var
|
||
|
LBuffer: string;
|
||
|
LTokenPos: Integer;
|
||
|
LURI: string;
|
||
|
begin
|
||
|
FURI := Value;
|
||
|
NormalizePath(FURI);
|
||
|
LURI := FURI;
|
||
|
FHost := ''; {Do not Localize}
|
||
|
FProtocol := ''; {Do not Localize}
|
||
|
FPath := ''; {Do not Localize}
|
||
|
FDocument := ''; {Do not Localize}
|
||
|
FPort := ''; {Do not Localize}
|
||
|
FBookmark := ''; {Do not Localize}
|
||
|
FUsername := ''; {Do not Localize}
|
||
|
FPassword := ''; {Do not Localize}
|
||
|
FParams := ''; {Do not localise} //Peter Mee
|
||
|
FIPVersion := Id_IPv4;
|
||
|
|
||
|
LTokenPos := IndyPos('://', LURI); {Do not Localize}
|
||
|
if LTokenPos > 0 then begin
|
||
|
// absolute URI
|
||
|
// What to do when data don't match configuration ?? {Do not Localize}
|
||
|
// Get the protocol
|
||
|
FProtocol := Copy(LURI, 1, LTokenPos - 1);
|
||
|
Delete(LURI, 1, LTokenPos + 2);
|
||
|
// separate the path from the parameters
|
||
|
LTokenPos := IndyPos('?', LURI); {Do not Localize}
|
||
|
// RLebeau: this is BAD! It messes up JSP and similar URLs that use '=' characters in the document
|
||
|
{if LTokenPos = 0 then begin
|
||
|
LTokenPos := IndyPos('=', LURI); {Do not Localize
|
||
|
end;}
|
||
|
if LTokenPos > 0 then begin
|
||
|
FParams := Copy(LURI, LTokenPos + 1, MaxInt);
|
||
|
LURI := Copy(LURI, 1, LTokenPos - 1);
|
||
|
// separate the bookmark from the parameters
|
||
|
LTokenPos := IndyPos('#', FParams); {Do not Localize}
|
||
|
if LTokenPos > 0 then begin {Do not Localize}
|
||
|
FBookmark := FParams;
|
||
|
FParams := Fetch(FBookmark, '#'); {Do not Localize}
|
||
|
end;
|
||
|
end else begin
|
||
|
// separate the path from the bookmark
|
||
|
LTokenPos := IndyPos('#', LURI); {Do not Localize}
|
||
|
if LTokenPos > 0 then begin {Do not Localize}
|
||
|
FBookmark := Copy(LURI, LTokenPos + 1, MaxInt);
|
||
|
LURI := Copy(LURI, 1, LTokenPos - 1);
|
||
|
end;
|
||
|
end;
|
||
|
// Get the user name, password, host and the port number
|
||
|
LBuffer := Fetch(LURI, '/', True); {Do not Localize}
|
||
|
// Get username and password
|
||
|
LTokenPos := RPos('@', LBuffer); {Do not Localize}
|
||
|
if LTokenPos > 0 then begin
|
||
|
FPassword := Copy(LBuffer, 1, LTokenPos - 1);
|
||
|
Delete(LBuffer, 1, LTokenPos);
|
||
|
FUserName := Fetch(FPassword, ':'); {Do not Localize}
|
||
|
// Ignore cases where there is only password (http://:password@host/pat/doc)
|
||
|
if Length(FUserName) = 0 then begin
|
||
|
FPassword := ''; {Do not Localize}
|
||
|
end;
|
||
|
end;
|
||
|
// Get the host and the port number
|
||
|
if (IndyPos('[', LBuffer) > 0) and (IndyPos(']', LBuffer) > IndyPos('[', LBuffer)) then begin {Do not Localize}
|
||
|
//This is for IPv6 Hosts
|
||
|
FHost := Fetch(LBuffer, ']'); {Do not Localize}
|
||
|
Fetch(FHost, '['); {Do not Localize}
|
||
|
Fetch(LBuffer, ':'); {Do not Localize}
|
||
|
FIPVersion := Id_IPv6;
|
||
|
end else begin
|
||
|
FHost := Fetch(LBuffer, ':', True); {Do not Localize}
|
||
|
end;
|
||
|
FPort := LBuffer;
|
||
|
// Get the path
|
||
|
LTokenPos := RPos('/', LURI, -1);
|
||
|
if LTokenPos > 0 then begin
|
||
|
FPath := '/' + Copy(LURI, 1, LTokenPos); {Do not Localize}
|
||
|
Delete(LURI, 1, LTokenPos);
|
||
|
end else begin
|
||
|
FPath := '/'; {Do not Localize}
|
||
|
end;
|
||
|
end else begin
|
||
|
// received an absolute path, not an URI
|
||
|
LTokenPos := IndyPos('?', LURI); {Do not Localize}
|
||
|
// RLebeau: this is BAD! It messes up JSP and similar URLs that use '=' characters in the document
|
||
|
{if LTokenPos = 0 then begin
|
||
|
LTokenPos := IndyPos('=', LURI); {Do not Localize
|
||
|
end;}
|
||
|
if LTokenPos > 0 then begin // The case when there is parameters after the document name
|
||
|
FParams := Copy(LURI, LTokenPos + 1, MaxInt);
|
||
|
LURI := Copy(LURI, 1, LTokenPos - 1);
|
||
|
// separate the bookmark from the parameters
|
||
|
LTokenPos := IndyPos('#', FParams); {Do not Localize}
|
||
|
if LTokenPos > 0 then begin
|
||
|
FBookmark := FParams;
|
||
|
FParams := Fetch(FBookmark, '#'); {Do not Localize}
|
||
|
end;
|
||
|
end else begin
|
||
|
// separate the bookmark from the path
|
||
|
LTokenPos := IndyPos('#', LURI); {Do not Localize}
|
||
|
if LTokenPos > 0 then begin // The case when there is a bookmark after the document name
|
||
|
FBookmark := Copy(LURI, LTokenPos + 1, MaxInt);
|
||
|
LURI := Copy(LURI, 1, LTokenPos - 1);
|
||
|
end;
|
||
|
end;
|
||
|
// Get the path
|
||
|
LTokenPos := RPos('/', LURI, -1); {Do not Localize}
|
||
|
if LTokenPos > 0 then begin
|
||
|
FPath := Copy(LURI, 1, LTokenPos);
|
||
|
Delete(LURI, 1, LTokenPos);
|
||
|
end;
|
||
|
end;
|
||
|
// Get the document
|
||
|
FDocument := LURI;
|
||
|
end;
|
||
|
|
||
|
function TIdURI.GetURI: String;
|
||
|
begin
|
||
|
FURI := GetFullURI;
|
||
|
// Result must contain only the proto://host/path/document
|
||
|
// If you need the full URI then you have to call GetFullURI
|
||
|
Result := GetFullURI([]);
|
||
|
end;
|
||
|
|
||
|
class function TIdURI.URLDecode(ASrc: string; AByteEncoding: IIdTextEncoding = nil
|
||
|
{$IFDEF STRING_IS_ANSI}; ADestEncoding: IIdTextEncoding = nil{$ENDIF}
|
||
|
): string;
|
||
|
var
|
||
|
i: Integer;
|
||
|
ESC: string;
|
||
|
LChars: TIdWideChars;
|
||
|
LBytes: TIdBytes;
|
||
|
begin
|
||
|
Result := ''; {Do not Localize}
|
||
|
LChars := nil;
|
||
|
LBytes := nil;
|
||
|
EnsureEncoding(AByteEncoding, encUTF8);
|
||
|
// S.G. 27/11/2002: Spaces is NOT to be encoded as "+".
|
||
|
// S.G. 27/11/2002: "+" is a field separator in query parameter, space is...
|
||
|
// S.G. 27/11/2002: well, a space
|
||
|
// ASrc := ReplaceAll(ASrc, '+', ' '); {do not localize}
|
||
|
i := 1;
|
||
|
while i <= Length(ASrc) do begin
|
||
|
if ASrc[i] <> '%' then begin {do not localize}
|
||
|
AppendByte(LBytes, Ord(ASrc[i])); // Copy the char
|
||
|
Inc(i); // Then skip it
|
||
|
end else begin
|
||
|
Inc(i); // skip the % char
|
||
|
if not CharIsInSet(ASrc, i, 'uU') then begin {do not localize}
|
||
|
// simple ESC char
|
||
|
ESC := Copy(ASrc, i, 2); // Copy the escape code
|
||
|
Inc(i, 2); // Then skip it.
|
||
|
try
|
||
|
AppendByte(LBytes, Byte(IndyStrToInt('$' + ESC))); {do not localize}
|
||
|
except end;
|
||
|
end else
|
||
|
begin
|
||
|
// unicode ESC code
|
||
|
|
||
|
// RLebeau 5/10/2006: under Win32, the character will likely end
|
||
|
// up as '?' in the Result when converted from Unicode to Ansi,
|
||
|
// but at least the URL will be parsed properly
|
||
|
|
||
|
ESC := Copy(ASrc, i+1, 4); // Copy the escape code
|
||
|
Inc(i, 5); // Then skip it.
|
||
|
try
|
||
|
if LChars = nil then begin
|
||
|
SetLength(LChars, 1);
|
||
|
end;
|
||
|
LChars[0] := WideChar(IndyStrToInt('$' + ESC)); {do not localize}
|
||
|
AppendBytes(LBytes, AByteEncoding.GetBytes(LChars));
|
||
|
except end;
|
||
|
end;
|
||
|
end;
|
||
|
end;
|
||
|
{$IFDEF STRING_IS_ANSI}
|
||
|
EnsureEncoding(ADestEncoding, encOSDefault);
|
||
|
CheckByteEncoding(LBytes, AByteEncoding, ADestEncoding);
|
||
|
SetString(Result, PAnsiChar(LBytes), Length(LBytes));
|
||
|
{$ELSE}
|
||
|
Result := AByteEncoding.GetString(LBytes);
|
||
|
{$ENDIF}
|
||
|
end;
|
||
|
|
||
|
class function TIdURI.ParamsEncode(const ASrc: string; AByteEncoding: IIdTextEncoding = nil
|
||
|
{$IFDEF STRING_IS_ANSI}; ASrcEncoding: IIdTextEncoding = nil{$ENDIF}
|
||
|
): string;
|
||
|
const
|
||
|
UnsafeChars: TIdUnicodeString = '*<>#%"{}|\^[]`'; {do not localize}
|
||
|
var
|
||
|
I, J, CharLen, ByteLen: Integer;
|
||
|
Buf: TIdBytes;
|
||
|
{$IFDEF STRING_IS_ANSI}
|
||
|
LChars: TIdWideChars;
|
||
|
{$ENDIF}
|
||
|
LChar: WideChar;
|
||
|
begin
|
||
|
Result := ''; {Do not Localize}
|
||
|
|
||
|
// keep the compiler happy
|
||
|
Buf := nil;
|
||
|
{$IFDEF STRING_IS_ANSI}
|
||
|
LChars := nil;
|
||
|
{$ENDIF}
|
||
|
|
||
|
if ASrc = '' then begin
|
||
|
Exit;
|
||
|
end;
|
||
|
|
||
|
EnsureEncoding(AByteEncoding, encUTF8);
|
||
|
{$IFDEF STRING_IS_ANSI}
|
||
|
EnsureEncoding(ASrcEncoding, encOSDefault);
|
||
|
LChars := ASrcEncoding.GetChars(RawToBytes(ASrc[1], Length(ASrc)));
|
||
|
{$ENDIF}
|
||
|
|
||
|
// 2 Chars to handle UTF-16 surrogates
|
||
|
SetLength(Buf, AByteEncoding.GetMaxByteCount(2));
|
||
|
|
||
|
I := 0;
|
||
|
while I < Length({$IFDEF STRING_IS_UNICODE}ASrc{$ELSE}LChars{$ENDIF}) do
|
||
|
begin
|
||
|
LChar := {$IFDEF STRING_IS_UNICODE}ASrc[I+1]{$ELSE}LChars[I]{$ENDIF};
|
||
|
|
||
|
// S.G. 27/11/2002: Changed the parameter encoding: Even in parameters, a space
|
||
|
// S.G. 27/11/2002: is much more likely to be meaning "space" than "this is
|
||
|
// S.G. 27/11/2002: a new parameter"
|
||
|
// S.G. 27/11/2002: ref: Message-ID: <3de30169@newsgroups.borland.com> borland.public.delphi.internet.winsock
|
||
|
// S.G. 27/11/2002: Most low-ascii is actually Ok in parameters encoding.
|
||
|
|
||
|
// RLebeau 1/7/09: using Char() for #128-#255 because in D2009, the compiler
|
||
|
// may change characters >= #128 from their Ansi codepage value to their true
|
||
|
// Unicode codepoint value, depending on the codepage used for the source code.
|
||
|
// For instance, #128 may become #$20AC...
|
||
|
|
||
|
if WideCharIsInSet(UnsafeChars, LChar) or (Ord(LChar) < 33) or (Ord(LChar) > 127) then
|
||
|
begin
|
||
|
CharLen := CalcUTF16CharLength(
|
||
|
{$IFDEF STRING_IS_UNICODE}ASrc, I+1{$ELSE}LChars, I{$ENDIF}
|
||
|
); // calculate length including surrogates
|
||
|
ByteLen := AByteEncoding.GetBytes(
|
||
|
{$IFDEF STRING_IS_UNICODE}ASrc, I+1{$ELSE}LChars, I{$ENDIF},
|
||
|
CharLen, Buf, 0); // explicit Unicode->Ansi conversion
|
||
|
for J := 0 to ByteLen-1 do begin
|
||
|
Result := Result + '%' + IntToHex(Ord(Buf[J]), 2); {do not localize}
|
||
|
end;
|
||
|
Inc(I, CharLen);
|
||
|
end else
|
||
|
begin
|
||
|
Result := Result + Char(LChar);
|
||
|
Inc(I);
|
||
|
end;
|
||
|
end;
|
||
|
end;
|
||
|
|
||
|
class function TIdURI.PathEncode(const ASrc: string; AByteEncoding: IIdTextEncoding = nil
|
||
|
{$IFDEF STRING_IS_ANSI}; ASrcEncoding: IIdTextEncoding = nil{$ENDIF}
|
||
|
): string;
|
||
|
const
|
||
|
UnsafeChars = '*<>#%"{}|\^[]`+'; {do not localize}
|
||
|
var
|
||
|
I, J, CharLen, ByteLen: Integer;
|
||
|
Buf: TIdBytes;
|
||
|
{$IFDEF STRING_IS_ANSI}
|
||
|
LChars: TIdWideChars;
|
||
|
{$ENDIF}
|
||
|
LChar: WideChar;
|
||
|
begin
|
||
|
Result := ''; {Do not Localize}
|
||
|
|
||
|
// keep the compiler happy
|
||
|
Buf := nil;
|
||
|
{$IFDEF STRING_IS_ANSI}
|
||
|
LChars := nil;
|
||
|
{$ENDIF}
|
||
|
|
||
|
if ASrc = '' then begin
|
||
|
Exit;
|
||
|
end;
|
||
|
|
||
|
EnsureEncoding(AByteEncoding, encUTF8);
|
||
|
{$IFDEF STRING_IS_ANSI}
|
||
|
EnsureEncoding(ASrcEncoding, encOSDefault);
|
||
|
LChars := ASrcEncoding.GetChars(RawToBytes(ASrc[1], Length(ASrc)));
|
||
|
{$ENDIF}
|
||
|
|
||
|
// 2 Chars to handle UTF-16 surrogates
|
||
|
SetLength(Buf, AByteEncoding.GetMaxByteCount(2));
|
||
|
|
||
|
I := 0;
|
||
|
while I < Length({$IFDEF STRING_IS_UNICODE}ASrc{$ELSE}LChars{$ENDIF}) do
|
||
|
begin
|
||
|
LChar := {$IFDEF STRING_IS_UNICODE}ASrc[I+1]{$ELSE}LChars[I]{$ENDIF};
|
||
|
|
||
|
if WideCharIsInSet(UnsafeChars, LChar) or (Ord(LChar) < 33) or (Ord(LChar) > 127) then
|
||
|
begin
|
||
|
CharLen := CalcUTF16CharLength(
|
||
|
{$IFDEF STRING_IS_UNICODE}ASrc, I+1{$ELSE}LChars, I{$ENDIF}
|
||
|
); // calculate length including surrogates
|
||
|
ByteLen := AByteEncoding.GetBytes(
|
||
|
{$IFDEF STRING_IS_UNICODE}ASrc, I+1{$ELSE}LChars, I{$ENDIF},
|
||
|
CharLen, Buf, 0); // explicit Unicode->Ansi conversion
|
||
|
for J := 0 to ByteLen-1 do begin
|
||
|
Result := Result + '%' + IntToHex(Ord(Buf[J]), 2); {do not localize}
|
||
|
end;
|
||
|
Inc(I, CharLen);
|
||
|
end else
|
||
|
begin
|
||
|
Result := Result + Char(LChar);
|
||
|
Inc(I);
|
||
|
end;
|
||
|
end;
|
||
|
end;
|
||
|
|
||
|
class function TIdURI.URLEncode(const ASrc: string; AByteEncoding: IIdTextEncoding = nil
|
||
|
{$IFDEF STRING_IS_ANSI}; ASrcEncoding: IIdTextEncoding = nil{$ENDIF}
|
||
|
): string;
|
||
|
var
|
||
|
LUri: TIdURI;
|
||
|
begin
|
||
|
LUri := TIdURI.Create(ASrc);
|
||
|
try
|
||
|
LUri.Path := PathEncode(LUri.Path, AByteEncoding
|
||
|
{$IFDEF STRING_IS_ANSI}, ASrcEncoding{$ENDIF}
|
||
|
);
|
||
|
LUri.Document := PathEncode(LUri.Document, AByteEncoding
|
||
|
{$IFDEF STRING_IS_ANSI}, ASrcEncoding{$ENDIF}
|
||
|
);
|
||
|
LUri.Params := ParamsEncode(LUri.Params, AByteEncoding
|
||
|
{$IFDEF STRING_IS_ANSI}, ASrcEncoding{$ENDIF}
|
||
|
);
|
||
|
Result := LUri.URI;
|
||
|
finally
|
||
|
LUri.Free;
|
||
|
end;
|
||
|
end;
|
||
|
|
||
|
function TIdURI.GetFullURI(const AOptionalFields: TIdURIOptionalFieldsSet): String;
|
||
|
var
|
||
|
LURI: String;
|
||
|
begin
|
||
|
if FProtocol = '' then begin
|
||
|
raise EIdURIException.Create(RSURINoProto);
|
||
|
end;
|
||
|
|
||
|
if FHost = '' then begin
|
||
|
raise EIdURIException.Create(RSURINoHost);
|
||
|
end;
|
||
|
|
||
|
LURI := FProtocol + '://'; {Do not Localize}
|
||
|
|
||
|
if (FUserName <> '') and (ofAuthInfo in AOptionalFields) then begin
|
||
|
LURI := LURI + FUserName;
|
||
|
if FPassword <> '' then begin
|
||
|
LURI := LURI + ':' + FPassword; {Do not Localize}
|
||
|
end;
|
||
|
LURI := LURI + '@'; {Do not Localize}
|
||
|
end;
|
||
|
|
||
|
if IPVersion = Id_IPv6 then begin
|
||
|
LURI := LURI + '[' + FHost + ']'; {Do not Localize}
|
||
|
end else begin
|
||
|
LURI := LURI + FHost;
|
||
|
end;
|
||
|
|
||
|
if FPort <> '' then begin
|
||
|
case PosInStrArray(FProtocol, ['HTTP', 'HTTPS', 'FTP'], False) of {Do not Localize}
|
||
|
0:
|
||
|
begin
|
||
|
if FPort <> '80' then begin
|
||
|
LURI := LURI + ':' + FPort; {Do not Localize}
|
||
|
end;
|
||
|
end;
|
||
|
1:
|
||
|
begin
|
||
|
if FPort <> '443' then begin
|
||
|
LURI := LURI + ':' + FPort; {Do not Localize}
|
||
|
end;
|
||
|
end;
|
||
|
2:
|
||
|
begin
|
||
|
if FPort <> '21' then begin
|
||
|
LURI := LURI + ':' + FPort; {Do not Localize}
|
||
|
end;
|
||
|
end;
|
||
|
else
|
||
|
begin
|
||
|
LURI := LURI + ':' + FPort; {Do not Localize}
|
||
|
end;
|
||
|
end;
|
||
|
end;
|
||
|
|
||
|
LURI := LURI + GetPathAndParams;
|
||
|
|
||
|
if (FBookmark <> '') and (ofBookmark in AOptionalFields) then begin
|
||
|
LURI := LURI + '#' + FBookmark; {Do not Localize}
|
||
|
end;
|
||
|
|
||
|
Result := LURI;
|
||
|
end;
|
||
|
|
||
|
function TIdURI.GetPathAndParams: String;
|
||
|
begin
|
||
|
Result := FPath + FDocument;
|
||
|
if FParams <> '' then begin
|
||
|
Result := Result + '?' + FParams; {Do not Localize}
|
||
|
end;
|
||
|
end;
|
||
|
|
||
|
end.
|