restemplate/indy/Protocols/IdGlobalProtocols.pas

4972 lines
172 KiB
Plaintext
Raw Permalink Blame History

{
$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$
}
{
10 Indy10 1.9 5/4/2005 7:06:24 PM J. Peter Mugaas Attempt to
fix another junked part of the file.
9 Indy10 1.8 5/4/2005 7:02:50 PM J. Peter Mugaas Attempt to
fix a junked file.
8 Indy10 1.7 5/4/2005 6:31:08 PM J. Peter Mugaas These
should now work. I moved a TextWrapping function out of TIdHeaderList
and into IdGlobalProtocols so the FTP List output object can use it and
so we can rework the routine slightly to use StringBuilder in DotNET.
7 Indy10 1.6 4/28/2005 11:02:30 PM J. Peter Mugaas Removed
StrToInt64Def symbol. We now use Sys.StrToInt64 instead.
6 Indy10 1.5 4/28/2005 10:23:14 PM J. Peter Mugaas Should now
work with new API change in CharInSet.
5 Indy10 1.4 4/20/2005 10:44:24 PM Ben Taylor IdSys
changes
4 Indy10 1.3 4/20/2005 12:43:48 AM J. Peter Mugaas Removed
SysUtils from most units and added it to IdGlobalProtocols (works best
that way).
3 Indy10 1.2 4/19/2005 5:19:11 PM J. Peter Mugaas Removed
SysUtils and fixed EIdException reference.
2 Indy10 1.1 4/19/2005 10:15:26 AM J. Peter Mugaas Updates
Rev 1.31 04/03/2005 21:21:56 HHariri
Fix for DirectoryExists and removal of FileCtrl dependency
Rev 1.30 3/3/2005 10:12:38 AM JPMugaas
Fix for compiler warning about DotNET and ByteType.
Rev 1.29 2/12/2005 8:08:02 AM JPMugaas
Attempt to fix MDTM bug where msec was being sent.
Rev 1.28 2/10/2005 2:24:40 PM JPMugaas
Minor Restructures for some new UnixTime Service components.
Rev 1.27 1/15/2005 6:02:46 PM JPMugaas
Byte extract with byte order now use updated code in IdGlobal.
Rev 1.26 1/8/2005 3:59:58 PM JPMugaas
New functions for reading integer values to and from TIdBytes using the
network byte order functions. They should be used for embedding values in
some Internet Protocols such as FSP, SNTP, and maybe others.
Rev 1.25 12/3/2004 3:16:20 PM DSiders
Fixed assignment error in MakeTempFilename.
Rev 1.24 12/1/2004 4:40:42 AM JPMugaas
Fix for GMT Time routine. This has been tested.
Rev 1.23 11/14/2004 10:28:42 PM JPMugaas
Compiler warning in IdGlobalProtocol about an undefined result.
Rev 1.22 12/11/2004 9:31:22 HHariri
Fix for Delphi 5
Rev 1.21 11/11/2004 11:18:04 PM JPMugaas
Function to get the Last Modified file in GMT instead of localtime. Needed
by TIdFSP.
Rev 1.20 2004.10.27 9:17:50 AM czhower
For TIdStrings
Rev 1.19 10/26/2004 10:07:02 PM JPMugaas
Updated refs.
Rev 1.18 10/13/2004 7:48:52 PM DSiders
Modified GetUniqueFilename to pass correct argument type to tempnam function.
Rev 1.17 10/6/2004 11:39:48 PM DSiders
Modified MakeTempFilename to use GetUniqueFilename. File extensions are
omitted on Linux.
Modified GetUniqueFilename to use tempnam function on Linux. Validates path
on Win32 and .Net. Uses platform-specific temp path on Win32 and .Net.
Rev 1.16 9/5/2004 2:55:52 AM JPMugaas
Fixed a range check error in
function TwoCharToWord(AChar1,AChar2: Char):Word;.
Rev 1.15 8/10/04 8:47:16 PM RLebeau
Bug fix for TIdMimeTable.AddMimeType()
Rev 1.14 8/5/04 5:44:40 PM RLebeau
Added GetMIMEDefaultFileExt() function
Rev 1.13 7/23/04 6:51:34 PM RLebeau
Added extra exception handling to IndyCopyFile()
Updated CopyFileTo() to call IndyCopyFile()
TFileStream access right tweak for FileSizeByName()
Rev 1.12 7/8/04 5:23:46 PM RLebeau
Updated CardinalToFourChar() to remove use of local TIdBytes variable
Rev 1.11 11/06/2004 00:22:38 CCostelloe
Implemented GetClockValue for Linux
Rev 1.10 09/06/2004 10:03:00 CCostelloe
Kylix 3 patch
Rev 1.9 02/05/2004 13:20:50 CCostelloe
Added RemoveHeaderEntry for use by IdMessage and IdMessageParts (typically
removing old boundary)
Rev 1.8 2/22/2004 12:09:38 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.7 2/19/2004 11:53:00 PM JPMugaas
Moved some functions out of CoderQuotedPrintable for reuse.
Rev 1.6 2/19/2004 11:40:28 PM JPMugaas
Character to hex translation routine added for QP and some
internationalization work.
Rev 1.5 2/19/2004 3:22:40 PM JPMugaas
ABNFToText and related functions added for some RFC 2234. This is somee
groundwork for RFC 2640 - Internationalization of the File Transfer Protocol.
Rev 1.4 2/16/2004 1:53:34 PM JPMugaas
Moved some routines to the system package.
Rev 1.3 2/11/2004 5:17:50 AM JPMugaas
Bit flip functionality was removed because is problematic on some
architectures. They were used in place of the standard network byte order
conversion routines. On an Intel chip, flip works the same as those but in
architectures where network order is the same as host order, some functions
will fail and you may get strange results. The network byte order conversion
functions provide transparancy amoung architectures.
Rev 1.2 2/9/2004 11:27:48 AM JPMugaas
Some functions weren't working as expected. Renamed them to describe them
better.
Rev 1.1 2/7/2004 7:18:38 PM JPMugaas
Moved some functions out of IdDNSCommon so we can use them elsewhere.
Rev 1.0 2004.02.03 7:46:04 PM czhower
New names
Rev 1.43 1/31/2004 3:31:58 PM JPMugaas
Removed some File System stuff for new package.
Rev 1.42 1/31/2004 1:00:26 AM JPMugaas
FileDateByName was changed to LocalFileDateByName as that uses the Local Time
Zone.
Added BMTDateByName for some GMT-based stuff.
We now use the IdFileSystem*.pas units instead of SysUtils for directory
functions. This should remove a dependancy on platform specific things in
DotNET.
Rev 1.41 1/29/2004 6:22:22 AM JPMugaas
IndyComputerName will now use Environment.MachineName in DotNET. This should
fix the ESMTP bug where IndyComputerName would return nothing causing an EHLO
and HELO command to fail in TIdSMTP under DotNET.
Rev 1.40 2004.01.22 5:58:56 PM czhower
IdCriticalSection
Rev 1.39 14/01/2004 00:16:10 CCostelloe
Updated to remove deprecated warnings by using
TextIsSame/IndyLowerCase/IndyUpperCase
Rev 1.38 2003.12.28 6:50:30 PM czhower
Update for Ticks function
Rev 1.37 4/12/2003 10:24:06 PM GGrieve
Fix to Compile
Rev 1.36 11/29/2003 12:19:50 AM JPMugaas
CompareDateTime added for more accurate DateTime comparisons. Sometimes
comparing two floating point values for equality will fail because they are
of different percision and some fractions such as 1/3 and pi (7/22) can never
be calculated 100% accurately.
Rev 1.35 25/11/2003 12:24:20 PM SGrobety
various IdStream fixes with ReadLn/D6
Rev 1.34 10/16/2003 11:18:10 PM DSiders
Added localization comments.
Corrected spelling error in coimments.
Rev 1.33 10/15/2003 9:53:58 PM GGrieve
Add TIdInterfacedObject
Rev 1.32 10/10/2003 10:52:12 PM BGooijen
Removed IdHexDigits
Rev 1.31 10/8/2003 9:52:40 PM GGrieve
reintroduce GetSystemLocale as IdGetDefaultCharSet
Rev 1.30 10/8/2003 2:25:40 PM GGrieve
Update ROL and ROR for DotNet
Rev 1.29 10/5/2003 11:43:32 PM GGrieve
Add IsLeadChar
Rev 1.28 10/5/2003 5:00:10 PM GGrieve
GetComputerName (once was IndyGetHostName)
Rev 1.27 10/4/2003 9:14:26 PM GGrieve
Remove TIdCardinalBytes - replace with other methods
Rev 1.26 10/3/2003 11:55:50 PM GGrieve
First full DotNet version
Rev 1.25 10/3/2003 5:39:30 PM GGrieve
dotnet work
Rev 1.24 2003.10.02 10:52:48 PM czhower
.Net
Rev 1.23 2003.10.02 9:27:50 PM czhower
DotNet Excludes
Rev 1.22 9/18/2003 07:41:46 PM JPMugaas
Moved GetThreadHandle to IdCoreGlobal.
Rev 1.21 9/10/2003 03:26:42 AM JPMugaas
Added EnsureMsgIDBrackets() function. Checked in on behalf of Remy Lebeau
Rev 1.20 6/27/2003 05:53:28 AM JPMugaas
Removed IsNumeric. That's now in IdCoreGlobal.
Rev 1.19 2003.06.23 2:57:18 PM czhower
Comments added
Rev 1.18 2003.06.23 9:46:54 AM czhower
Russian, Ukranian support for headers.
Rev 1.17 2003.06.13 2:24:40 PM czhower
Expanded TIdCardinalBytes
Rev 1.16 5/13/2003 12:45:50 PM JPMugaas
GetClockValue added for unique clock values.
Rev 1.15 5/8/2003 08:43:14 PM JPMugaas
Function for finding an integer's position in an array of integers. This is
required by some SASL code.
Rev 1.14 4/21/2003 7:52:58 PM BGooijen
other nt version detection, removed non-existing windows versions
Rev 1.13 4/18/2003 09:28:24 PM JPMugaas
Changed Win32 Operating System detection so it can distinguish between
workstation OS NT versions and server versions. I also added specific
detection for Windows NT 4.0 with a Service Pack below 6 (requested by Bas).
Rev 1.12 2003.04.16 10:06:22 PM czhower
Moved DebugOutput to IdCoreGlobal
Rev 1.11 4/10/2003 02:54:32 PM JPMugaas
Improvement for FTP STOU command. Unique filename now uses
IdGlobal.GetUniqueFileName instead of Rand. I also fixed GetUniqueFileName
so that it can accept an empty path specification.
Rev 1.10 4/5/2003 10:39:06 PM BGooijen
LAM,LPM were not initialized
Rev 1.9 4/5/2003 04:12:00 AM JPMugaas
Date Time should now be able to process AM/PM.
Rev 1.8 4/4/2003 11:02:56 AM JPMugaas
Added GetUniqueFileName for the Virtual FTP File System component.
Rev 1.7 20/3/2003 19:15:46 GGrieve
Fix GMTToLocalDateTime for empty content
Rev 1.6 3/9/2003 04:34:40 PM JPMugaas
FileDateByName now works on directories.
Rev 1.5 2/14/2003 11:50:58 AM JPMugaas
Removed a function for giving an OS identifier in the FTP server because we
no longer use that function.
Rev 1.4 1/27/2003 12:30:22 AM JPMugaas
Forgot to add a space after one OS type. That makes the job a little easier
for the FTP Server SYST command handler.
Rev 1.3 1/26/2003 11:56:30 PM JPMugaas
Added function for returning an OS descriptor for combining with a FTP Server
SysDescription for the SYST command reply. This can also optionally return
the true system identifier.
Rev 1.2 1/9/2003 05:39:08 PM JPMugaas
Added workaround for if the date is missing a space after a comma.
Rev 1.1 12/29/2002 2:13:14 PM JPMugaas
Moved THandle to IdCoreGlobal for new function used in the core.
Rev 1.0 11/13/2002 08:29:32 AM JPMugaas
Initial import from FTP VC.
}
unit IdGlobalProtocols;
interface
{$i IdCompilerDefines.inc}
uses
Classes,
{$IFDEF WINDOWS}
Windows,
{$ENDIF}
IdCharsets,
IdBaseComponent,
IdGlobal,
IdException,
SysUtils;
const
LWS = TAB + CHAR32;
// TODO: get rid of these and use the ones in the IdGlobal unit
wdays: array[1..7] of string =
('Sun', 'Mon', 'Tue', 'Wed', 'Thu', 'Fri', 'Sat'); {do not localize}
monthnames: array[1..12] of string =
('Jan', 'Feb', 'Mar', 'Apr', 'May', 'Jun', 'Jul', 'Aug', 'Sep', 'Oct', 'Nov', 'Dec'); {do not localize}
type
//WinCE only has Unicode functions for files.
{$IFDEF WINCE}
TIdFileName = TIdUnicodeString;
PIdFileNameChar = PWideChar;
{$ELSE}
TIdFileName = String;
PIdFileNameChar = PChar;
{$ENDIF}
TIdReadLnFunction = function: string of object;
TStringEvent = procedure(ASender: TComponent; const AString: String);
TIdMimeTable = class(TObject)
protected
FLoadTypesFromOS: Boolean;
FOnBuildCache: TNotifyEvent;
FMIMEList: TStrings;
FFileExt: TStrings;
procedure BuildDefaultCache; virtual;
public
property LoadTypesFromOS: Boolean read FLoadTypesFromOS write FLoadTypesFromOS;
procedure BuildCache; virtual;
procedure AddMimeType(const Ext, MIMEType: string; const ARaiseOnError: Boolean = True);
function GetFileMIMEType(const AFileName: string): string;
function GetDefaultFileExt(const MIMEType: string): string;
procedure LoadFromStrings(const AStrings: TStrings; const MimeSeparator: Char = '='); {Do not Localize}
procedure SaveToStrings(const AStrings: TStrings; const MimeSeparator: Char = '='); {Do not Localize}
constructor Create(const AutoFill: Boolean = True); reintroduce; virtual;
destructor Destroy; override;
//
property OnBuildCache: TNotifyEvent read FOnBuildCache write FOnBuildCache;
end;
TIdInterfacedObject = class (TInterfacedObject)
public
function _AddRef: Integer;
function _Release: Integer;
end;
TIdHeaderQuotingType = (QuotePlain, QuoteRFC822, QuoteMIME, QuoteHTTP);
//
EIdExtensionAlreadyExists = class(EIdException);
// Procs - KEEP THESE ALPHABETICAL!!!!!
// procedure BuildMIMETypeMap(dest: TIdStringList);
// TODO: IdStrings have optimized SplitColumns* functions, can we remove it?
function ABNFToText(const AText : String) : String;
function BinStrToInt(const ABinary: String): Integer;
function BreakApart(BaseString, BreakString: string; StringList: TStrings): TStrings;
function UInt32ToFourChar(AValue : UInt32): string;
function LongWordToFourChar(AValue : UInt32): string; {$IFDEF HAS_DEPRECATED}deprecated{$IFDEF HAS_DEPRECATED_MSG} 'Use UInt32ToFourChar()'{$ENDIF};{$ENDIF}
function CharRange(const AMin, AMax : Char): String;
procedure CommaSeparatedToStringList(AList: TStrings; const Value:string);
function CompareDateTime(const ADateTime1, ADateTime2 : TDateTime) : Integer;
function ContentTypeToEncoding(const AContentType: string; AQuoteType: TIdHeaderQuotingType): IIdTextEncoding;
function CharsetToEncoding(const ACharset: string): IIdTextEncoding;
function ReadStringAsContentType(AStream: TStream; const AContentType: String;
AQuoteType: TIdHeaderQuotingType
{$IFDEF STRING_IS_ANSI}; ADestEncoding: IIdTextEncoding = nil{$ENDIF}): String;
procedure ReadStringsAsContentType(AStream: TStream; AStrings: TStrings;
const AContentType: String; AQuoteType: TIdHeaderQuotingType
{$IFDEF STRING_IS_ANSI}; ADestEncoding: IIdTextEncoding = nil{$ENDIF});
procedure WriteStringAsContentType(AStream: TStream; const AStr, AContentType: String;
AQuoteType: TIdHeaderQuotingType
{$IFDEF STRING_IS_ANSI}; ASrcEncoding: IIdTextEncoding = nil{$ENDIF});
procedure WriteStringsAsContentType(AStream: TStream; const AStrings: TStrings;
const AContentType: String; AQuoteType: TIdHeaderQuotingType
{$IFDEF STRING_IS_ANSI}; ASrcEncoding: IIdTextEncoding = nil{$ENDIF});
procedure WriteStringAsCharset(AStream: TStream; const AStr, ACharset: string
{$IFDEF STRING_IS_ANSI}; ASrcEncoding: IIdTextEncoding = nil{$ENDIF});
procedure WriteStringsAsCharset(AStream: TStream; const AStrings: TStrings;
const ACharset: string
{$IFDEF STRING_IS_ANSI}; ASrcEncoding: IIdTextEncoding = nil{$ENDIF});
function ReadStringAsCharset(AStream: TStream; const ACharset: String
{$IFDEF STRING_IS_ANSI}; ADestEncoding: IIdTextEncoding = nil{$ENDIF}): String;
procedure ReadStringsAsCharset(AStream: TStream; AStrings: TStrings; const ACharset: string
{$IFDEF STRING_IS_ANSI}; ADestEncoding: IIdTextEncoding = nil{$ENDIF});
{
These are for handling binary values that are in Network Byte order. They call
ntohs, ntols, htons, and htons which are required by SNTP and FSP
(probably some other protocols). They aren't aren't in IdGlobals because that
doesn't refer to IdStack so you can't use GStack there.
}
procedure CopyBytesToHostUInt32(const ASource : TIdBytes; const ASourceIndex: Integer; var VDest : UInt32);
procedure CopyBytesToHostUInt16(const ASource : TIdBytes; const ASourceIndex: Integer; var VDest : UInt16);
procedure CopyTIdNetworkUInt32(const ASource: UInt32; var VDest: TIdBytes; const ADestIndex: Integer);
procedure CopyTIdNetworkUInt16(const ASource: UInt16; var VDest: TIdBytes; const ADestIndex: Integer);
procedure CopyBytesToHostLongWord(const ASource : TIdBytes; const ASourceIndex: Integer; var VDest : UInt32); {$IFDEF HAS_DEPRECATED}deprecated{$IFDEF HAS_DEPRECATED_MSG} 'Use CopyBytesToHostUInt32'{$ENDIF};{$ENDIF}
procedure CopyBytesToHostWord(const ASource : TIdBytes; const ASourceIndex: Integer; var VDest : UInt16); {$IFDEF HAS_DEPRECATED}deprecated{$IFDEF HAS_DEPRECATED_MSG} 'Use CopyBytesToHostWord'{$ENDIF};{$ENDIF}
procedure CopyTIdNetworkLongWord(const ASource: UInt32; var VDest: TIdBytes; const ADestIndex: Integer); {$IFDEF HAS_DEPRECATED}deprecated{$IFDEF HAS_DEPRECATED_MSG} 'Use CopyTIdNetworkLongWord'{$ENDIF};{$ENDIF}
procedure CopyTIdNetworkWord(const ASource: UInt16; var VDest: TIdBytes; const ADestIndex: Integer); {$IFDEF HAS_DEPRECATED}deprecated{$IFDEF HAS_DEPRECATED_MSG} 'Use CopyTIdNetworkWord'{$ENDIF};{$ENDIF}
function CopyFileTo(const Source, Destination: TIdFileName): Boolean;
function DomainName(const AHost: String): String;
function EnsureMsgIDBrackets(const AMsgID: String): String;
function ExtractHeaderItem(const AHeaderLine: String): String;
function ExtractHeaderSubItem(const AHeaderLine, ASubItem: String; AQuoteType: TIdHeaderQuotingType): String;
function ReplaceHeaderSubItem(const AHeaderLine, ASubItem, AValue: String; AQuoteType: TIdHeaderQuotingType): String; overload;
function ReplaceHeaderSubItem(const AHeaderLine, ASubItem, AValue: String; var VOld: String; AQuoteType: TIdHeaderQuotingType): String; overload;
function IsHeaderMediaType(const AHeaderLine, AMediaType: String): Boolean;
function IsHeaderMediaTypes(const AHeaderLine: String; const AMediaTypes: array of String): Boolean;
function ExtractHeaderMediaType(const AHeaderLine: String): String;
function ExtractHeaderMediaSubType(const AHeaderLine: String): String;
function IsHeaderValue(const AHeaderLine: String; const AValue: String): Boolean;
function FileSizeByName(const AFilename: TIdFileName): Int64;
{$IFDEF WINDOWS}
function IsVolume(const APathName : TIdFileName) : Boolean;
{$ENDIF}
//MLIST FTP DateTime conversion functions
function FTPMLSToGMTDateTime(const ATimeStamp : String):TDateTime;
function FTPMLSToLocalDateTime(const ATimeStamp : String):TDateTime;
function FTPGMTDateTimeToMLS(const ATimeStamp : TDateTime; const AIncludeMSecs : Boolean=True): String;
function FTPLocalDateTimeToMLS(const ATimeStamp : TDateTime; const AIncludeMSecs : Boolean=True): String;
function GetClockValue : Int64;
function GetMIMETypeFromFile(const AFile: TIdFileName): string;
function GetMIMEDefaultFileExt(const MIMEType: string): TIdFileName;
function GetGMTDateByName(const AFileName : TIdFileName) : TDateTime;
function GmtOffsetStrToDateTime(const S: string): TDateTime;
function GMTToLocalDateTime(S: string): TDateTime;
function CookieStrToLocalDateTime(S: string): TDateTime;
function IdGetDefaultCharSet : TIdCharSet;
function IntToBin(Value: UInt32): string;
function IndyComputerName : String; // DotNet: see comments regarding GDotNetComputerName below
function IndyCurrentYear : Integer;
function IndyStrToBool(const AString: String): Boolean;
function IsDomain(const S: String): Boolean;
function IsFQDN(const S: String): Boolean;
function IsBinary(const AChar : Char) : Boolean;
function IsHex(const AChar : Char) : Boolean;
function IsHostname(const S: String): Boolean;
{$IFDEF STRING_IS_ANSI}
function IsLeadChar(ACh : Char): Boolean;
{$ENDIF}
function IsTopDomain(const AStr: string): Boolean;
function IsValidIP(const S: String): Boolean;
function MakeTempFilename(const APath: TIdFileName = ''): TIdFileName;
function OrdFourByteToUInt32(AByte1, AByte2, AByte3, AByte4 : Byte): UInt32;
function OrdFourByteToLongWord(AByte1, AByte2, AByte3, AByte4 : Byte): UInt32; {$IFDEF HAS_DEPRECATED}deprecated{$IFDEF HAS_DEPRECATED_MSG} 'Use OrdFourByteToUInt32()'{$ENDIF};{$ENDIF}
procedure UInt32ToOrdFourByte(const AValue: UInt32; var VByte1, VByte2, VByte3, VByte4 : Byte);
procedure LongWordToOrdFourByte(const AValue: UInt32; var VByte1, VByte2, VByte3, VByte4 : Byte); {$IFDEF HAS_DEPRECATED}deprecated{$IFDEF HAS_DEPRECATED_MSG} 'Use UInt32ToOrdFourByte()'{$ENDIF};{$ENDIF}
function PadString(const AString : String; const ALen : Integer; const AChar: Char): String;
function UnquotedStr(const AStr : String): String;
function ProcessPath(const ABasePath: String; const APath: String; const APathDelim: string = '/'): string; {Do not Localize}
function RightStr(const AStr: String; const Len: Integer): String;
// still to figure out how to reproduce these under .Net
function ROL(const AVal: UInt32; AShift: Byte): UInt32;
function ROR(const AVal: UInt32; AShift: Byte): UInt32;
function RPos(const ASub, AIn: String; AStart: Integer = -1): Integer;
function IndySetLocalTime(Value: TDateTime): Boolean;
function StartsWith(const ANSIStr, APattern : String) : Boolean;
function StrInternetToDateTime(Value: string): TDateTime;
function StrToDay(const ADay: string): Byte;
function StrToMonth(const AMonth: string): Byte;
function StrToWord(const Value: String): Word;
function TimeZoneBias: TDateTime;
//these are for FSP but may also help with MySQL
function UnixDateTimeToDelphiDateTime(UnixDateTime: UInt32): TDateTime;
function DateTimeToUnix(ADateTime: TDateTime): UInt32;
function TwoCharToUInt16(AChar1, AChar2: Char): Word;
function TwoCharToWord(AChar1, AChar2: Char): Word; {$IFDEF HAS_DEPRECATED}deprecated{$IFDEF HAS_DEPRECATED_MSG} 'Use TwoCharToUInt16()'{$ENDIF};{$ENDIF}
function UpCaseFirst(const AStr: string): string;
function UpCaseFirstWord(const AStr: string): string;
function GetUniqueFileName(const APath, APrefix, AExt : String) : String;
procedure UInt16ToTwoBytes(AWord : Word; ByteArray: TIdBytes; Index: integer);
procedure WordToTwoBytes(AWord : Word; ByteArray: TIdBytes; Index: integer); {$IFDEF HAS_DEPRECATED}deprecated{$IFDEF HAS_DEPRECATED_MSG} 'Use UInt16ToTwoBytes()'{$ENDIF};{$ENDIF}
function UInt16ToStr(const Value: Word): String;
function WordToStr(const Value: Word): String; {$IFDEF HAS_DEPRECATED}deprecated{$IFDEF HAS_DEPRECATED_MSG} 'Use UInt16ToStr()'{$ENDIF};{$ENDIF}
//moved here so I can IFDEF a DotNET ver. that uses StringBuilder
function IndyWrapText(const ALine, ABreakStr, ABreakChars : string; MaxCol: Integer): string;
//The following is for working on email headers and message part headers...
function RemoveHeaderEntry(const AHeader, AEntry: string; AQuoteType: TIdHeaderQuotingType): string; overload;
function RemoveHeaderEntry(const AHeader, AEntry: string; var VOld: String; AQuoteType: TIdHeaderQuotingType): string; overload;
function RemoveHeaderEntries(const AHeader: string; AEntries: array of string; AQuoteType: TIdHeaderQuotingType): string;
{
Three functions for easier manipulating of strings. Don't know of any
system functions to perform these actions. If there aren't and someone
can find an optimised way of performing then please implement...
}
function FindFirstOf(const AFind, AText: string; const ALength: Integer = -1; const AStartPos: Integer = 1): Integer;
function FindFirstNotOf(const AFind, AText: string; const ALength: Integer = -1; const AStartPos: Integer = 1): Integer;
function TrimAllOf(const ATrim, AText: string): string;
procedure ParseMetaHTTPEquiv(AStream: TStream; AHeaders : TStrings; var VCharSet: string);
type
TIdEncodingNeededEvent = function(const ACharset: String): IIdTextEncoding;
var
{$IFDEF UNIX}
// For linux the user needs to set these variables to be accurate where used (mail, etc)
GIdDefaultCharSet : TIdCharSet = idcs_ISO_8859_1; // idcsISO_8859_1;
{$ENDIF}
GIdEncodingNeeded: TIdEncodingNeededEvent = nil;
IndyFalseBoolStrs : array of String;
IndyTrueBoolStrs : array of String;
//This is from: http://www.swissdelphicenter.ch/en/showcode.php?id=844
const
// Sets UnixStartDate to TIdDateTime of 01/01/1970
UNIXSTARTDATE : TDateTime = 25569.0;
{This indicates that the default date is Jan 1, 1900 which was specified
by RFC 868.}
TIME_BASEDATE = 2;
//These are moved here to facilitate inlining
const
HexNumbers = '01234567890ABCDEF'; {Do not Localize}
BinNumbers = '01'; {Do not localize}
implementation
uses
{$IFDEF USE_VCL_POSIX}
{$IFDEF DARWIN}
Macapi.CoreServices,
{$ENDIF}
{$ENDIF}
IdIPAddress,
{$IFDEF UNIX}
{$IFDEF KYLIXCOMPAT}
Libc,
{$ENDIF}
{$IFDEF FPC}
{$IFDEF USE_BASEUNIX}
BaseUnix,
Unix,
DateUtils,
{$ENDIF}
{$ENDIF}
{$IFDEF USE_VCL_POSIX}
DateUtils,
Posix.SysStat, Posix.SysTime, Posix.Time, Posix.Unistd,
{$ENDIF}
{$ENDIF}
{$IFDEF WINDOWS}
Messages,
Registry,
{$ENDIF}
{$IFDEF DOTNET}
System.IO,
System.Text,
{$ENDIF}
IdAssignedNumbers,
IdResourceStringsCore,
IdResourceStringsProtocols,
IdStack
{$IFDEF USE_OBJECT_ARC}
{$IFDEF HAS_UNIT_Generics_Collections}
, System.Generics.Collections
{$ENDIF}
{$ENDIF}
;
//
function UnquotedStr(const AStr : String): String;
begin
Result := AStr;
if TextStartsWith(Result, '"') then begin
IdDelete(Result, 1, 1);
Result := Fetch(Result, '"');
end;
end;
{This is taken from Borland's SysUtils and modified for our folding} {Do not Localize}
function IndyWrapText(const ALine, ABreakStr, ABreakChars : string; MaxCol: Integer): string;
const
QuoteChars = '"'; {Do not Localize}
var
LCol, LPos: Integer;
LLinePos, LLineLen: Integer;
LBreakLen, LBreakPos: Integer;
LQuoteChar, LCurChar: Char;
LExistingBreak: Boolean;
begin
LCol := 1;
LPos := 1;
LLinePos := 1;
LBreakPos := 0;
LQuoteChar := ' '; {Do not Localize}
LExistingBreak := False;
LLineLen := Length(ALine);
LBreakLen := Length(ABreakStr);
Result := ''; {Do not Localize}
while LPos <= LLineLen do begin
LCurChar := ALine[LPos];
{$IFDEF STRING_IS_ANSI}
if IsLeadChar(LCurChar) then begin
Inc(LPos);
Inc(LCol);
end else begin //if CurChar in LeadBytes then
{$ENDIF}
if LCurChar = ABreakStr[1] then begin
if LQuoteChar = ' ' then begin {Do not Localize}
LExistingBreak := TextIsSame(ABreakStr, Copy(ALine, LPos, LBreakLen));
if LExistingBreak then begin
Inc(LPos, LBreakLen-1);
LBreakPos := LPos;
end; //if ExistingBreak then
end // if QuoteChar = ' ' then {Do not Localize}
end else begin// if CurChar = BreakStr[1] then
if CharIsInSet(LCurChar, 1, ABreakChars) then begin
if LQuoteChar = ' ' then begin {Do not Localize}
LBreakPos := LPos;
end;
end else begin // if CurChar in BreakChars then
if CharIsInSet(LCurChar, 1, QuoteChars) then begin
if LCurChar = LQuoteChar then begin
LQuoteChar := ' '; {Do not Localize}
end else begin
if LQuoteChar = ' ' then begin {Do not Localize}
LQuoteChar := LCurChar;
end;
end;
end;
end;
end;
{$IFDEF STRING_IS_ANSI}
end;
{$ENDIF}
Inc(LPos);
Inc(LCol);
if not (CharIsInSet(LQuoteChar, 1, QuoteChars)) and
(LExistingBreak or
((LCol > MaxCol) and (LBreakPos > LLinePos))) then begin
LCol := LPos - LBreakPos;
Result := Result + Copy(ALine, LLinePos, LBreakPos - LLinePos + 1);
if not (CharIsInSet(LCurChar, 1, QuoteChars)) then begin
while (LPos <= LLineLen) and (CharIsInSet(ALine, LPos, ABreakChars + #13+#10)) do begin
Inc(LPos);
end;
if not LExistingBreak and (LPos < LLineLen) then begin
Result := Result + ABreakStr;
end;
end;
Inc(LBreakPos);
LLinePos := LBreakPos;
LExistingBreak := False;
end; //if not
end; //while Pos <= LineLen do
Result := Result + Copy(ALine, LLinePos, MaxInt);
end;
function IndyCurrentYear : Integer;
{$IFDEF HAS_CurrentYear}
{$IFDEF USE_INLINE} inline; {$ENDIF}
{$ELSE}
var
LYear, LMonth, LDay : Word;
{$ENDIF}
begin
{$IFDEF HAS_CurrentYear}
Result := CurrentYear;
{$ELSE}
DecodeDate(Now, LYear, LMonth, LDay);
Result := LYear;
{$ENDIF}
end;
function CharRange(const AMin, AMax : Char): String;
var
i : Char;
{$IFDEF STRING_IS_IMMUTABLE}
LSB : TIdStringBuilder;
{$ENDIF}
begin
{$IFDEF STRING_IS_IMMUTABLE}
LSB := TIdStringBuilder.Create(Ord(AMax) - Ord(AMin) + 1);
for i := AMin to AMax do begin
LSB.Append(i);
end;
Result := LSB.ToString;
{$ELSE}
SetLength(Result, Ord(AMax) - Ord(AMin) + 1);
for i := AMin to AMax do begin
Result[Ord(i) - Ord(AMin) + 1] := i;
end;
{$ENDIF}
end;
{$IFDEF WINDOWS}
var
ATempPath: TIdFileName;
{$ENDIF}
function StartsWith(const ANSIStr, APattern : String) : Boolean;
{$IFDEF USE_INLINE} inline; {$ENDIF}
begin
Result := TextStartsWith(ANSIStr, APattern) {do not localize}
//tentative fix for a problem with Korean indicated by "SungDong Kim" <infi@acrosoft.pe.kr>
{$IFNDEF DOTNET}
//note that in DotNET, everything is MBCS
and (ByteType(ANSIStr, 1) = mbSingleByte)
{$ENDIF} ;
//just in case someone is doing a recursive listing and there's a dir with the name total
end;
function UnixDateTimeToDelphiDateTime(UnixDateTime: UInt32): TDateTime;
{$IFDEF USE_INLINE} inline; {$ENDIF}
begin
Result := (UnixDateTime / 86400) + UnixStartDate;
{
From: http://homepages.borland.com/efg2lab/Library/UseNet/1999/0309b.txt
}
// Result := EncodeDate(1970, 1, 1) + (UnixDateTime / 86400); {86400=No. of secs. per day}
end;
function DateTimeToUnix(ADateTime: TDateTime): UInt32;
{$IFDEF USE_INLINE} inline; {$ENDIF}
begin
//example: DateTimeToUnix(now);
Result := Round((ADateTime - UnixStartDate) * 86400);
end;
{$I IdDeprecatedImplBugOff.inc}
procedure CopyBytesToHostWord(const ASource : TIdBytes; const ASourceIndex: Integer; var VDest : UInt16);
{$I IdDeprecatedImplBugOn.inc}
{$IFDEF USE_INLINE} inline; {$ENDIF}
begin
CopyBytesToHostUInt16(ASource, ASourceIndex, VDest);
end;
procedure CopyBytesToHostUInt16(const ASource : TIdBytes; const ASourceIndex: Integer; var VDest : UInt16);
{$IFDEF USE_INLINE} inline; {$ENDIF}
begin
VDest := BytesToUInt16(ASource, ASourceIndex);
VDest := GStack.NetworkToHost(VDest);
end;
{$I IdDeprecatedImplBugOff.inc}
procedure CopyBytesToHostLongWord(const ASource : TIdBytes; const ASourceIndex: Integer; var VDest : UInt32);
{$I IdDeprecatedImplBugOn.inc}
{$IFDEF USE_INLINE} inline; {$ENDIF}
begin
CopyBytesToHostUInt32(ASource, ASourceIndex, VDest);
end;
procedure CopyBytesToHostUInt32(const ASource : TIdBytes; const ASourceIndex: Integer; var VDest : UInt32);
{$IFDEF USE_INLINE} inline; {$ENDIF}
begin
VDest := BytesToUInt32(ASource, ASourceIndex);
VDest := GStack.NetworkToHost(VDest);
end;
{$I IdDeprecatedImplBugOff.inc}
procedure CopyTIdNetworkWord(const ASource: UInt16; var VDest: TIdBytes; const ADestIndex: Integer);
{$I IdDeprecatedImplBugOn.inc}
{$IFDEF USE_INLINE} inline; {$ENDIF}
begin
CopyTIdNetworkUInt16(ASource, VDest, ADestIndex);
end;
procedure CopyTIdNetworkUInt16(const ASource: UInt16; var VDest: TIdBytes; const ADestIndex: Integer);
{$IFDEF USE_INLINE} inline; {$ENDIF}
begin
CopyTIdUInt16(GStack.HostToNetwork(ASource),VDest,ADestIndex);
end;
{$I IdDeprecatedImplBugOff.inc}
procedure CopyTIdNetworkLongWord(const ASource: UInt32; var VDest: TIdBytes; const ADestIndex: Integer);
{$I IdDeprecatedImplBugOn.inc}
{$IFDEF USE_INLINE} inline; {$ENDIF}
begin
CopyTIdNetworkUInt32(ASource, VDest, ADestIndex);
end;
procedure CopyTIdNetworkUInt32(const ASource: UInt32; var VDest: TIdBytes; const ADestIndex: Integer);
{$IFDEF USE_INLINE} inline; {$ENDIF}
begin
CopyTIdUInt32(GStack.HostToNetwork(ASource),VDest,ADestIndex);
end;
function UInt32ToFourChar(AValue : UInt32): string;
{$IFDEF USE_INLINE} inline; {$ENDIF}
begin
Result := BytesToStringRaw(ToBytes(AValue));
end;
{$I IdDeprecatedImplBugOff.inc}
function LongWordToFourChar(AValue : UInt32): string;
{$I IdDeprecatedImplBugOn.inc}
{$IFDEF USE_INLINE} inline; {$ENDIF}
begin
Result := UInt32ToFourChar(AValue);
end;
procedure UInt16ToTwoBytes(AWord : Word; ByteArray: TIdBytes; Index: integer);
{$IFDEF USE_INLINE} inline; {$ENDIF}
begin
//ByteArray[Index] := AWord div 256;
//ByteArray[Index + 1] := AWord mod 256;
ByteArray[Index + 1] := AWord div 256;
ByteArray[Index] := AWord mod 256;
end;
{$I IdDeprecatedImplBugOff.inc}
procedure WordToTwoBytes(AWord : Word; ByteArray: TIdBytes; Index: integer);
{$I IdDeprecatedImplBugOn.inc}
{$IFDEF USE_INLINE}inline;{$ENDIF}
begin
UInt16ToTwoBytes(AWord, ByteArray, Index);
end;
function StrToWord(const Value: String): Word;
{$IFDEF USE_INLINE} inline; {$ENDIF}
begin
if Length(Value) > 1 then begin
{$IFDEF STRING_IS_UNICODE}
Result := TwoCharToUInt16(Value[1], Value[2]);
{$ELSE}
Result := PWord(Pointer(Value))^;
{$ENDIF}
end else begin
Result := 0;
end;
end;
function UInt16ToStr(const Value: Word): String;
{$IFDEF USE_INLINE} inline; {$ENDIF}
begin
{$IFDEF STRING_IS_UNICODE}
Result := BytesToStringRaw(ToBytes(Value));
{$ELSE}
SetLength(Result, SizeOf(Value));
Move(Value, Result[1], SizeOf(Value));
{$ENDIF}
end;
{$I IdDeprecatedImplBugOff.inc}
function WordToStr(const Value: Word): String;
{$I IdDeprecatedImplBugOn.inc}
{$IFDEF USE_INLINE} inline; {$ENDIF}
begin
Result := UInt16ToStr(Value);
end;
function OrdFourByteToUInt32(AByte1, AByte2, AByte3, AByte4 : Byte): UInt32;
{$IFDEF USE_INLINE} inline; {$ENDIF}
var
LValue: TIdBytes;
begin
SetLength(LValue, SizeOf(UInt32));
LValue[0] := AByte1;
LValue[1] := AByte2;
LValue[2] := AByte3;
LValue[3] := AByte4;
Result := BytesToUInt32(LValue);
end;
{$I IdDeprecatedImplBugOff.inc}
function OrdFourByteToLongWord(AByte1, AByte2, AByte3, AByte4 : Byte): UInt32;
{$I IdDeprecatedImplBugOn.inc}
{$IFDEF USE_INLINE} inline; {$ENDIF}
begin
Result := OrdFourByteToUInt32(AByte1, AByte2, AByte3, AByte4);
end;
procedure UInt32ToOrdFourByte(const AValue: UInt32; var VByte1, VByte2, VByte3, VByte4 : Byte);
{$IFDEF USE_INLINE} inline; {$ENDIF}
var
LValue: TIdBytes;
begin
LValue := ToBytes(AValue);
VByte1 := LValue[0];
VByte2 := LValue[1];
VByte3 := LValue[2];
VByte4 := LValue[3];
end;
{$I IdDeprecatedImplBugOff.inc}
procedure LongWordToOrdFourByte(const AValue: UInt32; var VByte1, VByte2, VByte3, VByte4 : Byte);
{$I IdDeprecatedImplBugOn.inc}
{$IFDEF USE_INLINE} inline; {$ENDIF}
begin
UInt32ToOrdFourByte(AValue, VByte1, VByte2, VByte3, VByte4);
end;
function TwoCharToUInt16(AChar1, AChar2: Char): UInt16;
//Since Replys are returned as Strings, we need a rountime to convert two
// characters which are a 2 byte U Int into a two byte unsigned integer
var
LWord: TIdBytes;
begin
SetLength(LWord, SizeOf(UInt16));
LWord[0] := Ord(AChar1);
LWord[1] := Ord(AChar2);
Result := BytesToUInt16(LWord);
// Result := Word((Ord(AChar1) shl 8) and $FF00) or Word(Ord(AChar2) and $00FF);
end;
{$I IdDeprecatedImplBugOff.inc}
function TwoCharToWord(AChar1, AChar2: Char): Word;
{$I IdDeprecatedImplBugOn.inc}
{$IFDEF USE_INLINE}inline;{$ENDIF}
begin
Result := TwoCharToUInt16(AChar1, AChar2);
end;
function CompareDateTime(const ADateTime1, ADateTime2 : TDateTime) : Integer;
var
LYear1, LYear2 : Word;
LMonth1, LMonth2 : Word;
LDay1, LDay2 : Word;
LHour1, LHour2 : Word;
LMin1, LMin2 : Word;
LSec1, LSec2 : Word;
LMSec1, LMSec2 : Word;
{
The return value is less than 0 if ADateTime1 is less than ADateTime2,
0 if ADateTime1 equals ADateTime2, or
greater than 0 if ADateTime1 is greater than ADateTime2.
}
begin
DecodeDate(ADateTime1, LYear1, LMonth1, LDay1);
DecodeDate(ADateTime2, LYear2, LMonth2, LDay2);
// year
Result := LYear1 - LYear2;
if Result <> 0 then begin
Exit;
end;
// month
Result := LMonth1 - LMonth2;
if Result <> 0 then begin
Exit;
end;
// day
Result := LDay1 - LDay2;
if Result <> 0 then begin
Exit;
end;
DecodeTime(ADateTime1, LHour1, LMin1, LSec1, LMSec1);
DecodeTime(ADateTime2, LHour2, LMin2, LSec2, LMSec2);
//hour
Result := LHour1 - LHour2;
if Result <> 0 then begin
Exit;
end;
//minute
Result := LMin1 - LMin2;
if Result <> 0 then begin
Exit;
end;
//second
Result := LSec1 - LSec2;
if Result <> 0 then begin
Exit;
end;
//millasecond
Result := LMSec1 - LMSec2;
end;
{This is an internal procedure so the StrInternetToDateTime and GMTToLocalDateTime can share common code}
function RawStrInternetToDateTime(var Value: string; var VDateTime: TDateTime): Boolean;
var
i: Integer;
Dt, Mo, Yr, Ho, Min, Sec, MSec: Word;
sYear, sTime, sDelim: string;
//flags for if AM/PM marker found
LAM, LPM : Boolean;
procedure ParseDayOfMonth;
begin
Dt := IndyStrToInt( Fetch(Value, sDelim), 1);
Value := TrimLeft(Value);
end;
procedure ParseMonth;
begin
Mo := StrToMonth( Fetch (Value, sDelim) );
Value := TrimLeft(Value);
end;
function ParseISO8601: Boolean;
var
S: String;
Len, Offset, Found: Integer;
begin
Result := False;
// TODO: implement logic from IdVCard.ParseISO8601DateAndOrTime() here and then remove that function
{
var
LDate: TIdISO8601DateComps;
LTime: TIdISO8601TimeComps;
begin
Result := ParseISO8601DateAndOrTime(Value, LDate, LTime);
if Result then begin
VDateTime := EncodeDate(LDate.Year, LDate.Month, LDate.Day) + EncodeTime(LTime.Hour, LTime.Min, LTime.Sec, LTime.MSec);
Value := LTime.UTFOffset;
end;
end;
}
S := Value;
Len := Length(S);
if not IsNumeric(S, 4) then begin
Exit;
end;
// defaults for omitted values
Dt := 1;
Mo := 1;
Ho := 0;
Min := 0;
Sec := 0;
MSec := 0;
Yr := IndyStrToInt( Copy(S, 1, 4) );
Offset := 5;
if Offset <= Len then
begin
if (not CharEquals(S, Offset, '-')) or (not IsNumeric(S, 2, Offset+1)) then begin
Exit;
end;
Mo := IndyStrToInt( Copy(S, Offset+1, 2) );
Inc(Offset, 3);
if Offset <= Len then
begin
if (not CharEquals(S, Offset, '-')) or {Do not Localize}
(not IsNumeric(S, 2, Offset+1)) then
begin
Exit;
end;
Dt := IndyStrToInt( Copy(S, Offset+1, 2) );
Inc(Offset, 3);
if Offset <= Len then
begin
if (not CharEquals(S, Offset, 'T')) or {Do not Localize}
(not IsNumeric(S, 2, Offset+1)) or
(not CharEquals(S, Offset+3, ':')) then {Do not Localize}
begin
Exit;
end;
Ho := IndyStrToInt( Copy(S, Offset+1, 2) );
Inc(Offset, 4);
if not IsNumeric(S, 2, Offset) then begin
Exit;
end;
Min := IndyStrToInt( Copy(S, Offset, 2) );
Inc(Offset, 2);
if Offset > Len then begin
Exit;
end;
if CharEquals(S, Offset, ':') then {Do not Localize}
begin
if not IsNumeric(S, 2, Offset+1) then begin
Exit;
end;
Sec := IndyStrToInt( Copy(S, Offset+1, 2) );
Inc(Offset, 3);
if Offset > Len then begin
Exit;
end;
if CharEquals(S, Offset, '.') then {Do not Localize}
begin
Found := FindFirstNotOf('0123456789', S, -1, Offset+1); {Do not Localize}
if Found = 0 then begin
Exit;
end;
MSec := IndyStrToInt( Copy(S, Offset+1, Found-Offset-1) );
Inc(Offset, Found-Offset+1);
end;
end;
end;
end;
end;
VDateTime := EncodeDate(Yr, Mo, Dt) + EncodeTime(Ho, Min, Sec, MSec);
Value := Copy(S, Offset, MaxInt);
Result := True;
end;
begin
Result := False;
VDateTime := 0.0;
Value := Trim(Value);
if Length(Value) = 0 then begin
Exit;
end;
try
// RLebeau: have noticed some HTTP servers deliver dates using ISO-8601
// format even though this is in violation of the HTTP specs!
if ParseISO8601 then begin
Result := True;
Exit;
end;
{Day of Week}
if StrToDay(Copy(Value, 1, 3)) > 0 then begin
//workaround in case a space is missing after the initial column
if CharEquals(Value, 4, ',') and (not CharEquals(Value, 5, ' ')) then begin
Insert(' ', Value, 5);
end;
Fetch(Value);
Value := TrimLeft(Value);
end;
// Workaround for some buggy web servers which use '-' to separate the date parts. {Do not Localize}
i := IndyPos('-', Value); {Do not Localize}
if (i > 1) and (i < IndyPos(' ', Value)) then begin {Do not Localize}
sDelim := '-'; {Do not Localize}
end else begin
sDelim := ' '; {Do not Localize}
end;
//workaround for improper dates such as 'Fri, Sep 7 2001' {Do not Localize}
//RFC 2822 states that they should be like 'Fri, 7 Sep 2001' {Do not Localize}
if StrToMonth(Fetch(Value, sDelim, False)) > 0 then begin
{Month}
ParseMonth;
{Day of Month}
ParseDayOfMonth;
end else begin
{Day of Month}
ParseDayOfMonth;
{Month}
ParseMonth;
end;
{Year}
// There is some strange date/time formats like
// DayOfWeek Month DayOfMonth Time Year
sYear := Fetch(Value);
Yr := IndyStrToInt(sYear, High(Word));
if Yr = High(Word) then begin // Is sTime valid Integer?
sTime := sYear;
sYear := Fetch(Value);
Value := TrimRight(sTime + ' ' + Value);
Yr := IndyStrToInt(sYear);
end;
// RLebeau: According to RFC 2822, Section 4.3:
//
// "Where a two or three digit year occurs in a date, the year is to be
// interpreted as follows: If a two digit year is encountered whose
// value is between 00 and 49, the year is interpreted by adding 2000,
// ending up with a value between 2000 and 2049. If a two digit year is
// encountered with a value between 50 and 99, or any three digit year
// is encountered, the year is interpreted by adding 1900."
if Length(sYear) = 2 then begin
if {(Yr >= 0) and} (Yr <= 49) then begin
Inc(Yr, 2000);
end
else if (Yr >= 50) and (Yr <= 99) then begin
Inc(Yr, 1900);
end;
end
else if Length(sYear) = 3 then begin
Inc(Yr, 1900);
end;
VDateTime := EncodeDate(Yr, Mo, Dt);
// SG 26/9/00: Changed so that ANY time format is accepted
if IndyPos('AM', Value) > 0 then begin{do not localize}
LAM := True;
LPM := False;
Value := Fetch(Value, 'AM'); {do not localize}
end
else if IndyPos('PM', Value) > 0 then begin {do not localize}
LAM := False;
LPM := True;
Value := Fetch(Value, 'PM'); {do not localize}
end else begin
LAM := False;
LPM := False;
end;
// RLebeau 03/04/2009: some countries use dot instead of colon
// for the time separator
i := IndyPos('.', Value); {do not localize}
if (i > 0) and (i < IndyPos(' ', Value)) then begin {do not localize}
sDelim := '.'; {do not localize}
end else begin
sDelim := ':'; {do not localize}
end;
i := IndyPos(sDelim, Value);
if i > 0 then begin
// Copy time string up until next space (before GMT offset)
sTime := Fetch(Value, ' '); {do not localize}
{Hour}
Ho := IndyStrToInt( Fetch(sTime, sDelim), 0);
{Minute}
Min := IndyStrToInt( Fetch(sTime, sDelim), 0);
{Second}
Sec := IndyStrToInt( Fetch(sTime), 0);
MSec := 0; // TODO
{AM/PM part if present}
Value := TrimLeft(Value);
if LAM then begin
if Ho = 12 then begin
Ho := 0;
end;
end
else if LPM then begin
//in the 12 hour format, afternoon is 12:00PM followed by 1:00PM
//while midnight is written as 12:00 AM
//Not exactly technically correct but pretty accurate
if Ho < 12 then begin
Inc(Ho, 12);
end;
end;
{The date and time stamp returned}
VDateTime := VDateTime + EncodeTime(Ho, Min, Sec, MSec);
end;
Value := TrimLeft(Value);
Result := True;
except
VDateTime := 0.0;
Result := False;
end;
end;
{This should never be localized}
function StrInternetToDateTime(Value: string): TDateTime;
begin
RawStrInternetToDateTime(Value, Result);
end;
function FTPMLSToGMTDateTime(const ATimeStamp : String):TDateTime;
var
LYear, LMonth, LDay, LHour, LMin, LSec, LMSec : Integer;
LBuffer : String;
begin
Result := 0;
LBuffer := ATimeStamp;
if LBuffer <> '' then begin
// 1234 56 78 90 12 34
// ---------- ---------
// 1998 11 07 08 52 15
LYear := IndyStrToInt( Copy( LBuffer,1,4),0);
LMonth := IndyStrToInt(Copy(LBuffer,5,2),0);
LDay := IndyStrToInt(Copy(LBuffer,7,2),0);
LHour := IndyStrToInt(Copy(LBuffer,9,2),0);
LMin := IndyStrToInt(Copy(LBuffer,11,2),0);
LSec := IndyStrToInt(Copy(LBuffer,13,2),0);
Fetch(LBuffer,'.');
LMSec := IndyStrToInt(LBuffer,0);
Result := EncodeDate(LYear,LMonth,LDay);
Result := Result + EncodeTime(LHour,LMin,LSec,LMSec);
end;
end;
function FTPMLSToLocalDateTime(const ATimeStamp : String):TDateTime;
{$IFDEF USE_INLINE} inline; {$ENDIF}
begin
Result := 0.0;
if ATimeStamp <> '' then begin
Result := FTPMLSToGMTDateTime(ATimeStamp);
// Apply local offset
Result := Result + OffsetFromUTC;
end;
end;
function FTPGMTDateTimeToMLS(const ATimeStamp : TDateTime; const AIncludeMSecs : Boolean=True): String;
var
LYear, LMonth, LDay,
LHour, LMin, LSec, LMSec : Word;
begin
DecodeDate(ATimeStamp,LYear,LMonth,LDay);
DecodeTime(ATimeStamp,LHour,LMin,LSec,LMSec);
Result := IndyFormat('%4d%2d%2d%2d%2d%2d',[LYear,LMonth,LDay,LHour,LMin,LSec]);
if AIncludeMSecs then begin
if (LMSec <> 0) then begin
Result := Result + IndyFormat('.%3d',[LMSec]);
end;
end;
Result := ReplaceAll(Result, ' ', '0');
end;
{
Note that MS-DOS displays the time in the Local Time Zone - MLISx commands use
stamps based on GMT)
}
function FTPLocalDateTimeToMLS(const ATimeStamp : TDateTime; const AIncludeMSecs : Boolean=True): String;
{$IFDEF USE_INLINE} inline; {$ENDIF}
begin
Result := FTPGMTDateTimeToMLS(ATimeStamp - OffsetFromUTC, AIncludeMSecs);
end;
function BreakApart(BaseString, BreakString: string; StringList: TStrings): TStrings;
var
EndOfCurrentString: integer;
begin
repeat
EndOfCurrentString := Pos(BreakString, BaseString);
if EndOfCurrentString = 0 then begin
StringList.Add(BaseString);
Break;
end;
StringList.Add(Copy(BaseString, 1, EndOfCurrentString - 1));
Delete(BaseString, 1, EndOfCurrentString + Length(BreakString) - 1); //Copy(BaseString, EndOfCurrentString + length(BreakString), length(BaseString) - EndOfCurrentString);
until False;
Result := StringList;
end;
procedure CommaSeparatedToStringList(AList: TStrings; const Value: string);
var
iStart,
iEnd,
iQuote,
iPos,
iLength : integer ;
sTemp : string ;
begin
iQuote := 0;
iPos := 1 ;
iLength := Length(Value);
AList.Clear ;
while iPos <= iLength do begin
iStart := iPos ;
iEnd := iStart ;
while iPos <= iLength do begin
if Value[iPos] = '"' then begin {do not localize}
Inc(iQuote);
end;
if Value[iPos] = ',' then begin {do not localize}
if iQuote <> 1 then begin
Break;
end;
end;
Inc(iEnd);
Inc(iPos);
end ;
sTemp := Trim(Copy(Value, iStart, iEnd - iStart));
if Length(sTemp) > 0 then begin
AList.Add(sTemp);
end;
iPos := iEnd + 1 ;
iQuote := 0 ;
end ;
end;
{$UNDEF NATIVEFILEAPI}
{$UNDEF NATIVECOPYAPI}
{$IFDEF DOTNET}
{$DEFINE NATIVEFILEAPI}
{$DEFINE NATIVECOPYAPI}
{$ENDIF}
{$IFDEF WINDOWS}
{$DEFINE NATIVEFILEAPI}
{$DEFINE NATIVECOPYAPI}
{$ENDIF}
{$IFDEF UNIX}
{$DEFINE NATIVEFILEAPI}
{$ENDIF}
function CopyFileTo(const Source, Destination: TIdFileName): Boolean;
{$IFDEF NATIVECOPYAPI}
{$IFDEF USE_INLINE}inline;{$ENDIF}
{$IFDEF WIN32_OR_WIN64}
var
LOldErrorMode : Integer;
{$ENDIF}
{$ELSE}
var
SourceF, DestF : File;
NumRead, NumWritten: Integer;
Buffer: array[1..2048] of Byte;
{$ENDIF}
begin
{$IFDEF DOTNET}
try
System.IO.File.Copy(Source, Destination, True);
Result := True; // or you'll get an exception
except
Result := False;
end;
{$ENDIF}
{$IFDEF WINDOWS}
{$IFDEF WIN32_OR_WIN64}
LOldErrorMode := SetErrorMode(SEM_FAILCRITICALERRORS);
try
{$ENDIF}
Result := CopyFile(PIdFileNameChar(Source), PIdFileNameChar(Destination), False);
{$IFDEF WIN32_OR_WIN64}
finally
SetErrorMode(LOldErrorMode);
end;
{$ENDIF}
{$ENDIF}
{$IFNDEF NATIVECOPYAPI}
//mostly from http://delphi.about.com/od/fileio/a/untypedfiles.htm
//note that I do use the I+ and I- directive.
// decided not to use streams because some may not handle more than
// 2GB'sand it would run counter to the intent of this, return false
//on failure.
//This is intended to be generic because it may run in many different
//Operating systems
// -TODO: Change to use a Linux copy function
// There is no native Linux copy function (at least "cp" doesn't use one
// and I can't find one anywhere (Johannes Berg))
{$IFOPT I+} // detect IO checking
{$DEFINE _IPlusWasEnabled}
{$I-}
{$ENDIF}
Assign(SourceF, Source);
Reset(SourceF, 1);
Result := IOResult = 0;
if not Result then begin
Exit;
end;
Assign(DestF, Destination);
Rewrite(DestF, 1);
Result := IOResult = 0;
if Result then begin
repeat
BlockRead(SourceF, Buffer, SizeOf(Buffer), NumRead);
Result := IOResult = 0;
if (not Result) or (NumRead = 0) then begin
Break;
end;
BlockWrite(DestF, Buffer, NumRead, NumWritten);
Result := (IOResult = 0) and (NumWritten = NumRead);
until not Result;
Close(DestF);
end;
Close(SourceF);
// Restore IO checking
{$IFDEF _IPlusWasEnabled} // detect previous setting
{$UNDEF _IPlusWasEnabled}
{$I+}
{$ENDIF}
{$ENDIF}
end;
{$IFDEF WINDOWS}
function TempPath: TIdFileName;
var
i: Integer;
begin
SetLength(Result, MAX_PATH);
i := GetTempPath(MAX_PATH, PIdFileNameChar(Result));
if i > 0 then begin
SetLength(Result, i);
Result := IndyIncludeTrailingPathDelimiter(Result);
end else begin
Result := '';
end;
end;
{$ENDIF}
function MakeTempFilename(const APath: TIdFileName = ''): TIdFileName;
var
lPath: TIdFileName;
lExt: TIdFileName;
begin
lPath := APath;
{$IFDEF UNIX}
lExt := '';
{$ELSE}
lExt := '.tmp';
{$ENDIF}
{$IFDEF WINDOWS}
if lPath = '' then begin
lPath := ATempPath;
end;
{$ELSE}
{$IFDEF DOTNET}
if lPath = '' then begin
lPath := System.IO.Path.GetTempPath;
end;
{$ENDIF}
{$ENDIF}
Result := GetUniqueFilename(lPath, 'Indy', lExt);
end;
function GetUniqueFileName(const APath, APrefix, AExt : String) : String;
var
{$IFDEF FPC}
LPrefix: string;
{$ELSE}
LNamePart : TIdTicks;
LFQE : String;
LFName: String;
{$ENDIF}
begin
{$IFDEF FPC}
//Do not use Tempnam in Unix-like Operating systems. That function is dangerous
//and you will be warned about it when compiling. FreePascal has GetTempFileName. Use
//that instead.
LPrefix := APrefix;
if LPrefix = '' then begin
LPrefix := 'Indy'; {Do not localize}
end;
Result := GetTempFileName(APath, LPrefix);
{$ELSE}
// TODO: Use Winapi.GetTempFileName() in Windows...
LFQE := AExt;
// period is optional in the extension... force it
if LFQE <> '' then begin
if LFQE[1] <> '.' then begin
LFQE := '.' + LFQE;
end;
end;
// validate path and add path delimiter before file name prefix
if APath <> '' then begin
if not IndyDirectoryExists(APath) then begin
// TODO: fail with an error instead...
LFName := APrefix;
end else begin
// uses the Indy function... not the Borland one
LFName := IndyIncludeTrailingPathDelimiter(APath) + APrefix;
end;
end else begin
// TODO: without a starting path, we cannot check for file existance, so fail...
LFName := APrefix;
end;
LNamePart := Ticks64;
repeat
Result := LFName + IntToHex(LNamePart, 8) + LFQE;
if not FileExists(Result) then begin
Break;
end;
Inc(LNamePart);
until False;
{$ENDIF}
end;
// Find a token given a direction (>= 0 from start; < 0 from end)
// S.G. 19/4/00:
// Changed to be more readable
function RPos(const ASub, AIn: String; AStart: Integer = -1): Integer;
var
i: Integer;
LStartPos: Integer;
LTokenLen: Integer;
begin
Result := 0;
LTokenLen := Length(ASub);
// Get starting position
if AStart < 0 then begin
AStart := Length(AIn);
end;
if AStart < (Length(AIn) - LTokenLen + 1) then begin
LStartPos := AStart;
end else begin
LStartPos := (Length(AIn) - LTokenLen + 1);
end;
// Search for the string
for i := LStartPos downto 1 do begin
if TextIsSame(Copy(AIn, i, LTokenLen), ASub) then begin
Result := i;
Break;
end;
end;
end;
{$IFDEF WINDOWS}
function IsVolume(const APathName : TIdFileName) : Boolean;
{$IFDEF USE_INLINE}inline;{$ENDIF}
begin
Result := TextEndsWith(APathName, ':') or TextEndsWith(APathName, ':\');
end;
{$ENDIF}
// OS-independant version
function FileSizeByName(const AFilename: TIdFileName): Int64;
//Leave in for HTTP Server
{$IFDEF DOTNET}
var
LFile : System.IO.FileInfo;
{$ELSE}
{$IFDEF USE_INLINE} inline; {$ENDIF}
{$IFDEF WINDOWS}
var
LHandle : THandle;
LRec : TWin32FindData;
{$IFDEF WIN32_OR_WIN64}
LOldErrorMode : Integer;
{$ENDIF}
{$ENDIF}
{$IFDEF UNIX}
var
{$IFDEF USE_VCL_POSIX}
LRec : _Stat;
{$IFDEF USE_MARSHALLED_PTRS}
M: TMarshaller;
{$ENDIF}
{$ELSE}
{$IFDEF KYLIXCOMPAT}
LRec : TStatBuf;
{$ELSE}
LRec : TStat;
LU : time_t;
{$ENDIF}
{$ENDIF}
{$ENDIF}
{$IFNDEF NATIVEFILEAPI}
var
LStream: TIdReadFileExclusiveStream;
{$ENDIF}
{$ENDIF}
begin
{$IFDEF DOTNET}
Result := -1;
LFile := System.IO.FileInfo.Create(AFileName);
if LFile.Exists then begin
Result := LFile.Length;
end;
{$ENDIF}
{$IFDEF WINDOWS}
Result := -1;
//check to see if something like "a:\" is specified and fail in that case.
//FindFirstFile would probably succede even though a drive is not a proper
//file.
if not IsVolume(AFileName) then begin
{
IMPORTANT!!!
For servers in Windows, you probably want the API call to fail rather than
get a "Cancel Try Again Continue " dialog-box box if a drive is not
ready or there's some other critical I/O error.
}
{$IFDEF WIN32_OR_WIN64}
LOldErrorMode := SetErrorMode(SEM_FAILCRITICALERRORS);
try
{$ENDIF}
LHandle := Windows.FindFirstFile(PIdFileNameChar(AFileName), LRec);
if LHandle <> INVALID_HANDLE_VALUE then begin
Windows.FindClose(LHandle);
if (LRec.dwFileAttributes and Windows.FILE_ATTRIBUTE_DIRECTORY) = 0 then begin
Result := (Int64(LRec.nFileSizeHigh) shl 32) + LRec.nFileSizeLow;
end;
end;
{$IFDEF WIN32_OR_WIN64}
finally
SetErrorMode(LOldErrorMode);
end;
{$ENDIF}
end;
{$ENDIF}
{$IFDEF UNIX}
Result := -1;
{$IFDEF USE_VCL_POSIX}
//This is messy with IFDEF's but I want to be able to handle 63 bit file sizes.
if stat(
{$IFDEF USE_MARSHALLED_PTRS}
M.AsAnsi(AFileName).ToPointer
{$ELSE}
PAnsiChar(
{$IFDEF STRING_IS_ANSI}
AFileName
{$ELSE}
AnsiString(AFileName) // explicit convert to Ansi
{$ENDIF}
)
{$ENDIF}
, LRec) = 0 then
begin
Result := LRec.st_size;
end;
{$ELSE}
//Note that we can use stat here because we are only looking at the date.
if {$IFDEF KYLIXCOMPAT}stat{$ELSE}fpstat{$ENDIF}(
PAnsiChar(
{$IFDEF STRING_IS_ANSI}
AFileName
{$ELSE}
AnsiString(AFileName) // explicit convert to Ansi
{$ENDIF}
), LRec) = 0 then
begin
Result := LRec.st_Size;
end;
{$ENDIF}
{$ENDIF}
{$IFNDEF NATIVEFILEAPI}
Result := -1;
if FileExists(AFilename) then begin
// the other cases simply return -1 on error, so make sure to do the same here
try
// TODO: maybe use TIdReadFileNonExclusiveStream instead?
LStream := TIdReadFileExclusiveStream.Create(AFilename);
try
Result := LStream.Size;
finally
LStream.Free;
end;
except
end;
end;
{$ENDIF}
end;
function GetGMTDateByName(const AFileName : TIdFileName) : TDateTime;
{$IFDEF WINDOWS}
var
LRec : TWin32FindData;
LHandle : THandle;
LTime : {$IFDEF WINCE}TSystemTime{$ELSE}Integer{$ENDIF};
{$IFDEF WIN32_OR_WIN64}
LOldErrorMode : Integer;
{$ENDIF}
{$ENDIF}
{$IFDEF UNIX}
var
LTime : Integer;
{$IFDEF USE_VCL_POSIX}
LRec : _Stat;
{$IFDEF USE_MARSHALLED_PTRS}
M: TMarshaller;
{$ENDIF}
{$ENDIF}
{$IFDEF KYLIXCOMPAT}
LRec : TStatBuf;
LU : TUnixTime;
{$ENDIF}
{$IFDEF USE_BASEUNIX}
LRec : TStat;
LU : time_t;
{$ENDIF}
{$ENDIF}
begin
Result := -1;
{$IFDEF WINDOWS}
if not IsVolume(AFileName) then begin
{$IFDEF WIN32_OR_WIN64}
LOldErrorMode := SetErrorMode(SEM_FAILCRITICALERRORS);
try
{$ENDIF}
LHandle := Windows.FindFirstFile(PIdFileNameChar(AFileName), LRec);
{$IFDEF WIN32_OR_WIN64}
finally
SetErrorMode(LOldErrorMode);
end;
{$ENDIF}
if LHandle <> INVALID_HANDLE_VALUE then begin
Windows.FindClose(LHandle);
{$IFDEF WINCE}
FileTimeToSystemTime(@LRec, @LTime);
Result := SystemTimeToDateTime(LTime);
{$ELSE}
FileTimeToDosDateTime(LRec.ftLastWriteTime, LongRec(LTime).Hi, LongRec(LTime).Lo);
Result := FileDateToDateTime(LTime);
{$ENDIF}
end;
end;
{$ENDIF}
{$IFDEF DOTNET}
if System.IO.File.Exists(AFileName) then begin
Result := System.IO.File.GetLastWriteTimeUtc(AFileName).ToOADate;
end;
{$ENDIF}
{$IFDEF UNIX}
//Note that we can use stat here because we are only looking at the date.
{$IFDEF USE_BASEUNIX}
if fpstat(PAnsiChar(AnsiString(AFileName)), LRec) = 0 then
{$ENDIF}
{$IFDEF KYLIXCOMPAT}
if stat(PAnsiChar(AnsiString(AFileName)), LRec) = 0 then
{$ENDIF}
{$IFDEF USE_VCL_POSIX}
if stat(
{$IFDEF USE_MARSHALLED_PTRS}
M.AsAnsi(AFileName).ToPointer
{$ELSE}
PAnsiChar(
{$IFDEF STRING_IS_ANSI}
AFileName
{$ELSE}
AnsiString(AFileName) // explicit convert to Ansi
{$ENDIF}
)
{$ENDIF}
, LRec) = 0 then
{$ENDIF}
begin
LTime := LRec.st_mtime;
{$IFDEF KYLIXCOMPAT}
gmtime_r(@LTime, LU);
Result := EncodeDate(LU.tm_year + 1900, LU.tm_mon + 1, LU.tm_mday) +
EncodeTime(LU.tm_hour, LU.tm_min, LU.tm_sec, 0);
{$ENDIF}
{$IFDEF USE_BASEUNIX}
Result := UnixToDateTime(LTime);
{$ENDIF}
{$IFDEF USE_VCL_POSIX}
Result := DateUtils.UnixToDateTime(LTime);
{$ENDIF}
end;
{$ENDIF}
end;
function RightStr(const AStr: String; const Len: Integer): String;
var
LStrLen : Integer;
begin
LStrLen := Length(AStr);
if (Len > LStrLen) or (Len < 0) then begin
Result := AStr;
end else begin
//+1 is necessary for the Index because it is one based
Result := Copy(AStr, LStrLen - Len+1, Len);
end;
end;
function TimeZoneBias: TDateTime;
{$IFDEF USE_INLINE} inline; {$ENDIF}
{$IFNDEF FPC}
{$IFDEF UNIX}
var
T: Time_T;
TV: TimeVal;
{$IFDEF USE_VCL_POSIX}
UT: tm;
{$ELSE}
UT: TUnixTime;
{$ENDIF}
{$ENDIF}
{$ENDIF}
begin
{$IFNDEF FPC}
{$IFDEF UNIX}
// TODO: use -OffsetFromUTC here. It has this same Unix logic in it
{from http://edn.embarcadero.com/article/27890 }
gettimeofday(TV, nil);
T := TV.tv_sec;
{$IFDEF USE_VCL_POSIX}
localtime_r(T, UT);
// __tm_gmtoff is the bias in seconds from the UTC to the current time.
// so I multiply by -1 to compensate for this.
Result := (UT.tm_gmtoff / 60 / 60 / 24);
{$ELSE}
localtime_r(@T, UT);
// __tm_gmtoff is the bias in seconds from the UTC to the current time.
// so I multiply by -1 to compensate for this.
Result := (UT.__tm_gmtoff / 60 / 60 / 24);
{$ENDIF}
{$ELSE}
Result := -OffsetFromUTC;
{$ENDIF}
{$ELSE}
Result := -OffsetFromUTC;
{$ENDIF}
end;
function IndyStrToBool(const AString : String) : Boolean;
begin
// First check against each of the elements of the FalseBoolStrs
if PosInStrArray(AString, IndyFalseBoolStrs, False) <> -1 then begin
Result := False;
Exit;
end;
// Second check against each of the elements of the TrueBoolStrs
if PosInStrArray(AString, IndyTrueBoolStrs, False) <> -1 then begin
Result := True;
Exit;
end;
// None of the strings match, so convert to numeric (allowing an
// EConvertException to be thrown if not) and test against zero.
// If zero, return false, otherwise return true.
Result := IndyStrToInt(AString) <> 0;
end;
function IndySetLocalTime(Value: TDateTime): Boolean;
{$IFNDEF WINDOWS}
{$IFDEF USE_INLINE}inline;{$ENDIF}
{$ELSE}
var
dSysTime: TSystemTime;
buffer: DWord;
tkp, tpko: TTokenPrivileges;
hToken: THandle;
{$ENDIF}
begin
Result := False;
{$IFDEF LINUX}
//TODO: Implement SetTime for Linux. This call is not critical.
{$ENDIF}
{$IFDEF DOTNET}
//TODO: Figure out how to do this
{$ENDIF}
{$IFDEF WINDOWS}
{I admit that this routine is a little more complicated than the one
in Indy 8.0. However, this routine does support Windows NT privileges
meaning it will work if you have administrative rights under that OS
Original author Kerry G. Neighbour with modifications and testing
from J. Peter Mugaas}
{$IFNDEF WINCE}
// RLebeau 2/1/2008: MSDN says that SetLocalTime() does the adjustment
// automatically, so why is it being done manually?
if IndyWindowsPlatform = VER_PLATFORM_WIN32_NT then begin
if not Windows.OpenProcessToken(Windows.GetCurrentProcess(), TOKEN_ADJUST_PRIVILEGES or TOKEN_QUERY, hToken) then begin
Exit;
end;
if not Windows.LookupPrivilegeValue(nil, 'SeSystemtimePrivilege', tkp.Privileges[0].Luid) then begin {Do not Localize}
Windows.CloseHandle(hToken);
Exit;
end;
tkp.PrivilegeCount := 1;
tkp.Privileges[0].Attributes := SE_PRIVILEGE_ENABLED;
if not Windows.AdjustTokenPrivileges(hToken, FALSE, tkp, SizeOf(tkp), tpko, buffer) then begin
Windows.CloseHandle(hToken);
Exit;
end;
end;
{$ENDIF}
DateTimeToSystemTime(Value, dSysTime);
Result := Windows.SetLocalTime({$IFDEF FPC}@{$ENDIF}dSysTime);
{$IFNDEF WINCE}
if Result then begin
// RLebeau 2/1/2008: According to MSDN:
//
// "The system uses UTC internally. Therefore, when you call SetLocalTime(),
// the system uses the current time zone information to perform the conversion,
// including the daylight saving time setting. Note that the system uses the
// daylight saving time setting of the current time, not the new time you are
// setting. Therefore, to ensure the correct result, call SetLocalTime() a
// second time, now that the first call has updated the daylight saving time
// setting."
//
// TODO: adjust the Time manually so only 1 call to SetLocalTime() is needed...
if IndyWindowsPlatform = VER_PLATFORM_WIN32_NT then begin
Windows.SetLocalTime({$IFDEF FPC}@{$ENDIF}dSysTime);
// Windows 2000+ will broadcast WM_TIMECHANGE automatically...
if not IndyCheckWindowsVersion(5) then begin // Windows 2000 = v5.0
SendMessage(HWND_BROADCAST, WM_TIMECHANGE, 0, 0);
end;
end else begin
SendMessage(HWND_BROADCAST, WM_TIMECHANGE, 0, 0);
end;
end;
{Undo the Process Privilege change we had done for the
set time and close the handle that was allocated}
if IndyWindowsPlatform = VER_PLATFORM_WIN32_NT then begin
Windows.AdjustTokenPrivileges(hToken, False, tpko, SizeOf(tpko), tkp, Buffer);
Windows.CloseHandle(hToken);
end;
{$ENDIF}
{$ENDIF}
end;
function StrToDay(const ADay: string): Byte;
{$IFDEF USE_INLINE} inline; {$ENDIF}
begin
// RLebeau 03/04/2009: TODO - support localized strings as well...
Result := Succ(
PosInStrArray(ADay,
['SUN','MON','TUE','WED','THU','FRI','SAT'], {do not localize}
False));
end;
function StrToMonth(const AMonth: string): Byte;
const
// 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...
Months: array[0..7] of array[1..12] of string = (
// English
('JAN', 'FEB', 'MAR', 'APR', 'MAY', 'JUN', 'JUL', 'AUG', 'SEP', 'OCT', 'NOV', 'DEC'),
// English - alt. 4 letter abbreviations (Netware Print Services may return a 4 char month such as Sept)
('', '', '', '', '', 'JUNE','JULY', '', 'SEPT', '', '', ''),
// German
('', '', 'MRZ', '', 'MAI', '', '', '', '', 'OKT', '', 'DEZ'),
// Spanish
('ENO', 'FBRO','MZO', 'AB', '', '', '', 'AGTO','SBRE','OBRE','NBRE','DBRE'),
// Dutch
('', '', 'MRT', '', 'MEI', '', '', '', '', 'OKT', '', ''),
// French
('JANV','F'+Char($C9)+'V', 'MARS','AVR', 'MAI', 'JUIN','JUIL','AO'+Char($DB), 'SEPT','', '', 'D'+Char($C9)+'C'),
// French (alt)
('', 'F'+Char($C9)+'VR','', '', '', '', 'JUI', 'AO'+Char($DB)+'T','', '', '', ''),
// Slovenian
('', '', '', '', 'MAJ', '', '', '', 'AVG', '', '', ''));
var
i: Integer;
begin
if AMonth <> '' then begin
for i := Low(Months) to High(Months) do begin
for Result := Low(Months[i]) to High(Months[i]) do begin
if TextIsSame(AMonth, Months[i][Result]) then begin
Exit;
end;
end;
end;
end;
Result := 0;
end;
function UpCaseFirst(const AStr: string): string;
{$IFDEF USE_INLINE} inline; {$ENDIF}
{$IFDEF STRING_IS_IMMUTABLE}
var
LSB: TIdStringBuilder;
{$ENDIF}
begin
// TODO: support Unicode surrogates in the first position?
{$IFDEF STRING_IS_IMMUTABLE}
LSB := TIdStringBuilder.Create(LowerCase(TrimLeft(AStr)));
if LSB.Length > 0 then begin {Do not Localize}
LSB[0] := UpCase(LSB[0]);
end;
Result := LSB.ToString;
{$ELSE}
Result := LowerCase(TrimLeft(AStr));
if Result <> '' then begin {Do not Localize}
Result[1] := UpCase(Result[1]);
end;
{$ENDIF}
end;
function UpCaseFirstWord(const AStr: string): string;
var
I: Integer;
begin
for I := 1 to Length(AStr) do begin
if CharIsInSet(AStr, I, LWS) then begin
if I > 1 then begin
Result := UpperCase(Copy(AStr, 1, I-1)) + Copy(AStr, I, MaxInt);
Exit;
end;
Break;
end;
end;
Result := UpperCase(AStr);
end;
function IsHex(const AChar : Char) : Boolean;
{$IFDEF USE_INLINE} inline; {$ENDIF}
begin
Result := IndyPos(UpperCase(AChar), HexNumbers) > 0;
end;
function IsBinary(const AChar : Char) : Boolean;
{$IFDEF USE_INLINE} inline; {$ENDIF}
begin
Result := IndyPos(UpperCase(AChar), BinNumbers) > 0;
end;
function BinStrToInt(const ABinary: String): Integer;
var
I: Integer;
//From: http://www.experts-exchange.com/Programming/Programming_Languages/Delphi/Q_20622755.html
begin
Result := 0;
for I := 1 to Length(ABinary) do begin
Result := Result shl 1 or (Byte(ABinary[I]) and 1);
end;
end;
function ABNFToText(const AText : String) : String;
type
TIdRuleMode = (data, rule, decimal, hex, binary);
var
i : Integer;
LR : TIdRuleMode;
LNum : String;
begin
LR := data;
Result := '';
for i := 1 to Length(AText) do begin
case LR of
data :
if (AText[i] = '%') and (i < Length(AText)) then begin
LR := rule;
end else begin
Result := Result + AText[i];
end;
rule :
case AText[i] of
'd','D' : LR := decimal;
'x','X' : LR := hex;
'b','B' : LR := binary;
else
begin
LR := data;
Result := Result + '%';
end;
end;
decimal :
If IsNumeric(AText[i]) then begin
LNum := LNum + AText[i];
if IndyStrToInt(LNum, 0) > $FF then begin
IdDelete(LNum,Length(LNum),1);
Result := Result + Char(IndyStrToInt(LNum, 0));
LR := Data;
Result := Result + AText[i];
end;
end else begin
Result := Result + Char(IndyStrToInt(LNum, 0));
LNum := '';
if AText[i] <> '.' then begin
LR := Data;
Result := Result + AText[i];
end;
end;
hex :
If IsHex(AText[i]) and (Length(LNum) < 2) then begin
LNum := LNum + AText[i];
if IndyStrToInt('$'+LNum, 0) > $FF then begin
IdDelete(LNum,Length(LNum),1);
Result := Result + Char(IndyStrToInt(LNum,0));
LR := Data;
Result := Result + AText[i];
end;
end else begin
Result := Result + Char(IndyStrToInt('$'+LNum, 0));
LNum := '';
if AText[i] <> '.' then begin
LR := Data;
Result := Result + AText[i];
end;
end;
binary :
If IsBinary(AText[i]) and (Length(LNum)<8) then begin
LNum := LNum + AText[i];
if (BinStrToInt(LNum)>$FF) then begin
IdDelete(LNum,Length(LNum),1);
Result := Result + Char(BinStrToInt(LNum));
LR := Data;
Result := Result + AText[i];
end;
end else begin
Result := Result + Char(IndyStrToInt('$'+LNum, 0));
LNum := '';
if AText[i] <> '.' then begin
LR := Data;
Result := Result + AText[i];
end;
end;
end;
end;
end;
function GetMIMETypeFromFile(const AFile: TIdFileName): string;
var
MIMEMap: TIdMIMETable;
begin
MIMEMap := TIdMimeTable.Create(True);
try
Result := MIMEMap.GetFileMIMEType(AFile);
finally
MIMEMap.Free;
end;
end;
function GetMIMEDefaultFileExt(const MIMEType: string): TIdFileName;
var
MIMEMap: TIdMIMETable;
begin
MIMEMap := TIdMimeTable.Create(True);
try
Result := MIMEMap.GetDefaultFileExt(MIMEType);
finally
MIMEMap.Free;
end;
end;
// RLebeau: According to RFC 2822 Section 4.3:
//
// In the obsolete time zone, "UT" and "GMT" are indications of
// "Universal Time" and "Greenwich Mean Time" respectively and are both
// semantically identical to "+0000".
//
// The remaining three character zones are the US time zones. The first
// letter, "E", "C", "M", or "P" stands for "Eastern", "Central",
// "Mountain" and "Pacific". The second letter is either "S" for
// "Standard" time, or "D" for "Daylight" (or summer) time. Their
// interpretations are as follows:
//
// EDT is semantically equivalent to -0400
// EST is semantically equivalent to -0500
// CDT is semantically equivalent to -0500
// CST is semantically equivalent to -0600
// MDT is semantically equivalent to -0600
// MST is semantically equivalent to -0700
// PDT is semantically equivalent to -0700
// PST is semantically equivalent to -0800
//
// The 1 character military time zones were defined in a non-standard
// way in [RFC822] and are therefore unpredictable in their meaning.
// The original definitions of the military zones "A" through "I" are
// equivalent to "+0100" through "+0900" respectively; "K", "L", and "M"
// are equivalent to "+1000", "+1100", and "+1200" respectively; "N"
// through "Y" are equivalent to "-0100" through "-1200" respectively;
// and "Z" is equivalent to "+0000". However, because of the error in
// [RFC822], they SHOULD all be considered equivalent to "-0000" unless
// there is out-of-band information confirming their meaning.
//
// Other multi-character (usually between 3 and 5) alphabetic time zones
// have been used in Internet messages. Any such time zone whose
// meaning is not known SHOULD be considered equivalent to "-0000"
// unless there is out-of-band information confirming their meaning.
// RLebeau: according to http://en.wikipedia.org/wiki/Central_European_Time:
//
// Central European Time (CET) is one of the names of the time zone that is
// 1 hour ahead of Coordinated Universal Time. It is used in most European
// and some North African countries.
//
// Its time offset is normally UTC+1. During daylight saving time, Central
// European Summer Time (CEST) is used instead (UTC+2). The current time
// offset is UTC+1.
// RLebeau: other abbreviations taken from:
// http://www.timeanddate.com/library/abbreviations/timezones/
function TimeZoneToGmtOffsetStr(const ATimeZone: String): String;
type
TimeZoneOffset = record
TimeZone: String;
Offset: String;
end;
const
cTimeZones: array[0..90] of TimeZoneOffset = (
(TimeZone:'A'; Offset:'+0100'), // Alpha Time Zone - Military {do not localize}
(TimeZone:'ACDT'; Offset:'+1030'), // Australian Central Daylight Time {do not localize}
(TimeZone:'ACST'; Offset:'+0930'), // Australian Central Standard Time {do not localize}
(TimeZone:'ADT'; Offset:'-0300'), // Atlantic Daylight Time - North America {do not localize}
(TimeZone:'AEDT'; Offset:'+1100'), // Australian Eastern Daylight Time {do not localize}
(TimeZone:'AEST'; Offset:'+1000'), // Australian Eastern Standard Time {do not localize}
(TimeZone:'AKDT'; Offset:'-0800'), // Alaska Daylight Time {do not localize}
(TimeZone:'AKST'; Offset:'-0900'), // Alaska Standard Time {do not localize}
(TimeZone:'AST'; Offset:'-0400'), // Atlantic Standard Time - North America {do not localize}
(TimeZone:'AWDT'; Offset:'+0900'), // Australian Western Daylight Time {do not localize}
(TimeZone:'AWST'; Offset:'+0800'), // Australian Western Standard Time {do not localize}
(TimeZone:'B'; Offset:'+0200'), // Bravo Time Zone - Military {do not localize}
(TimeZone:'BST'; Offset:'+0100'), // British Summer Time - Europe {do not localize}
(TimeZone:'C'; Offset:'+0300'), // Charlie Time Zone - Military {do not localize}
(TimeZone:'CDT'; Offset:'+1030'), // Central Daylight Time - Australia {do not localize}
(TimeZone:'CDT'; Offset:'-0500'), // Central Daylight Time - North America {do not localize}
(TimeZone:'CEDT'; Offset:'+0200'), // Central European Daylight Time {do not localize}
(TimeZone:'CEST'; Offset:'+0200'), // Central European Summer Time {do not localize}
(TimeZone:'CET'; Offset:'+0100'), // Central European Time {do not localize}
(TimeZone:'CST'; Offset:'+1030'), // Central Summer Time - Australia {do not localize}
(TimeZone:'CST'; Offset:'+0930'), // Central Standard Time - Australia {do not localize}
(TimeZone:'CST'; Offset:'-0600'), // Central Standard Time - North America {do not localize}
(TimeZone:'CXT'; Offset:'+0700'), // Christmas Island Time - Australia {do not localize}
(TimeZone:'D'; Offset:'+0400'), // Delta Time Zone - Military {do not localize}
(TimeZone:'E'; Offset:'+0500'), // Echo Time Zone - Military {do not localize}
(TimeZone:'EDT'; Offset:'+1100'), // Eastern Daylight Time - Australia {do not localize}
(TimeZone:'EDT'; Offset:'-0400'), // Eastern Daylight Time - North America {do not localize}
(TimeZone:'EEDT'; Offset:'+0300'), // Eastern European Daylight Time {do not localize}
(TimeZone:'EEST'; Offset:'+0300'), // Eastern European Summer Time {do not localize}
(TimeZone:'EET'; Offset:'+0200'), // Eastern European Time {do not localize}
(TimeZone:'EST'; Offset:'+1100'), // Eastern Summer Time - Australia {do not localize}
(TimeZone:'EST'; Offset:'+1000'), // Eastern Standard Time - Australia {do not localize}
(TimeZone:'EST'; Offset:'-0500'), // Eastern Standard Time - North America {do not localize}
(TimeZone:'F'; Offset:'+0600'), // Foxtrot Time Zone - Military {do not localize}
(TimeZone:'G'; Offset:'+0700'), // Golf Time Zone - Military {do not localize}
(TimeZone:'GMT'; Offset:'+0000'), // Greenwich Mean Time - Europe {do not localize}
(TimeZone:'H'; Offset:'+0800'), // Hotel Time Zone - Military {do not localize}
(TimeZone:'HAA'; Offset:'-0300'), // Heure Avanc<6E>e de l'Atlantique - North America {do not localize}
(TimeZone:'HAC'; Offset:'-0500'), // Heure Avanc<6E>e du Centre - North America {do not localize}
(TimeZone:'HADT'; Offset:'-0900'), // Hawaii-Aleutian Daylight Time - North America {do not localize}
(TimeZone:'HAE'; Offset:'-0400'), // Heure Avanc<6E>e de l'Est - North America {do not localize}
(TimeZone:'HAP'; Offset:'-0700'), // Heure Avanc<6E>e du Pacifique - North America {do not localize}
(TimeZone:'HAR'; Offset:'-0600'), // Heure Avanc<6E>e des Rocheuses - North America {do not localize}
(TimeZone:'HAST'; Offset:'-1000'), // Hawaii-Aleutian Standard Time - North America {do not localize}
(TimeZone:'HAT'; Offset:'-0230'), // Heure Avanc<6E>e de Terre-Neuve - North America {do not localize}
(TimeZone:'HAY'; Offset:'-0800'), // Heure Avanc<6E>e du Yukon - North America {do not localize}
(TimeZone:'HNA'; Offset:'-0400'), // Heure Normale de l'Atlantique - North America {do not localize}
(TimeZone:'HNC'; Offset:'-0600'), // Heure Normale du Centre - North America {do not localize}
(TimeZone:'HNE'; Offset:'-0500'), // Heure Normale de l'Est - North America {do not localize}
(TimeZone:'HNP'; Offset:'-0800'), // Heure Normale du Pacifique - North America {do not localize}
(TimeZone:'HNR'; Offset:'-0700'), // Heure Normale des Rocheuses - North America {do not localize}
(TimeZone:'HNT'; Offset:'-0330'), // Heure Normale de Terre-Neuve - North America {do not localize}
(TimeZone:'HNY'; Offset:'-0900'), // Heure Normale du Yukon - North America {do not localize}
(TimeZone:'I'; Offset:'+0900'), // India Time Zone - Military {do not localize}
(TimeZone:'IST'; Offset:'+0100'), // Irish Summer Time - Europe {do not localize}
(TimeZone:'K'; Offset:'+1000'), // Kilo Time Zone - Military {do not localize}
(TimeZone:'L'; Offset:'+1100'), // Lima Time Zone - Military {do not localize}
(TimeZone:'M'; Offset:'+1200'), // Mike Time Zone - Military {do not localize}
(TimeZone:'MDT'; Offset:'-0600'), // Mountain Daylight Time - North America {do not localize}
(TimeZone:'MEHSZ';Offset:'+0300'), // Mitteleurop<6F>ische Hochsommerzeit - Europe {do not localize}
(TimeZone:'MESZ'; Offset:'+0200'), // Mitteleuro<72>ische Sommerzeit - Europe {do not localize}
(TimeZone:'MEZ'; Offset:'+0100'), // Mitteleurop<6F>ische Zeit - Europe {do not localize}
(TimeZone:'MSD'; Offset:'+0400'), // Moscow Daylight Time - Europe {do not localize}
(TimeZone:'MSK'; Offset:'+0300'), // Moscow Standard Time - Europe {do not localize}
(TimeZone:'MST'; Offset:'-0700'), // Mountain Standard Time - North America {do not localize}
(TimeZone:'N'; Offset:'-0100'), // November Time Zone - Military {do not localize}
(TimeZone:'NDT'; Offset:'-0230'), // Newfoundland Daylight Time - North America {do not localize}
(TimeZone:'NFT'; Offset:'+1130'), // Norfolk (Island), Time - Australia {do not localize}
(TimeZone:'NST'; Offset:'-0330'), // Newfoundland Standard Time - North America {do not localize}
(TimeZone:'O'; Offset:'-0200'), // Oscar Time Zone - Military {do not localize}
(TimeZone:'P'; Offset:'-0300'), // Papa Time Zone - Military {do not localize}
(TimeZone:'PDT'; Offset:'-0700'), // Pacific Daylight Time - North America {do not localize}
(TimeZone:'PST'; Offset:'-0800'), // Pacific Standard Time - North America {do not localize}
(TimeZone:'Q'; Offset:'-0400'), // Quebec Time Zone - Military {do not localize}
(TimeZone:'R'; Offset:'-0500'), // Romeo Time Zone - Military {do not localize}
(TimeZone:'S'; Offset:'-0600'), // Sierra Time Zone - Military {do not localize}
(TimeZone:'T'; Offset:'-0700'), // Tango Time Zone - Military {do not localize}
(TimeZone:'U'; Offset:'-0800'), // Uniform Time Zone - Military {do not localize}
(TimeZone:'UT'; Offset:'+0000'), // Universal Time - Europe {do not localize}
(TimeZone:'UTC'; Offset:'+0000'), // Coordinated Universal Time - Europe {do not localize}
(TimeZone:'V'; Offset:'-0900'), // Victor Time Zone - Military {do not localize}
(TimeZone:'W'; Offset:'-1000'), // Whiskey Time Zone - Military {do not localize}
(TimeZone:'WDT'; Offset:'+0900'), // Western Daylight Time - Australia {do not localize}
(TimeZone:'WEDT'; Offset:'+0100'), // Western European Daylight Time - Europe {do not localize}
(TimeZone:'WEST'; Offset:'+0100'), // Western European Summer Time - Europe {do not localize}
(TimeZone:'WET'; Offset:'+0000'), // Western European Time - Europe {do not localize}
(TimeZone:'WST'; Offset:'+0900'), // Western Summer Time - Australia {do not localize}
(TimeZone:'WST'; Offset:'+0800'), // Western Standard Time - Australia {do not localize}
(TimeZone:'X'; Offset:'-1100'), // X-ray Time Zone - Military {do not localize}
(TimeZone:'Y'; Offset:'-1200'), // Yankee Time Zone - Military {do not localize}
(TimeZone:'Z'; Offset:'+0000') // Zulu Time Zone - Military {do not localize}
);
var
I: Integer;
begin
for I := Low(cTimeZones) to High(cTimeZones) do begin
if TextIsSame(ATimeZone, cTimeZones[I].TimeZone) then begin
Result := cTimeZones[I].Offset;
Exit;
end;
end;
Result := '-0000' {do not localize}
end;
function GmtOffsetStrToDateTime(const S: string): TDateTime;
var
sTmp: String;
begin
Result := 0.0;
sTmp := Trim(S);
sTmp := Fetch(sTmp);
if Length(sTmp) > 0 then begin
if not CharIsInSet(sTmp, 1, '-+') then begin {do not localize}
sTmp := TimeZoneToGmtOffsetStr(sTmp);
end else
begin
// ISO 8601 has a colon in the middle, ignore it
if Length(sTmp) = 6 then begin
if CharEquals(sTmp, 4, ':') then begin {do not localize}
IdDelete(sTmp, 4, 1);
end;
end
// ISO 8601 allows the minutes to be omitted, add them
else if Length(sTmp) = 3 then begin
sTmp := sTmp + '00';
end;
if (Length(sTmp) <> 5) or (not IsNumeric(sTmp, 2, 2)) or (not IsNumeric(sTmp, 2, 4)) then begin
Exit;
end;
end;
try
Result := EncodeTime(IndyStrToInt(Copy(sTmp, 2, 2)), IndyStrToInt(Copy(sTmp, 4, 2)), 0, 0);
if CharEquals(sTmp, 1, '-') then begin {do not localize}
Result := -Result;
end;
except
Result := 0.0;
end;
end;
end;
{-Always returns date/time relative to GMT!! -Replaces StrInternetToDateTime}
function GMTToLocalDateTime(S: string): TDateTime;
var
DateTimeOffset: TDateTime;
begin
if RawStrInternetToDateTime(S, Result) then begin
DateTimeOffset := GmtOffsetStrToDateTime(S);
{-Apply GMT and local offsets}
Result := Result - DateTimeOffset + OffsetFromUTC;
end;
end;
{$IFNDEF HAS_TryStrToInt}
// TODO: declare this in the interface section...
function TryStrToInt(const S: string; out Value: Integer): Boolean;
{$IFDEF USE_INLINE}inline;{$ENDIF}
var
E: Integer;
begin
Val(S, Value, E);
Result := E = 0;
end;
{$ENDIF}
{ Using the algorithm defined in RFC 6265 section 5.1.1 }
function CookieStrToLocalDateTime(S: string): TDateTime;
const
{
delimiter = %x09 / %x20-2F / %x3B-40 / %x5B-60 / %x7B-7E
non-delimiter = %x00-08 / %x0A-1F / DIGIT / ":" / ALPHA / %x7F-FF
}
cDelimiters = #9' !"#$%&''()*+,-./;<=>?@[\]^_`{|}~';
var
LStartPos, LEndPos: Integer;
LFoundTime, LFoundDayOfMonth, LFoundMonth, LFoundYear: Boolean;
LHour, LMinute, LSecond: Integer;
LYear, LMonth, LDayOfMonth: Integer;
function ExtractDigits(var AStr: String; MinDigits, MaxDigits: Integer): String;
var
LLength: Integer;
begin
Result := '';
LLength := 0;
while (LLength < Length(AStr)) and (LLength < MaxDigits) do
begin
if not IsNumeric(AStr[LLength+1]) then begin
Break;
end;
Inc(LLength);
end;
if (LLength > 0) and (LLength >= MinDigits) then begin
Result := Copy(AStr, 1, LLength);
AStr := Copy(AStr, LLength+1, MaxInt);
end;
end;
function ParseTime(const AStr: String): Boolean;
var
S, LTemp: String;
begin
{
non-digit = %x00-2F / %x3A-FF
time = hms-time [ non-digit *OCTET ]
hms-time = time-field ":" time-field ":" time-field
time-field = 1*2DIGIT
}
Result := False;
S := AStr;
LTemp := ExtractDigits(S, 1, 2);
if (LTemp = '') or (not CharEquals(S, 1, ':')) then begin
Exit;
end;
if not TryStrToInt(LTemp, LHour) then begin
Exit;
end;
IdDelete(S, 1, 1);
LTemp := ExtractDigits(S, 1, 2);
if (LTemp = '') or (not CharEquals(S, 1, ':')) then begin
Exit;
end;
if not TryStrToInt(LTemp, LMinute) then begin
Exit;
end;
IdDelete(S, 1, 1);
LTemp := ExtractDigits(S, 1, 2);
if LTemp = '' then begin
Exit;
end;
if S <> '' then begin
if IsNumeric(S, 1, 1) then begin
raise Exception.Create('Invalid Cookie Time');
end;
end;
if not TryStrToInt(LTemp, LSecond) then begin
Exit;
end;
if LHour > 23 then begin
raise Exception.Create('Invalid Cookie Time');
end;
if LMinute > 59 then begin
raise Exception.Create('Invalid Cookie Time');
end;
if LSecond > 59 then begin
raise Exception.Create('Invalid Cookie Time');
end;
Result := True;
end;
function ParseDayOfMonth(const AStr: String): Boolean;
var
S, LTemp: String;
begin
{
non-digit = %x00-2F / %x3A-FF
day-of-month = 1*2DIGIT [ non-digit *OCTET ]
}
Result := False;
S := AStr;
LTemp := ExtractDigits(S, 1, 2);
if LTemp = '' then begin
Exit;
end;
if S <> '' then begin
if IsNumeric(S, 1, 1) then begin
raise Exception.Create('Invalid Cookie Day of Month');
end;
end;
if not TryStrToInt(LTemp, LDayOfMonth) then begin
Exit;
end;
if (LDayOfMonth < 1) or (LDayOfMonth > 31) then begin
raise Exception.Create('Invalid Cookie Day of Month');
end;
Result := True;
end;
function ParseMonth(const AStr: String): Boolean;
var
S, LTemp: String;
begin
{
month = ( "jan" / "feb" / "mar" / "apr" /
"may" / "jun" / "jul" / "aug" /
"sep" / "oct" / "nov" / "dec" ) *OCTET
}
Result := False;
LMonth := PosInStrArray(Copy(AStr, 1, 3), ['jan', 'feb', 'mar', 'apr', 'may', 'jun', 'jul', 'aug', 'sep', 'oct', 'nov', 'dec'], False) + 1;
if LMonth = 0 then begin
// RLebeau: per JP, some cookies have been encountered that use numbers
// instead of names, even though this is not allowed by various RFCs...
S := AStr;
LTemp := ExtractDigits(S, 1, 2);
if LTemp = '' then begin
Exit;
end;
if S <> '' then begin
if IsNumeric(S, 1, 1) then begin
raise Exception.Create('Invalid Cookie Month');
end;
end;
if not TryStrToInt(LTemp, LMonth) then begin
Exit;
end;
if (LMonth < 1) or (LMonth > 12) then begin
raise Exception.Create('Invalid Cookie Month');
end;
end;
Result := True;
end;
function ParseYear(const AStr: String): Boolean;
var
S, LTemp: String;
begin
// year = 2*4DIGIT [ non-digit *OCTET ]
Result := False;
S := AStr;
LTemp := ExtractDigits(S, 2, 4);
if (LTemp = '') or IsNumeric(S, 1, 1) then begin
Exit;
end;
if not TryStrToInt(AStr, LYear) then begin
Exit;
end;
if (LYear >= 70) and (LYear <= 99) then begin
Inc(LYear, 1900);
end
else if (LYear >= 0) and (LYear <= 69) then begin
Inc(LYear, 2000);
end;
if LYear < 1601 then begin
raise Exception.Create('Invalid Cookie Year');
end;
Result := True;
end;
procedure ProcessToken(const AStr: String);
begin
if not LFoundTime then begin
if ParseTime(AStr) then begin
LFoundTime := True;
Exit;
end;
end;
if not LFoundDayOfMonth then begin
if ParseDayOfMonth(AStr) then begin
LFoundDayOfMonth := True;
Exit;
end;
end;
if not LFoundMonth then begin
if ParseMonth(AStr) then begin
LFoundMonth := True;
Exit;
end;
end;
if not LFoundYear then begin
if ParseYear(AStr) then begin
LFoundYear := True;
Exit;
end;
end;
end;
begin
LFoundTime := False;
LFoundDayOfMonth := False;
LFoundMonth := False;
LFoundYear := False;
try
LEndPos := 0;
repeat
LStartPos := FindFirstNotOf(cDelimiters, S, -1, LEndPos+1);
if LStartPos = 0 then begin
Break;
end;
LEndPos := FindFirstOf(cDelimiters, S, -1, LStartPos+1);
if LEndPos = 0 then begin
ProcessToken(Copy(S, LStartPos, MaxInt));
Break;
end;
ProcessToken(Copy(S, LStartPos, LEndPos-LStartPos));
until False;
if (not LFoundDayOfMonth) or (not LFoundMonth) or (not LFoundYear) or (not LFoundTime) then begin
raise Exception.Create('Invalid Cookie Date format');
end;
Result := EncodeDate(LYear, LMonth, LDayOfMonth) + EncodeTime(LHour, LMinute, LSecond, 0) + OffsetFromUTC;
except
Result := 0.0;
end;
end;
{ Takes a UInt32 value and returns the string representation of it's binary value} {Do not Localize}
function IntToBin(Value: UInt32): string;
var
i: Integer;
{$IFDEF STRING_IS_IMMUTABLE}
LSB: TStringBuilder;
{$ENDIF}
begin
{$IFDEF STRING_IS_IMMUTABLE}
LSB := TStringBuilder.Create(32);
{$ELSE}
SetLength(Result, 32);
{$ENDIF}
for i := 1 to 32 do begin
if ((Value shl (i-1)) shr 31) = 0 then begin
{$IFDEF STRING_IS_IMMUTABLE}
LSB.Append(Char('0')); {do not localize}
{$ELSE}
Result[i] := '0'; {do not localize}
{$ENDIF}
end else begin
{$IFDEF STRING_IS_IMMUTABLE}
LSB.Append(Char('1')); {do not localize}
{$ELSE}
Result[i] := '1'; {do not localize}
{$ENDIF}
end;
end;
{$IFDEF STRING_IS_IMMUTABLE}
Result := LSB.ToString;
{$ENDIF}
end;
{ TIdMimeTable }
{$IFDEF UNIX}
procedure LoadMIME(const AFileName : String; AMIMEList : TStrings);
var
KeyList: TStringList;
i, p: Integer;
s, LMimeType, LExtension: String;
begin
if FileExists(AFileName) then begin {Do not localize}
// build list from /etc/mime.types style list file
// I'm lazy so I'm using a stringlist to load the file, ideally
// this should not be done, reading the file line by line is better
// I think - at least in terms of storage
KeyList := TStringList.Create;
try
// TODO: use TStreamReader instead, on versions that support it
KeyList.LoadFromFile(AFileName); {Do not localize}
for i := 0 to KeyList.Count -1 do begin
s := KeyList[i];
p := IndyPos('#', s); {Do not localize}
if p > 0 then begin
SetLength(s, p-1);
end;
if s <> '' then begin {Do not localize}
s := Trim(s);
LMimeType := IndyLowerCase(Fetch(s));
if LMimeType <> '' then begin {Do not localize}
while s <> '' do begin {Do not localize}
LExtension := IndyLowerCase(Fetch(s));
if LExtension <> '' then {Do not localize}
try
if LExtension[1] <> '.' then begin
LExtension := '.' + LExtension; {Do not localize}
end;
AMIMEList.Values[LExtension] := LMimeType;
except
on EListError do {ignore} ;
end;
end;
end;
end;
end;
except
on EFOpenError do {ignore} ;
end;
End;
end;
{$ENDIF}
procedure FillMimeTable(const AMIMEList: TStrings; const ALoadFromOS: Boolean = True);
{$IFDEF WINDOWS}
var
reg: TRegistry;
KeyList: TStringList;
i: Integer;
s, LExt: String;
{$ENDIF}
begin
{ Protect if someone is already filled (custom MomeConst) }
if not Assigned(AMIMEList) then begin
Exit;
end;
if AMIMEList.Count > 0 then begin
Exit;
end;
{NOTE: All of these strings should never be translated
because they are protocol specific and are important for some
web-browsers}
{ Animation }
AMIMEList.Add('.nml=animation/narrative'); {Do not Localize}
{ Audio }
AMIMEList.Add('.aac=audio/mp4');
AMIMEList.Add('.aif=audio/x-aiff'); {Do not Localize}
AMIMEList.Add('.aifc=audio/x-aiff'); {Do not Localize}
AMIMEList.Add('.aiff=audio/x-aiff'); {Do not Localize}
AMIMEList.Add('.au=audio/basic'); {Do not Localize}
AMIMEList.Add('.gsm=audio/x-gsm'); {Do not Localize}
AMIMEList.Add('.kar=audio/midi'); {Do not Localize}
AMIMEList.Add('.m3u=audio/mpegurl'); {Do not Localize}
AMIMEList.Add('.m4a=audio/x-mpg'); {Do not Localize}
AMIMEList.Add('.mid=audio/midi'); {Do not Localize}
AMIMEList.Add('.midi=audio/midi'); {Do not Localize}
AMIMEList.Add('.mpega=audio/x-mpg'); {Do not Localize}
AMIMEList.Add('.mp2=audio/x-mpg'); {Do not Localize}
AMIMEList.Add('.mp3=audio/x-mpg'); {Do not Localize}
AMIMEList.Add('.mpga=audio/x-mpg'); {Do not Localize}
AMIMEList.Add('.m3u=audio/x-mpegurl'); {Do not Localize}
AMIMEList.Add('.pls=audio/x-scpls'); {Do not Localize}
AMIMEList.Add('.qcp=audio/vnd.qcelp'); {Do not Localize}
AMIMEList.Add('.ra=audio/x-realaudio'); {Do not Localize}
AMIMEList.Add('.ram=audio/x-pn-realaudio'); {Do not Localize}
AMIMEList.Add('.rm=audio/x-pn-realaudio'); {Do not Localize}
AMIMEList.Add('.sd2=audio/x-sd2'); {Do not Localize}
AMIMEList.Add('.sid=audio/prs.sid'); {Do not Localize}
AMIMEList.Add('.snd=audio/basic'); {Do not Localize}
AMIMEList.Add('.wav=audio/x-wav'); {Do not Localize}
AMIMEList.Add('.wax=audio/x-ms-wax'); {Do not Localize}
AMIMEList.Add('.wma=audio/x-ms-wma'); {Do not Localize}
AMIMEList.Add('.mjf=audio/x-vnd.AudioExplosion.MjuiceMediaFile'); {Do not Localize}
{ Image }
AMIMEList.Add('.art=image/x-jg'); {Do not Localize}
AMIMEList.Add('.bmp=image/bmp'); {Do not Localize}
AMIMEList.Add('.cdr=image/x-coreldraw'); {Do not Localize}
AMIMEList.Add('.cdt=image/x-coreldrawtemplate'); {Do not Localize}
AMIMEList.Add('.cpt=image/x-corelphotopaint'); {Do not Localize}
AMIMEList.Add('.djv=image/vnd.djvu'); {Do not Localize}
AMIMEList.Add('.djvu=image/vnd.djvu'); {Do not Localize}
AMIMEList.Add('.gif=image/gif'); {Do not Localize}
AMIMEList.Add('.ief=image/ief'); {Do not Localize}
AMIMEList.Add('.ico=image/x-icon'); {Do not Localize}
AMIMEList.Add('.jng=image/x-jng'); {Do not Localize}
AMIMEList.Add('.jpg=image/jpeg'); {Do not Localize}
AMIMEList.Add('.jpeg=image/jpeg'); {Do not Localize}
AMIMEList.Add('.jpe=image/jpeg'); {Do not Localize}
AMIMEList.Add('.pat=image/x-coreldrawpattern'); {Do not Localize}
AMIMEList.Add('.pcx=image/pcx'); {Do not Localize}
AMIMEList.Add('.pbm=image/x-portable-bitmap'); {Do not Localize}
AMIMEList.Add('.pgm=image/x-portable-graymap'); {Do not Localize}
AMIMEList.Add('.pict=image/x-pict'); {Do not Localize}
AMIMEList.Add('.png=image/x-png'); {Do not Localize}
AMIMEList.Add('.pnm=image/x-portable-anymap'); {Do not Localize}
AMIMEList.Add('.pntg=image/x-macpaint'); {Do not Localize}
AMIMEList.Add('.ppm=image/x-portable-pixmap'); {Do not Localize}
AMIMEList.Add('.psd=image/x-psd'); {Do not Localize}
AMIMEList.Add('.qtif=image/x-quicktime'); {Do not Localize}
AMIMEList.Add('.ras=image/x-cmu-raster'); {Do not Localize}
AMIMEList.Add('.rf=image/vnd.rn-realflash'); {Do not Localize}
AMIMEList.Add('.rgb=image/x-rgb'); {Do not Localize}
AMIMEList.Add('.rp=image/vnd.rn-realpix'); {Do not Localize}
AMIMEList.Add('.sgi=image/x-sgi'); {Do not Localize}
AMIMEList.Add('.svg=image/svg+xml'); {Do not Localize}
AMIMEList.Add('.svgz=image/svg+xml'); {Do not Localize}
AMIMEList.Add('.targa=image/x-targa'); {Do not Localize}
AMIMEList.Add('.tif=image/x-tiff'); {Do not Localize}
AMIMEList.Add('.wbmp=image/vnd.wap.wbmp'); {Do not Localize}
AMIMEList.Add('.webp=image/webp'); {Do not localize}
AMIMEList.Add('.xbm=image/xbm'); {Do not Localize}
AMIMEList.Add('.xbm=image/x-xbitmap'); {Do not Localize}
AMIMEList.Add('.xpm=image/x-xpixmap'); {Do not Localize}
AMIMEList.Add('.xwd=image/x-xwindowdump'); {Do not Localize}
{ Text }
AMIMEList.Add('.323=text/h323'); {Do not Localize}
AMIMEList.Add('.xml=text/xml'); {Do not Localize}
AMIMEList.Add('.uls=text/iuls'); {Do not Localize}
AMIMEList.Add('.txt=text/plain'); {Do not Localize}
AMIMEList.Add('.rtx=text/richtext'); {Do not Localize}
AMIMEList.Add('.wsc=text/scriptlet'); {Do not Localize}
AMIMEList.Add('.rt=text/vnd.rn-realtext'); {Do not Localize}
AMIMEList.Add('.htt=text/webviewhtml'); {Do not Localize}
AMIMEList.Add('.htc=text/x-component'); {Do not Localize}
AMIMEList.Add('.vcf=text/x-vcard'); {Do not Localize}
{ Video }
AMIMEList.Add('.asf=video/x-ms-asf'); {Do not Localize}
AMIMEList.Add('.asx=video/x-ms-asf'); {Do not Localize}
AMIMEList.Add('.avi=video/x-msvideo'); {Do not Localize}
AMIMEList.Add('.dl=video/dl'); {Do not Localize}
AMIMEList.Add('.dv=video/dv'); {Do not Localize}
AMIMEList.Add('.flc=video/flc'); {Do not Localize}
AMIMEList.Add('.fli=video/fli'); {Do not Localize}
AMIMEList.Add('.gl=video/gl'); {Do not Localize}
AMIMEList.Add('.lsf=video/x-la-asf'); {Do not Localize}
AMIMEList.Add('.lsx=video/x-la-asf'); {Do not Localize}
AMIMEList.Add('.mng=video/x-mng'); {Do not Localize}
AMIMEList.Add('.mp2=video/mpeg'); {Do not Localize}
AMIMEList.Add('.mp3=video/mpeg'); {Do not Localize}
AMIMEList.Add('.mp4=video/mpeg'); {Do not Localize}
AMIMEList.Add('.mpeg=video/x-mpeg2a'); {Do not Localize}
AMIMEList.Add('.mpa=video/mpeg'); {Do not Localize}
AMIMEList.Add('.mpe=video/mpeg'); {Do not Localize}
AMIMEList.Add('.mpg=video/mpeg'); {Do not Localize}
AMIMEList.Add('.ogv=video/ogg'); {Do not Localize}
AMIMEList.Add('.moov=video/quicktime'); {Do not Localize}
AMIMEList.Add('.mov=video/quicktime'); {Do not Localize}
AMIMEList.Add('.mxu=video/vnd.mpegurl'); {Do not Localize}
AMIMEList.Add('.qt=video/quicktime'); {Do not Localize}
AMIMEList.Add('.qtc=video/x-qtc'); {Do not loccalize}
AMIMEList.Add('.rv=video/vnd.rn-realvideo'); {Do not Localize}
AMIMEList.Add('.ivf=video/x-ivf'); {Do not Localize}
AMIMEList.Add('.webm=video/webm'); {Do not Localize}
AMIMEList.Add('.wm=video/x-ms-wm'); {Do not Localize}
AMIMEList.Add('.wmp=video/x-ms-wmp'); {Do not Localize}
AMIMEList.Add('.wmv=video/x-ms-wmv'); {Do not Localize}
AMIMEList.Add('.wmx=video/x-ms-wmx'); {Do not Localize}
AMIMEList.Add('.wvx=video/x-ms-wvx'); {Do not Localize}
AMIMEList.Add('.rms=video/vnd.rn-realvideo-secure'); {Do not Localize}
AMIMEList.Add('.asx=video/x-ms-asf-plugin'); {Do not Localize}
AMIMEList.Add('.movie=video/x-sgi-movie'); {Do not Localize}
{ Application }
AMIMEList.Add('.7z=application/x-7z-compressed'); {Do not Localize}
AMIMEList.Add('.a=application/x-archive'); {Do not Localize}
AMIMEList.Add('.aab=application/x-authorware-bin'); {Do not Localize}
AMIMEList.Add('.aam=application/x-authorware-map'); {Do not Localize}
AMIMEList.Add('.aas=application/x-authorware-seg'); {Do not Localize}
AMIMEList.Add('.abw=application/x-abiword'); {Do not Localize}
AMIMEList.Add('.ace=application/x-ace-compressed'); {Do not Localize}
AMIMEList.Add('.ai=application/postscript'); {Do not Localize}
AMIMEList.Add('.alz=application/x-alz-compressed'); {Do not Localize}
AMIMEList.Add('.ani=application/x-navi-animation'); {Do not Localize}
AMIMEList.Add('.arj=application/x-arj'); {Do not Localize}
AMIMEList.Add('.asf=application/vnd.ms-asf'); {Do not Localize}
AMIMEList.Add('.bat=application/x-msdos-program'); {Do not Localize}
AMIMEList.Add('.bcpio=application/x-bcpio'); {Do not Localize}
AMIMEList.Add('.boz=application/x-bzip2'); {Do not Localize}
AMIMEList.Add('.bz=application/x-bzip');
AMIMEList.Add('.bz2=application/x-bzip2'); {Do not Localize}
AMIMEList.Add('.cab=application/vnd.ms-cab-compressed'); {Do not Localize}
AMIMEList.Add('.cat=application/vnd.ms-pki.seccat'); {Do not Localize}
AMIMEList.Add('.ccn=application/x-cnc'); {Do not Localize}
AMIMEList.Add('.cco=application/x-cocoa'); {Do not Localize}
AMIMEList.Add('.cdf=application/x-cdf'); {Do not Localize}
AMIMEList.Add('.cer=application/x-x509-ca-cert'); {Do not Localize}
AMIMEList.Add('.chm=application/vnd.ms-htmlhelp'); {Do not Localize}
AMIMEList.Add('.chrt=application/vnd.kde.kchart'); {Do not Localize}
AMIMEList.Add('.cil=application/vnd.ms-artgalry'); {Do not Localize}
AMIMEList.Add('.class=application/java-vm'); {Do not Localize}
AMIMEList.Add('.com=application/x-msdos-program'); {Do not Localize}
AMIMEList.Add('.clp=application/x-msclip'); {Do not Localize}
AMIMEList.Add('.cpio=application/x-cpio'); {Do not Localize}
AMIMEList.Add('.cpt=application/mac-compactpro'); {Do not Localize}
AMIMEList.Add('.cqk=application/x-calquick'); {Do not Localize}
AMIMEList.Add('.crd=application/x-mscardfile'); {Do not Localize}
AMIMEList.Add('.crl=application/pkix-crl'); {Do not Localize}
AMIMEList.Add('.csh=application/x-csh'); {Do not Localize}
AMIMEList.Add('.dar=application/x-dar'); {Do not Localize}
AMIMEList.Add('.dbf=application/x-dbase'); {Do not Localize}
AMIMEList.Add('.dcr=application/x-director'); {Do not Localize}
AMIMEList.Add('.deb=application/x-debian-package'); {Do not Localize}
AMIMEList.Add('.dir=application/x-director'); {Do not Localize}
AMIMEList.Add('.dist=vnd.apple.installer+xml'); {Do not Localize}
AMIMEList.Add('.distz=vnd.apple.installer+xml'); {Do not Localize}
AMIMEList.Add('.dll=application/x-msdos-program'); {Do not Localize}
AMIMEList.Add('.dmg=application/x-apple-diskimage'); {Do not Localize}
AMIMEList.Add('.doc=application/msword'); {Do not Localize}
AMIMEList.Add('.dot=application/msword'); {Do not Localize}
AMIMEList.Add('.dvi=application/x-dvi'); {Do not Localize}
AMIMEList.Add('.dxr=application/x-director'); {Do not Localize}
AMIMEList.Add('.ebk=application/x-expandedbook'); {Do not Localize}
AMIMEList.Add('.eps=application/postscript'); {Do not Localize}
AMIMEList.Add('.evy=application/envoy'); {Do not Localize}
AMIMEList.Add('.exe=application/x-msdos-program'); {Do not Localize}
AMIMEList.Add('.fdf=application/vnd.fdf'); {Do not Localize}
AMIMEList.Add('.fif=application/fractals'); {Do not Localize}
AMIMEList.Add('.flm=application/vnd.kde.kivio'); {Do not Localize}
AMIMEList.Add('.fml=application/x-file-mirror-list'); {Do not Localize}
AMIMEList.Add('.gzip=application/x-gzip'); {Do not Localize}
AMIMEList.Add('.gnumeric=application/x-gnumeric'); {Do not Localize}
AMIMEList.Add('.gtar=application/x-gtar'); {Do not Localize}
AMIMEList.Add('.gz=application/x-gzip'); {Do not Localize}
AMIMEList.Add('.hdf=application/x-hdf'); {Do not Localize}
AMIMEList.Add('.hlp=application/winhlp'); {Do not Localize}
AMIMEList.Add('.hpf=application/x-icq-hpf'); {Do not Localize}
AMIMEList.Add('.hqx=application/mac-binhex40'); {Do not Localize}
AMIMEList.Add('.hta=application/hta'); {Do not Localize}
AMIMEList.Add('.ims=application/vnd.ms-ims'); {Do not Localize}
AMIMEList.Add('.ins=application/x-internet-signup'); {Do not Localize}
AMIMEList.Add('.iii=application/x-iphone'); {Do not Localize}
AMIMEList.Add('.iso=application/x-iso9660-image'); {Do not Localize}
AMIMEList.Add('.jar=application/java-archive'); {Do not Localize}
AMIMEList.Add('.karbon=application/vnd.kde.karbon'); {Do not Localize}
AMIMEList.Add('.kfo=application/vnd.kde.kformula'); {Do not Localize}
AMIMEList.Add('.kon=application/vnd.kde.kontour'); {Do not Localize}
AMIMEList.Add('.kpr=application/vnd.kde.kpresenter'); {Do not Localize}
AMIMEList.Add('.kpt=application/vnd.kde.kpresenter'); {Do not Localize}
AMIMEList.Add('.kwd=application/vnd.kde.kword'); {Do not Localize}
AMIMEList.Add('.kwt=application/vnd.kde.kword'); {Do not Localize}
AMIMEList.Add('.latex=application/x-latex'); {Do not Localize}
AMIMEList.Add('.lha=application/x-lzh'); {Do not Localize}
AMIMEList.Add('.lcc=application/fastman'); {Do not Localize}
AMIMEList.Add('.lrm=application/vnd.ms-lrm'); {Do not Localize}
AMIMEList.Add('.lz=application/x-lzip'); {Do not Localize}
AMIMEList.Add('.lzh=application/x-lzh'); {Do not Localize}
AMIMEList.Add('.lzma=application/x-lzma'); {Do not Localize}
AMIMEList.Add('.lzo=application/x-lzop'); {Do not Localize}
AMIMEList.Add('.lzx=application/x-lzx');
AMIMEList.Add('.m13=application/x-msmediaview'); {Do not Localize}
AMIMEList.Add('.m14=application/x-msmediaview'); {Do not Localize}
AMIMEList.Add('.mpp=application/vnd.ms-project'); {Do not Localize}
AMIMEList.Add('.mvb=application/x-msmediaview'); {Do not Localize}
AMIMEList.Add('.man=application/x-troff-man'); {Do not Localize}
AMIMEList.Add('.mdb=application/x-msaccess'); {Do not Localize}
AMIMEList.Add('.me=application/x-troff-me'); {Do not Localize}
AMIMEList.Add('.ms=application/x-troff-ms'); {Do not Localize}
AMIMEList.Add('.msi=application/x-msi'); {Do not Localize}
AMIMEList.Add('.mpkg=vnd.apple.installer+xml'); {Do not Localize}
AMIMEList.Add('.mny=application/x-msmoney'); {Do not Localize}
AMIMEList.Add('.nix=application/x-mix-transfer'); {Do not Localize}
AMIMEList.Add('.o=application/x-object'); {Do not Localize}
AMIMEList.Add('.oda=application/oda'); {Do not Localize}
AMIMEList.Add('.odb=application/vnd.oasis.opendocument.database'); {Do not Localize}
AMIMEList.Add('.odc=application/vnd.oasis.opendocument.chart'); {Do not Localize}
AMIMEList.Add('.odf=application/vnd.oasis.opendocument.formula'); {Do not Localize}
AMIMEList.Add('.odg=application/vnd.oasis.opendocument.graphics'); {Do not Localize}
AMIMEList.Add('.odi=application/vnd.oasis.opendocument.image'); {Do not Localize}
AMIMEList.Add('.odm=application/vnd.oasis.opendocument.text-master'); {Do not Localize}
AMIMEList.Add('.odp=application/vnd.oasis.opendocument.presentation'); {Do not Localize}
AMIMEList.Add('.ods=application/vnd.oasis.opendocument.spreadsheet'); {Do not Localize}
AMIMEList.Add('.ogg=application/ogg'); {Do not Localize}
AMIMEList.Add('.odt=application/vnd.oasis.opendocument.text'); {Do not Localize}
AMIMEList.Add('.otg=application/vnd.oasis.opendocument.graphics-template'); {Do not Localize}
AMIMEList.Add('.oth=application/vnd.oasis.opendocument.text-web'); {Do not Localize}
AMIMEList.Add('.otp=application/vnd.oasis.opendocument.presentation-template'); {Do not Localize}
AMIMEList.Add('.ots=application/vnd.oasis.opendocument.spreadsheet-template'); {Do not Localize}
AMIMEList.Add('.ott=application/vnd.oasis.opendocument.text-template'); {Do not Localize}
AMIMEList.Add('.p10=application/pkcs10'); {Do not Localize}
AMIMEList.Add('.p12=application/x-pkcs12'); {Do not Localize}
AMIMEList.Add('.p7b=application/x-pkcs7-certificates'); {Do not Localize}
AMIMEList.Add('.p7m=application/pkcs7-mime'); {Do not Localize}
AMIMEList.Add('.p7r=application/x-pkcs7-certreqresp'); {Do not Localize}
AMIMEList.Add('.p7s=application/pkcs7-signature'); {Do not Localize}
AMIMEList.Add('.package=application/vnd.autopackage'); {Do not Localize}
AMIMEList.Add('.pfr=application/font-tdpfr'); {Do not Localize}
AMIMEList.Add('.pkg=vnd.apple.installer+xml'); {Do not Localize}
AMIMEList.Add('.pdf=application/pdf'); {Do not Localize}
AMIMEList.Add('.pko=application/vnd.ms-pki.pko'); {Do not Localize}
AMIMEList.Add('.pl=application/x-perl'); {Do not Localize}
AMIMEList.Add('.pnq=application/x-icq-pnq'); {Do not Localize}
AMIMEList.Add('.pot=application/mspowerpoint'); {Do not Localize}
AMIMEList.Add('.pps=application/mspowerpoint'); {Do not Localize}
AMIMEList.Add('.ppt=application/mspowerpoint'); {Do not Localize}
AMIMEList.Add('.ppz=application/mspowerpoint'); {Do not Localize}
AMIMEList.Add('.ps=application/postscript'); {Do not Localize}
AMIMEList.Add('.pub=application/x-mspublisher'); {Do not Localize}
AMIMEList.Add('.qpw=application/x-quattropro'); {Do not Localize}
AMIMEList.Add('.qtl=application/x-quicktimeplayer'); {Do not Localize}
AMIMEList.Add('.rar=application/rar'); {Do not Localize}
AMIMEList.Add('.rdf=application/rdf+xml'); {Do not Localize}
AMIMEList.Add('.rjs=application/vnd.rn-realsystem-rjs'); {Do not Localize}
AMIMEList.Add('.rm=application/vnd.rn-realmedia'); {Do not Localize}
AMIMEList.Add('.rmf=application/vnd.rmf'); {Do not Localize}
AMIMEList.Add('.rmp=application/vnd.rn-rn_music_package'); {Do not Localize}
AMIMEList.Add('.rmx=application/vnd.rn-realsystem-rmx'); {Do not Localize}
AMIMEList.Add('.rnx=application/vnd.rn-realplayer'); {Do not Localize}
AMIMEList.Add('.rpm=application/x-redhat-package-manager');
AMIMEList.Add('.rsml=application/vnd.rn-rsml'); {Do not Localize}
AMIMEList.Add('.rtsp=application/x-rtsp'); {Do not Localize}
AMIMEList.Add('.rss=application/rss+xml'); {Do not Localize}
AMIMEList.Add('.scm=application/x-icq-scm'); {Do not Localize}
AMIMEList.Add('.ser=application/java-serialized-object'); {Do not Localize}
AMIMEList.Add('.scd=application/x-msschedule'); {Do not Localize}
AMIMEList.Add('.sda=application/vnd.stardivision.draw'); {Do not Localize}
AMIMEList.Add('.sdc=application/vnd.stardivision.calc'); {Do not Localize}
AMIMEList.Add('.sdd=application/vnd.stardivision.impress'); {Do not Localize}
AMIMEList.Add('.sdp=application/x-sdp'); {Do not Localize}
AMIMEList.Add('.setpay=application/set-payment-initiation'); {Do not Localize}
AMIMEList.Add('.setreg=application/set-registration-initiation'); {Do not Localize}
AMIMEList.Add('.sh=application/x-sh'); {Do not Localize}
AMIMEList.Add('.shar=application/x-shar'); {Do not Localize}
AMIMEList.Add('.shw=application/presentations'); {Do not Localize}
AMIMEList.Add('.sit=application/x-stuffit'); {Do not Localize}
AMIMEList.Add('.sitx=application/x-stuffitx'); {Do not localize}
AMIMEList.Add('.skd=application/x-koan'); {Do not Localize}
AMIMEList.Add('.skm=application/x-koan'); {Do not Localize}
AMIMEList.Add('.skp=application/x-koan'); {Do not Localize}
AMIMEList.Add('.skt=application/x-koan'); {Do not Localize}
AMIMEList.Add('.smf=application/vnd.stardivision.math'); {Do not Localize}
AMIMEList.Add('.smi=application/smil'); {Do not Localize}
AMIMEList.Add('.smil=application/smil'); {Do not Localize}
AMIMEList.Add('.spl=application/futuresplash'); {Do not Localize}
AMIMEList.Add('.ssm=application/streamingmedia'); {Do not Localize}
AMIMEList.Add('.sst=application/vnd.ms-pki.certstore'); {Do not Localize}
AMIMEList.Add('.stc=application/vnd.sun.xml.calc.template'); {Do not Localize}
AMIMEList.Add('.std=application/vnd.sun.xml.draw.template'); {Do not Localize}
AMIMEList.Add('.sti=application/vnd.sun.xml.impress.template'); {Do not Localize}
AMIMEList.Add('.stl=application/vnd.ms-pki.stl'); {Do not Localize}
AMIMEList.Add('.stw=application/vnd.sun.xml.writer.template'); {Do not Localize}
AMIMEList.Add('.svi=application/softvision'); {Do not Localize}
AMIMEList.Add('.sv4cpio=application/x-sv4cpio'); {Do not Localize}
AMIMEList.Add('.sv4crc=application/x-sv4crc'); {Do not Localize}
AMIMEList.Add('.swf=application/x-shockwave-flash'); {Do not Localize}
AMIMEList.Add('.swf1=application/x-shockwave-flash'); {Do not Localize}
AMIMEList.Add('.sxc=application/vnd.sun.xml.calc'); {Do not Localize}
AMIMEList.Add('.sxi=application/vnd.sun.xml.impress'); {Do not Localize}
AMIMEList.Add('.sxm=application/vnd.sun.xml.math'); {Do not Localize}
AMIMEList.Add('.sxw=application/vnd.sun.xml.writer'); {Do not Localize}
AMIMEList.Add('.sxg=application/vnd.sun.xml.writer.global'); {Do not Localize}
AMIMEList.Add('.t=application/x-troff'); {Do not Localize}
AMIMEList.Add('.tar=application/x-tar'); {Do not Localize}
AMIMEList.Add('.tcl=application/x-tcl'); {Do not Localize}
AMIMEList.Add('.tex=application/x-tex'); {Do not Localize}
AMIMEList.Add('.texi=application/x-texinfo'); {Do not Localize}
AMIMEList.Add('.texinfo=application/x-texinfo'); {Do not Localize}
AMIMEList.Add('.tbz=application/x-bzip-compressed-tar'); {Do not Localize}
AMIMEList.Add('.tbz2=application/x-bzip-compressed-tar'); {Do not Localize}
AMIMEList.Add('.tgz=application/x-compressed-tar'); {Do not Localize}
AMIMEList.Add('.tlz=application/x-lzma-compressed-tar'); {Do not Localize}
AMIMEList.Add('.tr=application/x-troff'); {Do not Localize}
AMIMEList.Add('.trm=application/x-msterminal'); {Do not Localize}
AMIMEList.Add('.troff=application/x-troff'); {Do not Localize}
AMIMEList.Add('.tsp=application/dsptype'); {Do not Localize}
AMIMEList.Add('.torrent=application/x-bittorrent'); {Do not Localize}
AMIMEList.Add('.ttz=application/t-time'); {Do not Localize}
AMIMEList.Add('.txz=application/x-xz-compressed-tar'); {Do not localize}
AMIMEList.Add('.udeb=application/x-debian-package'); {Do not Localize}
AMIMEList.Add('.uin=application/x-icq'); {Do not Localize}
AMIMEList.Add('.urls=application/x-url-list'); {Do not Localize}
AMIMEList.Add('.ustar=application/x-ustar'); {Do not Localize}
AMIMEList.Add('.vcd=application/x-cdlink'); {Do not Localize}
AMIMEList.Add('.vor=application/vnd.stardivision.writer'); {Do not Localize}
AMIMEList.Add('.vsl=application/x-cnet-vsl'); {Do not Localize}
AMIMEList.Add('.wcm=application/vnd.ms-works'); {Do not Localize}
AMIMEList.Add('.wb1=application/x-quattropro'); {Do not Localize}
AMIMEList.Add('.wb2=application/x-quattropro'); {Do not Localize}
AMIMEList.Add('.wb3=application/x-quattropro'); {Do not Localize}
AMIMEList.Add('.wdb=application/vnd.ms-works'); {Do not Localize}
AMIMEList.Add('.wks=application/vnd.ms-works'); {Do not Localize}
AMIMEList.Add('.wmd=application/x-ms-wmd'); {Do not Localize}
AMIMEList.Add('.wms=application/x-ms-wms'); {Do not Localize}
AMIMEList.Add('.wmz=application/x-ms-wmz'); {Do not Localize}
AMIMEList.Add('.wp5=application/wordperfect5.1'); {Do not Localize}
AMIMEList.Add('.wpd=application/wordperfect'); {Do not Localize}
AMIMEList.Add('.wpl=application/vnd.ms-wpl'); {Do not Localize}
AMIMEList.Add('.wps=application/vnd.ms-works'); {Do not Localize}
AMIMEList.Add('.wri=application/x-mswrite'); {Do not Localize}
AMIMEList.Add('.xfdf=application/vnd.adobe.xfdf'); {Do not Localize}
AMIMEList.Add('.xls=application/x-msexcel'); {Do not Localize}
AMIMEList.Add('.xlb=application/x-msexcel'); {Do not Localize}
AMIMEList.Add('.xpi=application/x-xpinstall'); {Do not Localize}
AMIMEList.Add('.xps=application/vnd.ms-xpsdocument'); {Do not Localize}
AMIMEList.Add('.xsd=application/vnd.sun.xml.draw'); {Do not Localize}
AMIMEList.Add('.xul=application/vnd.mozilla.xul+xml'); {Do not Localize}
AMIMEList.Add('.z=application/x-compress'); {Do not Localize}
AMIMEList.Add('.zoo=application/x-zoo'); {Do not Localize}
AMIMEList.Add('.zip=application/x-zip-compressed'); {Do not Localize}
{ WAP }
AMIMEList.Add('.wbmp=image/vnd.wap.wbmp'); {Do not Localize}
AMIMEList.Add('.wml=text/vnd.wap.wml'); {Do not Localize}
AMIMEList.Add('.wmlc=application/vnd.wap.wmlc'); {Do not Localize}
AMIMEList.Add('.wmls=text/vnd.wap.wmlscript'); {Do not Localize}
AMIMEList.Add('.wmlsc=application/vnd.wap.wmlscriptc'); {Do not Localize}
{ Non-web text}
{
IMPORTANT!!
You should not use a text MIME type definition unless you are
extremely certain that the file will NOT be a binary. Some browsers
will display the text instead of saving to disk and it looks ugly
if a web-browser shows all of the 8bit charactors.
}
//of course, we have to add this :-).
AMIMEList.Add('.asm=text/x-asm'); {Do not Localize}
AMIMEList.Add('.p=text/x-pascal'); {Do not Localize}
AMIMEList.Add('.pas=text/x-pascal'); {Do not Localize}
AMIMEList.Add('.cs=text/x-csharp'); {Do not Localize}
AMIMEList.Add('.c=text/x-csrc'); {Do not Localize}
AMIMEList.Add('.c++=text/x-c++src'); {Do not Localize}
AMIMEList.Add('.cpp=text/x-c++src'); {Do not Localize}
AMIMEList.Add('.cxx=text/x-c++src'); {Do not Localize}
AMIMEList.Add('.cc=text/x-c++src'); {Do not Localize}
AMIMEList.Add('.h=text/x-chdr'); {Do not localize}
AMIMEList.Add('.h++=text/x-c++hdr'); {Do not Localize}
AMIMEList.Add('.hpp=text/x-c++hdr'); {Do not Localize}
AMIMEList.Add('.hxx=text/x-c++hdr'); {Do not Localize}
AMIMEList.Add('.hh=text/x-c++hdr'); {Do not Localize}
AMIMEList.Add('.java=text/x-java'); {Do not Localize}
{ WEB }
AMIMEList.Add('.css=text/css'); {Do not Localize}
AMIMEList.Add('.js=text/javascript'); {Do not Localize}
AMIMEList.Add('.htm=text/html'); {Do not Localize}
AMIMEList.Add('.html=text/html'); {Do not Localize}
AMIMEList.Add('.xhtml=application/xhtml+xml'); {Do not localize}
AMIMEList.Add('.xht=application/xhtml+xml'); {Do not localize}
AMIMEList.Add('.rdf=application/rdf+xml'); {Do not localize}
AMIMEList.Add('.rss=application/rss+xml'); {Do not localize}
AMIMEList.Add('.ls=text/javascript'); {Do not Localize}
AMIMEList.Add('.mocha=text/javascript'); {Do not Localize}
AMIMEList.Add('.shtml=server-parsed-html'); {Do not Localize}
AMIMEList.Add('.xml=text/xml'); {Do not Localize}
AMIMEList.Add('.sgm=text/sgml'); {Do not Localize}
AMIMEList.Add('.sgml=text/sgml'); {Do not Localize}
{ Message }
AMIMEList.Add('.mht=message/rfc822'); {Do not Localize}
if not ALoadFromOS then begin
Exit;
end;
{$IFDEF WINDOWS}
// Build the file type/MIME type map
Reg := TRegistry.Create;
try
KeyList := TStringList.create;
try
Reg.RootKey := HKEY_CLASSES_ROOT;
if Reg.OpenKeyReadOnly('\') then begin {do not localize}
Reg.GetKeyNames(KeyList);
Reg.Closekey;
end;
// get a list of registered extentions
for i := 0 to KeyList.Count - 1 do begin
LExt := KeyList[i];
if TextStartsWith(LExt, '.') then begin {do not localize}
if Reg.OpenKeyReadOnly(LExt) then begin
s := Reg.ReadString('Content Type'); {do not localize}
if Length(s) > 0 then begin
AMIMEList.Values[IndyLowerCase(LExt)] := IndyLowerCase(s);
end;
Reg.CloseKey;
end;
end;
end;
if Reg.OpenKeyReadOnly('\MIME\Database\Content Type') then begin {do not localize}
// get a list of registered MIME types
KeyList.Clear;
Reg.GetKeyNames(KeyList);
Reg.CloseKey;
for i := 0 to KeyList.Count - 1 do begin
if Reg.OpenKeyReadOnly('\MIME\Database\Content Type\' + KeyList[i]) then begin {do not localize}
LExt := IndyLowerCase(Reg.ReadString('Extension')); {do not localize}
if Length(LExt) > 0 then begin
if LExt[1] <> '.' then begin
LExt := '.' + LExt; {do not localize}
end;
AMIMEList.Values[LExt] := IndyLowerCase(KeyList[i]);
end;
Reg.CloseKey;
end;
end;
end;
finally
KeyList.Free;
end;
finally
Reg.Free;
end;
{$ENDIF}
{$IFDEF UNIX}
{
/etc/mime.types is not present in all Linux distributions.
It turns out that "/etc/htdig/mime.types" and
"/etc/usr/share/webmin/mime..types" are in the same format as what
Johannes Berg had expected.
Just read those files for best coverage. MIME Tables are not centralized
on Linux.
}
LoadMIME('/etc/mime.types', AMIMEList); {do not localize}
LoadMIME('/etc/htdig/mime.types', AMIMEList); {do not localize}
LoadMIME('/etc/usr/share/webmin/mime.types', AMIMEList); {do not localize}
{$ENDIF}
end;
procedure TIdMimeTable.AddMimeType(const Ext, MIMEType: string; const ARaiseOnError: Boolean = True);
var
LExt,
LMIMEType: string;
begin
{ Check and fix extension }
LExt := IndyLowerCase(Ext);
if Length(LExt) = 0 then begin
if ARaiseOnError then begin
raise EIdException.Create(RSMIMEExtensionEmpty);
end;
Exit;
end;
{ Check and fix MIMEType }
LMIMEType := IndyLowerCase(MIMEType);
if Length(LMIMEType) = 0 then begin
if ARaiseOnError then begin
raise EIdException.Create(RSMIMEMIMETypeEmpty);
end;
Exit;
end;
if LExt[1] <> '.' then begin {do not localize}
LExt := '.' + LExt; {do not localize}
end;
{ Check list }
if FFileExt.IndexOf(LExt) = -1 then begin
FFileExt.Add(LExt);
FMIMEList.Add(LMIMEType);
end else begin
if ARaiseOnError then begin
raise EIdException.Create(RSMIMEMIMEExtAlreadyExists);
end;
Exit;
end;
end;
procedure TIdMimeTable.BuildCache;
begin
if Assigned(FOnBuildCache) then begin
FOnBuildCache(Self);
end else begin
if FFileExt.Count = 0 then begin
BuildDefaultCache;
end;
end;
end;
procedure TIdMimeTable.BuildDefaultCache;
{This is just to provide some default values only}
var
LKeys : TStringList;
begin
LKeys := TStringList.Create;
try
FillMIMETable(LKeys, LoadTypesFromOS);
LoadFromStrings(LKeys);
finally
FreeAndNil(LKeys);
end;
end;
constructor TIdMimeTable.Create(const AutoFill: Boolean);
begin
inherited Create;
FLoadTypesFromOS := True;
FFileExt := TStringList.Create;
FMIMEList := TStringList.Create;
if AutoFill then begin
BuildCache;
end;
end;
destructor TIdMimeTable.Destroy;
begin
FreeAndNil(FMIMEList);
FreeAndNil(FFileExt);
inherited Destroy;
end;
function TIdMimeTable.GetDefaultFileExt(const MIMEType: string): String;
var
Index : Integer;
LMimeType: string;
begin
LMimeType := IndyLowerCase(MIMEType);
Index := FMIMEList.IndexOf(LMimeType);
if Index = -1 then begin
BuildCache;
Index := FMIMEList.IndexOf(LMIMEType);
end;
if Index <> -1 then begin
Result := FFileExt[Index];
end else begin
Result := ''; {Do not Localize}
end;
end;
function TIdMimeTable.GetFileMIMEType(const AFileName: string): string;
var
Index : Integer;
LExt: string;
begin
LExt := IndyLowerCase(ExtractFileExt(AFileName));
Index := FFileExt.IndexOf(LExt);
if Index = -1 then begin
BuildCache;
Index := FFileExt.IndexOf(LExt);
end;
if Index <> -1 then begin
Result := FMIMEList[Index];
end else begin
Result := 'application/octet-stream' {do not localize}
end;
end;
procedure TIdMimeTable.LoadFromStrings(const AStrings: TStrings; const MimeSeparator: Char = '='); {Do not Localize}
var
I, P: Integer;
S, Ext: string;
begin
Assert(AStrings <> nil);
FFileExt.Clear;
FMIMEList.Clear;
for I := 0 to AStrings.Count - 1 do begin
S := AStrings[I];
P := Pos(MimeSeparator, S);
if P > 0 then begin
Ext := IndyLowerCase(Copy(S, 1, P - 1));
AddMimeType(Ext, Copy(S, P + 1, MaxInt), False);
end;
end;
end;
procedure TIdMimeTable.SaveToStrings(const AStrings: TStrings;
const MimeSeparator: Char);
var
I : Integer;
begin
Assert(AStrings <> nil);
AStrings.Clear;
for I := 0 to FFileExt.Count - 1 do begin
AStrings.Add(FFileExt[I] + MimeSeparator + FMIMEList[I]);
end;
end;
function IsValidIP(const S: String): Boolean;
{$IFDEF USE_INLINE}inline;{$ENDIF}
var
LErr: Boolean;
begin
LErr := False; // keep the compiler happy
IPv4ToUInt32(S, LErr);
if LErr then begin
LErr := (MakeCanonicalIPv6Address(S) = '');
end;
Result := not LErr;
end;
//everything that does not start with '.' is treated as hostname
function IsHostname(const S: String): Boolean;
{$IFDEF USE_INLINE} inline; {$ENDIF}
begin
Result := (not TextStartsWith(S, '.')) and (not IsValidIP(S)) ; {Do not Localize}
end;
function IsTopDomain(const AStr: string): Boolean;
Var
i: Integer;
S1,LTmp: String;
begin
i := 0;
LTmp := UpperCase(Trim(AStr));
while IndyPos('.', LTmp) > 0 do begin {Do not Localize}
S1 := LTmp;
Fetch(LTmp, '.'); {Do not Localize}
i := i + 1;
end;
Result := ((Length(LTmp) > 2) and (i = 1));
if Length(LTmp) = 2 then begin // Country domain names
S1 := Fetch(S1, '.'); {Do not Localize}
// here will be the exceptions check: com.uk, co.uk, com.tw and etc.
if LTmp = 'UK' then begin {Do not Localize}
if S1 = 'CO' then begin
result := i = 2; {Do not Localize}
end;
if S1 = 'COM' then begin
result := i = 2; {Do not Localize}
end;
end;
if LTmp = 'TW' then begin {Do not Localize}
if S1 = 'CO' then begin
result := i = 2; {Do not Localize}
end;
if S1 = 'COM' then begin
result := i = 2; {Do not Localize}
end;
end;
end;
end;
function IsDomain(const S: String): Boolean;
{$IFDEF USE_INLINE} inline; {$ENDIF}
begin
Result := (not IsHostname(S)) and (IndyPos('.', S) > 0) and (not IsTopDomain(S)); {Do not Localize}
end;
function DomainName(const AHost: String): String;
{$IFDEF USE_INLINE} inline; {$ENDIF}
begin
Result := Copy(AHost, IndyPos('.', AHost), Length(AHost)); {Do not Localize}
end;
function IsFQDN(const S: String): Boolean;
{$IFDEF USE_INLINE} inline; {$ENDIF}
begin
Result := IsHostName(S) and IsDomain(DomainName(S));
end;
// The password for extracting password.bin from password.zip is indyrules
function PadString(const AString : String; const ALen : Integer; const AChar: Char): String;
{$IFDEF USE_INLINE} inline; {$ENDIF}
begin
if Length(Result) >= ALen then begin
Result := AString;
end else begin
Result := AString + StringOfChar(AChar, ALen-Length(AString));
end;
end;
function ProcessPath(const ABasePath: string;
const APath: string;
const APathDelim: string = '/'): string; {Do not Localize}
// Dont add / - sometimes a file is passed in as well and the only way to determine is
// to test against the actual targets
var
i: Integer;
LPreserveTrail: Boolean;
LWork: string;
begin
if TextStartsWith(APath, APathDelim) then begin
Result := APath;
end else begin
Result := ''; {Do not Localize}
LPreserveTrail := (Length(APath) = 0) or TextEndsWith(APath, APathDelim);
LWork := ABasePath;
// If LWork = '' then we just want it to be APath, no prefixed / {Do not Localize}
if (Length(LWork) > 0) and (not TextEndsWith(LWork, APathDelim)) then begin
LWork := LWork + APathDelim;
end;
LWork := LWork + APath;
if Length(LWork) > 0 then begin
i := 1;
while i <= Length(LWork) do begin
if LWork[i] = APathDelim then begin
if i = 1 then begin
Result := APathDelim;
end
else if not TextEndsWith(Result, APathDelim) then begin
Result := Result + LWork[i];
end;
end else begin
if LWork[i] = '.' then begin {Do not Localize}
// If the last character was a PathDelim then the . is a relative path modifier.
// If it doesnt follow a PathDelim, its part of a filename
if TextEndsWith(Result, APathDelim) and (Copy(LWork, i, 2) = '..') then begin {Do not Localize}
// Delete the last PathDelim
Delete(Result, Length(Result), 1);
// Delete up to the next PathDelim
while (Length(Result) > 0) and (not TextEndsWith(Result, APathDelim)) do begin
Delete(Result, Length(Result), 1);
end;
// Skip over second .
Inc(i);
end else begin
Result := Result + LWork[i];
end;
end else begin
Result := Result + LWork[i];
end;
end;
Inc(i);
end;
end;
// Sometimes .. semantics can put a PathDelim on the end
// But dont modify if it is only a PathDelim and nothing else, or it was there to begin with
if (Result <> APathDelim) and TextEndsWith(Result, APathDelim) and (not LPreserveTrail) then begin
Delete(Result, Length(Result), 1);
end;
end;
end;
{** HTML Parsing code for extracting Metadata. It can also be the basis of a Full HTML parser ***}
const
HTML_DOCWHITESPACE = #0+#9+#10+#13+#32; {do not localize}
HTML_ALLOWABLE_ALPHANUMBERIC = 'abcdefghijklmnopqrstuvwxyz'+ {do not localize}
'ABCDEFGHIJKLMNOPQRSTUVWXYZ'+ {do not localize}
'1234567890-_:.'; {do not localize}
HTML_QUOTECHARS = '''"'; {do not localize}
HTML_MainDocParts : array [0..2] of string = ('TITLE','HEAD', 'BODY'); {do not localize}
HTML_HeadDocAttrs : array [0..3] of string = ('META','TITLE','SCRIPT','LINK'); {do not localize}
HTML_MetaAttrs : array [0..1] of string = ('HTTP-EQUIV', 'charset'); {do not localize}
function ParseUntilEndOfTag(const AStr : String; var VPos : Integer;
const ALen : Integer): String; {$IFDEF USE_INLINE}inline;{$ENDIF}
var
LStart: Integer;
begin
LStart := VPos;
while VPos <= ALen do begin
if AStr[VPos] = '>' then begin {do not localize}
Break;
end;
Inc(VPos);
end;
Result := Copy(AStr, LStart, VPos - LStart);
end;
procedure DiscardUntilEndOfTag(const AStr : String; var VPos : Integer;
const ALen : Integer); {$IFDEF USE_INLINE}inline;{$ENDIF}
begin
while VPos <= ALen do begin
if AStr[VPos] = '>' then begin {do not localize}
Break;
end;
Inc(VPos);
end;
end;
function ExtractDocWhiteSpace(const AStr : String; var VPos : Integer;
const ALen : Integer) : String; {$IFDEF USE_INLINE}inline;{$ENDIF}
var
LStart: Integer;
begin
LStart := VPos;
while VPos <= ALen do begin
if not CharIsInSet(AStr, VPos, HTML_DOCWHITESPACE) then begin
Break;
end;
Inc(VPos);
end;
Result := Copy(AStr, LStart, VPos-LStart);
end;
procedure DiscardDocWhiteSpace(const AStr : String; var VPos : Integer; const ALen : Integer); {$IFDEF USE_INLINE}inline; {$ENDIF}
begin
while VPos <= ALen do begin
if not CharIsInSet(AStr, VPos, HTML_DOCWHITESPACE) then begin
Break;
end;
Inc(VPos);
end;
end;
function ParseWord(const AStr : String; var VPos : Integer;
const ALen : Integer) : String; {$IFDEF USE_INLINE}inline;{$ENDIF}
var
LStart: Integer;
begin
LStart := VPos;
while VPos <= ALen do begin
if not CharIsInSet(AStr, VPos, HTML_ALLOWABLE_ALPHANUMBERIC) then begin
Break;
end;
Inc(VPos);
end;
Result := Copy(AStr, LStart, VPos-LStart);
end;
procedure DiscardWord(const AStr : String; var VPos : Integer;
const ALen : Integer); {$IFDEF USE_INLINE}inline;{$ENDIF}
begin
while VPos <= ALen do begin
if not CharIsInSet(AStr, VPos, HTML_ALLOWABLE_ALPHANUMBERIC) then begin
Break;
end;
Inc(VPos);
end;
end;
function ParseUntil(const AStr : String; const AChar : Char;
var VPos : Integer; const ALen : Integer) : String; {$IFDEF USE_INLINE}inline;{$ENDIF}
var
LStart: Integer;
begin
LStart := VPos;
while VPos <= ALen do begin
if AStr[VPos] = AChar then begin
Break;
end;
Inc(VPos);
end;
Result := Copy(AStr, LStart, VPos-LStart);
end;
procedure DiscardUntil(const AStr : String; const AChar : Char;
var VPos : Integer; const ALen : Integer); {$IFDEF USE_INLINE}inline;{$ENDIF}
begin
while VPos <= ALen do begin
if AStr[VPos] = AChar then begin
Break;
end;
Inc(VPos);
end;
end;
function ParseUntilCharOrEndOfTag(const AStr : String; const AChar: Char;
var VPos : Integer; const ALen : Integer): String; {$IFDEF USE_INLINE}inline;{$ENDIF}
var
LStart: Integer;
begin
LStart := VPos;
while VPos <= ALen do begin
if (AStr[VPos] = AChar) or (AStr[VPos] = '>') then begin {do not localize}
Break;
end;
Inc(VPos);
end;
Result := Copy(AStr, LStart, VPos - LStart);
end;
procedure DiscardUntilCharOrEndOfTag(const AStr : String; const AChar: Char;
var VPos : Integer; const ALen : Integer); {$IFDEF USE_INLINE}inline;{$ENDIF}
begin
while VPos <= ALen do begin
if (AStr[VPos] = AChar) or (AStr[VPos] = '>') then begin {do not localize}
Break;
end;
Inc(VPos);
end;
end;
function ParseHTTPMetaEquiveData(const AStr : String; var VPos : Integer;
const ALen : Integer) : String; {$IFDEF USE_INLINE}inline;{$ENDIF}
var
LQuoteChar : Char;
LWord : String;
begin
Result := '';
DiscardDocWhiteSpace(AStr, VPos, ALen);
if CharIsInSet(AStr, VPos, HTML_QUOTECHARS) then begin
LQuoteChar := AStr[VPos];
Inc(VPos);
if VPos > ALen then begin
Exit;
end;
LWord := ParseUntil(AStr, LQuoteChar, VPos, ALen);
Inc(VPos);
end else begin
if VPos > ALen then begin
Exit;
end;
LWord := ParseWord(AStr, VPos, ALen);
end;
Result := LWord + ':'; {do not localize}
repeat
DiscardDocWhiteSpace(AStr, VPos, ALen);
if VPos > ALen then begin
Break;
end;
if AStr[VPos] = '/' then begin {do not localize}
Inc(VPos);
if VPos > ALen then begin
Break;
end;
end;
if AStr[VPos] = '>' then begin {do not localize}
Break;
end;
LWord := ParseWord(AStr, VPos, ALen);
if VPos > ALen then begin
Break;
end;
if AStr[VPos] = '=' then begin {do not localize}
Inc(VPos);
DiscardDocWhiteSpace(AStr, VPos, ALen);
if CharIsInSet(AStr, VPos, HTML_QUOTECHARS) then begin
LQuoteChar := AStr[VPos];
Inc(VPos);
if TextIsSame(LWord, 'CONTENT') then begin
Result := Result + ' ' + ParseUntil(AStr, LQuoteChar, VPos, ALen);
Inc(VPos);
// RLebeau: this is a special case for handling a malformed tag
// that was encountered in the wild:
// <meta http-equiv="Content-Type" content="text/html; charset="window-1255">
if VPos > ALen then begin
Break;
end;
if CharIsInSet(AStr, VPos, HTML_DOCWHITESPACE + '/>') then begin
Continue;
end;
Result := Result + ParseUntil(AStr, LQuoteChar, VPos, ALen);
Inc(VPos);
end else begin
DiscardUntil(AStr, LQuoteChar, VPos, ALen);
Inc(VPos);
end;
end else begin
if TextIsSame(LWord, 'CONTENT') then begin
Result := Result + ' ' + ParseUntilCharOrEndOfTag(AStr, ' ', VPos, ALen); {do not localize}
end else begin
DiscardUntilCharOrEndOfTag(AStr, ' ', VPos, ALen); {do not localize}
end;
end;
end else begin
Inc(VPos);
end;
until False;
end;
function ParseMetaCharsetData(const AStr : String; var VPos : Integer;
const ALen : Integer) : String; {$IFDEF USE_INLINE}inline;{$ENDIF}
var
LQuoteChar : Char;
LWord : String;
begin
Result := '';
DiscardDocWhiteSpace(AStr, VPos, ALen);
if CharIsInSet(AStr, VPos, HTML_QUOTECHARS) then begin
LQuoteChar := AStr[VPos];
Inc(VPos);
if VPos > ALen then begin
Exit;
end;
LWord := ParseUntil(AStr, LQuoteChar, VPos, ALen);
Inc(VPos);
end else begin
if VPos > ALen then begin
Exit;
end;
LWord := ParseWord(AStr, VPos, ALen);
end;
DiscardUntilEndOfTag(AStr, VPos, ALen);
Result := LWord;
end;
procedure DiscardToEndOfComment(const AStr : String; var VPos : Integer; const ALen : Integer); {$IFDEF USE_INLINE}inline; {$ENDIF}
var
i : Integer;
begin
DiscardUntil(AStr, '-', VPos, ALen); {do not localize}
i := 0;
while VPos <= ALen do begin
if AStr[VPos] = '-' then begin {do not localize}
if i < 2 then begin
Inc(i);
end;
end else begin
if (AStr[VPos] = '>') and (i = 2) then begin {do not localize}
Break;
end;
i := 0;
end;
Inc(VPos);
end;
end;
function ParseForCloseTag(const AStr, ATagWord : String; var VPos : Integer; const ALen : Integer) : String; {$IFDEF USE_INLINE}inline; {$ENDIF}
var
LWord, LTmp : String;
begin
Result := '';
while VPos <= ALen do begin
Result := Result + ParseUntil(AStr, '<', VPos, ALen); {do not localize}
if AStr[VPos] = '<' then begin
Inc(VPos);
end;
LTmp := '<' + ExtractDocWhiteSpace(AStr, VPos, ALen); {do not localize}
if AStr[VPos] = '/' then begin {do not localize}
Inc(VPos);
LTmp := LTmp + '/'; {do not localize}
LWord := ParseWord(AStr, VPos, ALen);
if TextIsSame(LWord, ATagWord) then begin
DiscardUntilEndOfTag(AStr, VPos, ALen);
Break;
end;
end;
Result := Result + LTmp + LWord + ParseUntilEndOfTag(AStr, VPos, ALen); {do not localize}
Inc(VPos);
end;
end;
procedure DiscardUntilCloseTag(const AStr, ATagWord : String; var VPos : Integer;
const ALen : Integer; const AIsScript : Boolean = False); {$IFDEF USE_INLINE}inline; {$ENDIF}
var
LWord, LTmp : String;
begin
while VPos <= ALen do begin
DiscardUntil(AStr, '<', VPos, ALen); {do not localize}
if AStr[VPos] = '<' then begin {do not localize}
Inc(VPos);
end;
LTmp := '<' + ExtractDocWhiteSpace(AStr, VPos, ALen);
if AStr[VPos] = '/' then begin {do not localize}
Inc(VPos);
LTmp := LTmp + '/'; {do not localize}
LWord := ParseWord(AStr, VPos, ALen);
if TextIsSame(LWord, ATagWord) then begin
DiscardUntilEndOfTag(AStr, VPos, ALen);
Break;
end;
end;
if not AIsScript then begin
DiscardUntilEndOfTag(AStr, VPos, ALen);
end;
Inc(VPos);
end;
end;
procedure ParseMetaHTTPEquiv(AStream: TStream; AHeaders : TStrings; var VCharSet: string);
type
TIdHTMLMode = (none, html, title, head, body, comment);
var
LRawData : String;
LWord : String;
LMode : TIdHTMLMode;
LPos : Integer;
LLen : Integer;
LEncoding: IIdTextEncoding;
begin
VCharSet := '';
// AHeaders.Clear;
AStream.Position := 0;
LEncoding := IndyTextEncoding_8Bit;
// TODO: parse the stream as-is without reading it into a String first...
LRawData := ReadStringFromStream(AStream, -1, LEncoding{$IFDEF STRING_IS_ANSI}, LEncoding{$ENDIF});
LEncoding := nil;
LMode := none;
LPos := 0;
LLen := Length(LRawData);
repeat
Inc(LPos);
if LPos > LLen then begin
Break;
end;
if LRawData[LPos] = '<' then begin {do not localize}
Inc(LPos);
if LPos > LLen then begin
Break;
end;
if LRawData[LPos] = '?' then begin {do not localize}
Inc(LPos);
if LPos > LLen then begin
Break;
end;
end
else if LRawData[LPos] = '!' then begin {do not localize}
Inc(LPos);
if LPos > LLen then begin
Break;
end;
//we have to handle comments separately since they appear in any mode.
if Copy(LRawData, LPos, 2) = '--' then begin {do not localize}
Inc(LPos, 2);
DiscardToEndOfComment(LRawData, LPos, LLen);
Continue;
end;
end;
DiscardDocWhiteSpace(LRawData, LPos, LLen);
LWord := ParseWord(LRawData, LPos, LLen);
case LMode of
none :
begin
DiscardUntilEndOfTag(LRawData, LPos, LLen);
if TextIsSame(LWord, 'HTML') then begin
LMode := html;
end;
end;
html :
begin
DiscardUntilEndOfTag(LRawData, LPos, LLen);
case PosInStrArray(LWord, HTML_MainDocParts, False) of
0 : LMode := title;//title
1 : LMode := head; //head
2 : LMode := body; //body
end;
end;
head :
begin
case PosInStrArray(LWord, HTML_HeadDocAttrs, False) of
0 : //'META'
begin
DiscardDocWhiteSpace(LRawData, LPos, LLen);
LWord := ParseWord(LRawData, LPos, LLen);
// '<meta http-equiv="..." content="...">'
// '<meta charset="...">' (used in HTML5)
// TODO: use ParseUntilEndOfTag() here
case PosInStrArray(LWord, HTML_MetaAttrs, False) of {do not localize}
0: // HTTP-EQUIV
begin
DiscardDocWhiteSpace(LRawData, LPos, LLen);
if LRawData[LPos] = '=' then begin {do not localize}
Inc(LPos);
if LPos > LLen then begin
Break;
end;
if AHeaders <> nil then begin
AHeaders.Add( ParseHTTPMetaEquiveData(LRawData, LPos, LLen) );
end else begin
ParseHTTPMetaEquiveData(LRawData, LPos, LLen);
end;
end;
end;
1: // charset
begin
DiscardDocWhiteSpace(LRawData, LPos, LLen);
if LRawData[LPos] = '=' then begin {do not localize}
Inc(LPos);
if LPos > LLen then begin
Break;
end;
VCharset := ParseMetaCharsetData(LRawData, LPos, LLen);
end;
end;
else
DiscardUntilEndOfTag(LRawData, LPos, LLen);
end;
end;
1 : //'TITLE'
begin
DiscardUntilEndOfTag(LRawData, LPos, LLen);
DiscardUntilCloseTag(LRawData, 'TITLE', LPos, LLen); {do not localize}
end;
2 : //'SCRIPT'
begin
DiscardUntilEndOfTag(LRawData, LPos, LLen);
DiscardUntilCloseTag(LRawData, 'SCRIPT', LPos, LLen, True); {do not localize}
end;
3 : //'LINK'
begin
DiscardUntilEndOfTag(LRawData, LPos, LLen); {do not localize}
end;
end;
end;
body: begin
Exit;
end;
end;
end;
until False;
end;
{*************************************************************************************************}
// make sure that an RFC MsgID has angle brackets on it
function EnsureMsgIDBrackets(const AMsgID: String): String;
{$IFDEF USE_INLINE} inline; {$ENDIF}
begin
Result := AMsgID;
if Length(Result) > 0 then begin
if Result[1] <> '<' then begin {do not localize}
Result := '<' + Result; {do not localize}
end;
if Result[Length(Result)] <> '>' then begin {do not localize}
Result := Result + '>'; {do not localize}
end;
end;
end;
function ExtractHeaderItem(const AHeaderLine: String): String;
var
s: string;
begin
// Store in s and not Result because of Fetch semantics
s := AHeaderLine;
Result := Trim(Fetch(s, ';')); {do not localize}
end;
const
QuoteSpecials: array[TIdHeaderQuotingType] of String = (
{Plain } '', {do not localize}
{RFC822} '()<>@,;:\"./', {do not localize}
{MIME } '()<>@,;:\"/[]?=', {do not localize}
{HTTP } '()<>@,;:\"/[]?={} '#9 {do not localize}
);
{$IFDEF USE_OBJECT_ARC}
// Under ARC, SplitHeaderSubItems() cannot put a non-TObject pointer value in
// the TStrings.Objects[] property...
type
TIdHeaderNameValueItem = record
Name, Value: String;
Quoted: Boolean;
constructor Create(const AName, AValue: String; const AQuoted: Boolean);
end;
TIdHeaderNameValueList = class(TList<TIdHeaderNameValueItem>)
public
function GetValue(const AName: string): string;
function IndexOfName(const AName: string): Integer;
procedure SetValue(const AIndex: Integer; const AValue: String);
end;
constructor TIdHeaderNameValueItem.Create(const AName, AValue: String; const AQuoted: Boolean);
begin
Name := AName;
Value := AValue;
Quoted := AQuoted;
end;
function TIdHeaderNameValueList.GetValue(const AName: string): string;
var
I: Integer;
begin
I := IndexOfName(AName);
if I <> -1 then begin
Result := Items[I].Value;
end else begin
Result := '';
end;
end;
function TIdHeaderNameValueList.IndexOfName(const AName: string): Integer;
var
I: Integer;
begin
Result := -1;
for I := 0 to Count-1 do
begin
if TextIsSame(Items[I].Name, AName) then
begin
Result := I;
Exit;
end;
end;
end;
procedure TIdHeaderNameValueList.SetValue(const AIndex: Integer; const AValue: String);
var
LItem: TIdHeaderNameValueItem;
begin
LItem := Items[AIndex];
LItem.Value := AValue;
Items[AIndex] := LItem;
end;
{$ENDIF}
procedure SplitHeaderSubItems(AHeaderLine: String;
AItems: {$IFDEF USE_OBJECT_ARC}TIdHeaderNameValueList{$ELSE}TStrings{$ENDIF};
AQuoteType: TIdHeaderQuotingType);
var
LName, LValue, LSep: String;
LQuoted: Boolean;
I: Integer;
function FetchQuotedString(var VHeaderLine: string): string;
begin
Result := '';
Delete(VHeaderLine, 1, 1);
I := 1;
while I <= Length(VHeaderLine) do begin
if VHeaderLine[I] = '\' then begin
// TODO: disable this logic for HTTP 1.0
if I < Length(VHeaderLine) then begin
Delete(VHeaderLine, I, 1);
end;
end
else if VHeaderLine[I] = '"' then begin
Result := Copy(VHeaderLine, 1, I-1);
VHeaderLine := Copy(VHeaderLine, I+1, MaxInt);
Break;
end;
Inc(I);
end;
Fetch(VHeaderLine, ';');
end;
begin
Fetch(AHeaderLine, ';'); {do not localize}
LSep := CharRange(#0, #32) + QuoteSpecials[AQuoteType] + #127;
while AHeaderLine <> '' do
begin
AHeaderLine := TrimLeft(AHeaderLine);
if AHeaderLine = '' then begin
Exit;
end;
LName := Trim(Fetch(AHeaderLine, '=')); {do not localize}
AHeaderLine := TrimLeft(AHeaderLine);
LQuoted := TextStartsWith(AHeaderLine, '"'); {do not localize}
if LQuoted then
begin
LValue := FetchQuotedString(AHeaderLine);
end else begin
I := FindFirstOf(LSep, AHeaderLine);
if I <> 0 then
begin
LValue := Copy(AHeaderLine, 1, I-1);
if AHeaderLine[I] = ';' then begin {do not localize}
Inc(I);
end;
Delete(AHeaderLine, 1, I-1);
end else begin
LValue := AHeaderLine;
AHeaderLine := '';
end;
end;
if (LName <> '') and ((LValue <> '') or LQuoted) then begin
{$IFDEF USE_OBJECT_ARC}
AItems.Add(TIdHeaderNameValueItem.Create(LName, LValue, LQuoted));
{$ELSE}
AItems.AddObject(LName + '=' + LValue, TObject(LQuoted));
{$ENDIF}
end;
end;
end;
function ExtractHeaderSubItem(const AHeaderLine, ASubItem: String;
AQuoteType: TIdHeaderQuotingType): String;
var
LItems: {$IFDEF USE_OBJECT_ARC}TIdHeaderNameValueList{$ELSE}TStringList{$ENDIF};
{$IFNDEF USE_OBJECT_ARC}
{$IFNDEF HAS_TStringList_CaseSensitive}
I: Integer;
{$ENDIF}
{$ENDIF}
begin
Result := '';
// TODO: instead of splitting the header into a list of name=value pairs,
// allocating memory for it, just parse the input string in-place and extract
// the necessary substring from it...
LItems := {$IFDEF USE_OBJECT_ARC}TIdHeaderNameValueList{$ELSE}TStringList{$ENDIF}.Create;
try
SplitHeaderSubItems(AHeaderLine, LItems, AQuoteType);
{$IFDEF USE_OBJECT_ARC}
Result := LItems.GetValue(ASubItem);
{$ELSE}
{$IFDEF HAS_TStringList_CaseSensitive}
LItems.CaseSensitive := False;
Result := LItems.Values[ASubItem];
{$ELSE}
I := IndyIndexOfName(LItems, ASubItem);
if I <> -1 then begin
Result := IndyValueFromIndex(LItems, I);
end;
{$ENDIF}
{$ENDIF}
finally
LItems.Free;
end;
end;
function ReplaceHeaderSubItem(const AHeaderLine, ASubItem, AValue: String;
AQuoteType: TIdHeaderQuotingType): String;
var
LOld: String;
begin
Result := ReplaceHeaderSubItem(AHeaderLine, ASubItem, AValue, LOld, AQuoteType);
end;
function ReplaceHeaderSubItem(const AHeaderLine, ASubItem, AValue: String;
var VOld: String; AQuoteType: TIdHeaderQuotingType): String;
var
LItems: {$IFDEF USE_OBJECT_ARC}TIdHeaderNameValueList{$ELSE}TStringList{$ENDIF};
I: Integer;
LValue: string;
function QuoteString(const S: String; const AForceQuotes: Boolean): String;
var
I: Integer;
LAddQuotes: Boolean;
LNeedQuotes, LNeedEscape: String;
begin
Result := '';
if Length(S) = 0 then begin
Exit;
end;
LAddQuotes := AForceQuotes;
LNeedQuotes := CharRange(#0, #32) + QuoteSpecials[AQuoteType] + #127;
// TODO: disable this logic for HTTP 1.0
LNeedEscape := '"\'; {Do not Localize}
if AQuoteType in [QuoteRFC822, QuoteMIME] then begin
LNeedEscape := LNeedEscape + CR; {Do not Localize}
end;
for I := 1 to Length(S) do begin
if CharIsInSet(S, I, LNeedEscape) then begin
LAddQuotes := True;
Result := Result + '\'; {do not localize}
end
else if CharIsInSet(S, I, LNeedQuotes) then begin
LAddQuotes := True;
end;
Result := Result + S[I];
end;
if LAddQuotes then begin
Result := '"' + Result + '"';
end;
end;
begin
Result := '';
// TODO: instead of splitting the header into a list of name=value pairs,
// allocating memory for it, and then putting the list back together, just
// parse the input string in-place and extract/replace the necessary
// substring from it as needed, preserving the rest of the string as-is...
LItems := {$IFDEF USE_OBJECT_ARC}TIdHeaderNameValueList{$ELSE}TStringList{$ENDIF}.Create;
try
SplitHeaderSubItems(AHeaderLine, LItems, AQuoteType);
{$IFDEF USE_OBJECT_ARC}
I := LItems.IndexOfName(ASubItem);
{$ELSE}
{$IFDEF HAS_TStringList_CaseSensitive}
LItems.CaseSensitive := False;
{$ENDIF}
I := IndyIndexOfName(LItems, ASubItem);
{$ENDIF}
if I >= 0 then begin
{$IFDEF USE_OBJECT_ARC}
VOld := LItems[I].Value;
{$ELSE}
VOld := LItems.Strings[I];
Fetch(VOld, '=');
{$ENDIF}
end else begin
VOld := '';
end;
LValue := Trim(AValue);
if LValue <> '' then begin
{$IFDEF USE_OBJECT_ARC}
if I < 0 then begin
LItems.Add(TIdHeaderNameValueItem.Create(ASubItem, LValue, False));
end else begin
LItems.SetValue(I, LValue);
end;
{$ELSE}
if I < 0 then begin
LItems.Add(ASubItem + '=' + LValue); {do not localize}
end else begin
{$IFDEF HAS_TStrings_ValueFromIndex}
LItems.ValueFromIndex[I] := LValue;
{$ELSE}
LItems.Strings[I] := ASubItem + '=' + LValue; {do not localize}
{$ENDIF}
end;
{$ENDIF}
end
else if I < 0 then begin
// subitem not found, just return the original header as-is...
Result := AHeaderLine;
Exit;
end else begin
LItems.Delete(I);
end;
Result := ExtractHeaderItem(AHeaderLine);
if Result <> '' then begin
for I := 0 to LItems.Count-1 do begin
{$IFDEF USE_OBJECT_ARC}
Result := Result + '; ' + LItems[I].Name + '=' + QuoteString(LItems[I].Value, LItems[I].Quoted); {do not localize}
{$ELSE}
Result := Result + '; ' + LItems.Names[I] + '=' + QuoteString(IndyValueFromIndex(LItems, I), Boolean(LItems.Objects[I])); {do not localize}
{$ENDIF}
end;
end;
finally
LItems.Free;
end;
end;
function MediaTypeMatches(const AValue, AMediaType: String): Boolean;
begin
if Pos('/', AMediaType) > 0 then begin {do not localize}
Result := TextIsSame(AValue, AMediaType);
end else begin
Result := TextStartsWith(AValue, AMediaType + '/'); {do not localize}
end;
end;
function IsHeaderMediaType(const AHeaderLine, AMediaType: String): Boolean;
begin
Result := MediaTypeMatches(ExtractHeaderItem(AHeaderLine), AMediaType);
end;
function IsHeaderMediaTypes(const AHeaderLine: String; const AMediaTypes: array of String): Boolean;
var
LHeader: String;
I: Integer;
begin
Result := False;
LHeader := ExtractHeaderItem(AHeaderLine);
for I := Low(AMediaTypes) to High(AMediaTypes) do begin
if MediaTypeMatches(LHeader, AMediaTypes[I]) then begin
Result := True;
Exit;
end;
end;
end;
function ExtractHeaderMediaType(const AHeaderLine: String): String;
var
S: String;
I: Integer;
begin
S := ExtractHeaderItem(AHeaderLine);
I := Pos('/', S);
if I > 0 then begin
Result := Copy(S, 1, I-1);
end else begin
Result := '';
end;
end;
function ExtractHeaderMediaSubType(const AHeaderLine: String): String;
var
S: String;
I: Integer;
begin
S := ExtractHeaderItem(AHeaderLine);
I := Pos('/', S);
if I > 0 then begin
Result := Copy(S, I+1, Length(S));
end else begin
Result := '';
end;
end;
function IsHeaderValue(const AHeaderLine: String; const AValue: String): Boolean;
begin
Result := TextIsSame(ExtractHeaderItem(AHeaderLine), AValue);
end;
function GetClockValue : Int64;
{$IFDEF DOTNET}
{$IFDEF USE_INLINE} inline; {$ENDIF}
{$ENDIF}
{$IFDEF WINDOWS}
type
TInt64Rec = record
case Integer of
0 : (High : UInt32;
Low : UInt32);
1 : (Long : Int64);
end;
var
LFTime : TFileTime;
{$ENDIF}
{$IFDEF UNIX}
{$IFNDEF USE_VCL_POSIX}
var
TheTms: tms;
{$ENDIF}
{$ENDIF}
begin
{$IFDEF WINDOWS}
{$IFDEF WINCE}
// TODO
{$ELSE}
Windows.GetSystemTimeAsFileTime(LFTime);
TInt64Rec(Result).Low := LFTime.dwLowDateTime;
TInt64Rec(Result).High := LFTime.dwHighDateTime;
{$ENDIF}
{$ENDIF}
{$IFDEF UNIX}
//Is the following correct?
{$IFDEF USE_BASEUNIX}
Result := fptimes(TheTms);
{$ENDIF}
{$IFDEF KYLIXCOMPAT}
Result := Times(TheTms);
{$ENDIF}
{$IFDEF USE_VCL_POSIX}
Result := time(nil);
{$ENDIF}
{$ENDIF}
{$IFDEF DOTNET}
Result := System.DateTime.Now.Ticks;
{$ENDIF}
end;
{$UNDEF NO_NATIVE_ASM}
{$IFDEF DOTNET}
{$DEFINE NO_NATIVE_ASM}
{$ENDIF}
{$IFDEF IOS}
{$IFDEF CPUARM}
{$DEFINE NO_NATIVE_ASM}
{$ENDIF}
{$ENDIF}
{$IFDEF ANDROID}
{$DEFINE NO_NATIVE_ASM}
{$ENDIF}
{$IFDEF FPC}
{$IFNDEF CPUI386}
{$DEFINE NO_NATIVE_ASM}
{$ENDIF}
{$ENDIF}
{$IFDEF NO_NATIVE_ASM}
function ROL(const AVal: UInt32; AShift: Byte): UInt32;
{$IFDEF USE_INLINE} inline; {$ENDIF}
begin
Result := (AVal shl AShift) or (AVal shr (32 - AShift));
end;
function ROR(const AVal: UInt32; AShift: Byte): UInt32;
{$IFDEF USE_INLINE} inline; {$ENDIF}
begin
Result := (AVal shr AShift) or (AVal shl (32 - AShift)) ;
end;
{$ELSE}
// 32-bit: Arg1=EAX, Arg2=DL
// 64-bit: Arg1=ECX, Arg2=DL
function ROL(const AVal: UInt32; AShift: Byte): UInt32; assembler;
asm
{$IFDEF CPU64}
mov eax, ecx
{$ENDIF}
mov cl, dl
rol eax, cl
end;
function ROR(const AVal: UInt32; AShift: Byte): UInt32; assembler;
asm
{$IFDEF CPU64}
mov eax, ecx
{$ENDIF}
mov cl, dl
ror eax, cl
end;
{$ENDIF}
function IndyComputerName: string;
{$IFDEF DOTNET}
{$IFDEF USE_INLINE} inline; {$ENDIF}
{$ENDIF}
{$IFDEF UNIX}
const
sMaxHostName = 255;
var
LHost: array[0..sMaxHostName] of TIdAnsiChar;
{$IFDEF USE_MARSHALLED_PTRS}
LHostPtr: TPtrWrapper;
{$ENDIF}
{$ENDIF}
{$IFDEF WINDOWS}
var
{$IFDEF WINCE}
Reg: TRegistry;
{$ELSE}
LHost: array[0..MAX_COMPUTERNAME_LENGTH] of Char;
i: DWORD;
{$ENDIF}
{$ENDIF}
begin
Result := '';
{$IFDEF UNIX}
//TODO: No need for LHost at all? Prob can use just Result
{$IFDEF KYLIXCOMPAT}
if GetHostname(LHost, sMaxHostName) <> -1 then begin
Result := String(LHost);
end;
{$ENDIF}
{$IFDEF USE_BASE_UNIX}
Result := GetHostName;
{$ENDIF}
{$IFDEF USE_VCL_POSIX}
{$IFDEF USE_MARSHALLED_PTRS}
LHostPtr := TPtrWrapper.Create(@LHost[0]);
{$ENDIF}
if Posix.Unistd.gethostname(
{$IFDEF USE_MARSHALLED_PTRS}
LHostPtr.ToPointer
{$ELSE}
LHost
{$ENDIF},
sMaxHostName) <> -1 then
begin
LHost[sMaxHostName] := TIdAnsiChar(0);
{$IFDEF USE_MARSHALLED_PTRS}
Result := TMarshal.ReadStringAsAnsi(LHostPtr);
{$ELSE}
Result := String(LHost);
{$ENDIF}
end;
{$ENDIF}
{$ENDIF}
{$IFDEF WINDOWS}
{$IFDEF WINCE}
Reg := TRegistry.Create;
try
Reg.RootKey := HKEY_LOCAL_MACHINE;
if Reg.OpenKeyReadOnly('\Ident') then begin
Result := Reg.ReadString('Name');
Reg.CloseKey;
end;
finally
Reg.Free;
end;
{$ELSE}
i := MAX_COMPUTERNAME_LENGTH;
if GetComputerName(LHost, i) then begin
SetString(Result, LHost, i);
end;
{$ENDIF}
{$ENDIF}
{$IFDEF DOTNET}
Result := Environment.MachineName;
{$ENDIF}
end;
{$IFDEF STRING_IS_ANSI}
function IsLeadChar(ACh : Char): Boolean;
{$IFDEF USE_INLINE} inline; {$ENDIF}
begin
Result := ACh in LeadBytes;
end;
{$ENDIF}
function IdGetDefaultCharSet: TIdCharSet;
{$IFDEF USE_INLINE}inline;{$ENDIF}
begin
{$IFDEF UNIX}
Result := GIdDefaultCharSet;
{$ENDIF}
{$IFDEF DOTNET}
Result := idcs_UNICODE_1_1;
// not a particular Unicode encoding - just unicode in general
// i.e. DotNet native string is 2 byte Unicode, we do not concern ourselves
// with Byte order. (though we have to concern ourselves once we start
// writing to some stream or Bytes
{$ENDIF}
{$IFDEF WINDOWS}
// Many defaults are set here when the choice is ambiguous. However for
// IdMessage OnInitializeISO can be used by user to choose other.
case SysLocale.PriLangID of
LANG_CHINESE: begin
if SysLocale.SubLangID = SUBLANG_CHINESE_SIMPLIFIED then begin
Result := idcs_GB2312;
end else begin
Result := idcs_Big5;
end;
end;
LANG_JAPANESE: Result := idcs_ISO_2022_JP;
LANG_KOREAN: Result := idcs_csEUCKR;
// Kudzu
// 1251 is the Windows standard for Russian but its not used in emails.
// KOI8-R is by far the most widely used and thus the default.
LANG_RUSSIAN: Result := idcs_KOI8_R;
// Kudzu
// Ukranian is about 50/50 KOI8u and 1251, but 1251 is the newer one and
// the Windows one so we default to it.
LANG_UKRAINIAN: Result := idcs_windows_1251;
else begin
{$IFDEF STRING_IS_UNICODE}
Result := idcs_UNICODE_1_1;
// not a particular Unicode encoding - just unicode in general
// i.e. Delphi/C++Builder 2009+ native string is 2 byte Unicode,
// we do not concern ourselves with Byte order. (though we have
// to concern ourselves once we start writing to some stream or
// Bytes
{$ELSE}
Result := idcs_ISO_8859_1;
{$ENDIF}
end;
end;
{$ENDIF}
end;
//The following is for working on email headers and message part headers.
//For example, to remove the boundary from the ContentType header, call
//ContentType := RemoveHeaderEntry(ContentType, 'boundary', QuoteMIME);
function RemoveHeaderEntry(const AHeader, AEntry: string;
AQuoteType: TIdHeaderQuotingType): string;
{$IFDEF USE_INLINE}inline;{$ENDIF}
begin
Result := ReplaceHeaderSubItem(AHeader, AEntry, '', AQuoteType);
end;
function RemoveHeaderEntry(const AHeader, AEntry: string; var VOld: String;
AQuoteType: TIdHeaderQuotingType): string;
{$IFDEF USE_INLINE}inline;{$ENDIF}
begin
Result := ReplaceHeaderSubItem(AHeader, AEntry, '', VOld, AQuoteType);
end;
function RemoveHeaderEntries(const AHeader: string; AEntries: array of string;
AQuoteType: TIdHeaderQuotingType): string;
var
I: Integer;
begin
Result := AHeader;
if Length(AEntries) > 0 then begin
for I := Low(AEntries) to High(AEntries) do begin
Result := ReplaceHeaderSubItem(Result, AEntries[I], '', AQuoteType);
end;
end;
end;
{
Three functions for easier manipulating of strings. Don't know of any
system functions to perform these actions. If there aren't and someone
can find an optimised way of performing then please implement...
}
function FindFirstOf(const AFind, AText: string; const ALength: Integer = -1;
const AStartPos: Integer = 1): Integer;
var
I, LLength, LPos: Integer;
begin
Result := 0;
if Length(AFind) > 0 then begin
LLength := IndyLength(AText, ALength, AStartPos);
if LLength > 0 then begin
for I := 0 to LLength-1 do begin
LPos := AStartPos + I;
if IndyPos(AText[LPos], AFind) <> 0 then begin
Result := LPos;
Exit;
end;
end;
end;
end;
end;
function FindFirstNotOf(const AFind, AText: string; const ALength: Integer = -1;
const AStartPos: Integer = 1): Integer;
var
I, LLength, LPos: Integer;
begin
Result := 0;
LLength := IndyLength(AText, ALength, AStartPos);
if LLength > 0 then begin
if Length(AFind) = 0 then begin
Result := AStartPos;
Exit;
end;
for I := 0 to LLength-1 do begin
LPos := AStartPos + I;
if IndyPos(AText[LPos], AFind) = 0 then begin
Result := LPos;
Exit;
end;
end;
end;
end;
function TrimAllOf(const ATrim, AText: string): string;
var
Len: Integer;
begin
Result := AText;
Len := Length(Result);
while Len > 0 do begin
if IndyPos(Result[1], ATrim) > 0 then begin
IdDelete(Result, 1, 1);
Dec(Len);
end else begin
Break;
end;
end;
while Len > 0 do begin
if IndyPos(Result[Len], ATrim) > 0 then begin
IdDelete(Result, Len, 1);
Dec(Len);
end else begin
Break;
end;
end;
end;
function ContentTypeToEncoding(const AContentType: String;
AQuoteType: TIdHeaderQuotingType): IIdTextEncoding;
var
LCharset: String;
begin
LCharset := ExtractHeaderSubItem(AContentType, 'charset', AQuoteType); {do not localize}
Result := CharsetToEncoding(LCharset);
end;
function CharsetToEncoding(const ACharset: String): IIdTextEncoding;
{$IFNDEF DOTNET_OR_ICONV}
var
CP: Word;
{$ENDIF}
begin
Result := nil;
if ACharSet <> '' then
begin
// let the user provide a custom encoding first, if desired...
if Assigned(GIdEncodingNeeded) then begin
Result := GIdEncodingNeeded(ACharSet);
if Assigned(Result) then begin
Exit;
end;
end;
// RLebeau 3/13/09: if there is a problem initializing an encoding
// class for the requested charset, either because the charset is
// not known to Indy, or because the OS does not support it natively,
// just return the 8-bit encoding as a fallback for now. The data
// being handled by it likely won't be encoded/decoded properly, but
// at least the error won't cause exceptions in the user's code, and
// maybe the user will know how to encode/decode the data manually
// as a workaround...
try
{$IFDEF DOTNET_OR_ICONV}
Result := IndyTextEncoding(ACharset);
{$ELSE}
CP := CharsetToCodePage(ACharset);
if CP <> 0 then begin
Result := IndyTextEncoding(CP);
end;
{$ENDIF}
except end;
end;
{JPM - I have decided to temporarily make this 8-bit because I'm concerned
about how binary files will be handled by the ASCII encoder (where there may
be 8bit byte-values. In addition, there are numerous charsets for various
languages and codepages that do some special mapping for them would be a mess.}
{RLebeau: technically, we should be returning a 7-bit encoding, as the
default charset for "text/" content types is "us-ascii".}
if not Assigned(Result) then
begin
{ TODO: finish implementing this
if PosInStrArray(
ACharSet,
['ISO-2022-JP', 'ISO-2022-JP-1', 'ISO-2022-JP-2', 'ISO-2022-JP-3', 'ISO-2022-JP-2004'], {do not localize
False) <> -1 then
begin
Result := TIdTextEncoding_ISO2022JP.Create;
Exit;
end;
}
Result := IndyTextEncoding_8Bit;
end;
end;
procedure WriteStringAsContentType(AStream: TStream; const AStr, AContentType: String;
AQuoteType: TIdHeaderQuotingType
{$IFDEF STRING_IS_ANSI}; ASrcEncoding: IIdTextEncoding = nil{$ENDIF});
begin
WriteStringToStream(AStream, AStr, ContentTypeToEncoding(AContentType, AQuoteType){$IFDEF STRING_IS_ANSI}, ASrcEncoding{$ENDIF});
end;
procedure WriteStringsAsContentType(AStream: TStream; const AStrings: TStrings;
const AContentType: String; AQuoteType: TIdHeaderQuotingType
{$IFDEF STRING_IS_ANSI}; ASrcEncoding: IIdTextEncoding = nil{$ENDIF});
begin
// RLebeau 10/06/2010: not using TStrings.SaveToStream() in D2009+
// anymore, as it may save a BOM which we do not want here...
// TODO: instead of writing AString.Text as a whole, loop through AStrings
// writing the individual strings to avoid unnecessary memory allocations...
WriteStringToStream(AStream, AStrings.Text, ContentTypeToEncoding(AContentType, AQuoteType){$IFDEF STRING_IS_ANSI}, ASrcEncoding{$ENDIF});
end;
procedure WriteStringAsCharset(AStream: TStream; const AStr, ACharset: string
{$IFDEF STRING_IS_ANSI}; ASrcEncoding: IIdTextEncoding = nil{$ENDIF});
begin
WriteStringToStream(AStream, AStr, CharsetToEncoding(ACharset){$IFDEF STRING_IS_ANSI}, ASrcEncoding{$ENDIF});
end;
procedure WriteStringsAsCharset(AStream: TStream; const AStrings: TStrings;
const ACharset: string
{$IFDEF STRING_IS_ANSI}; ASrcEncoding: IIdTextEncoding = nil{$ENDIF});
begin
// RLebeau 10/06/2010: not using TStrings.SaveToStream() in D2009+
// anymore, as it may save a BOM which we do not want here...
// TODO: instead of writing AString.Text as a whole, loop through AStrings
// writing the individual strings to avoid unnecessary memory allocations...
WriteStringToStream(AStream, AStrings.Text, CharsetToEncoding(ACharset){$IFDEF STRING_IS_ANSI}, ASrcEncoding{$ENDIF});
end;
function ReadStringAsContentType(AStream: TStream; const AContentType: String;
AQuoteType: TIdHeaderQuotingType
{$IFDEF STRING_IS_ANSI}; ADestEncoding: IIdTextEncoding = nil{$ENDIF}
): String;
begin
Result := ReadStringFromStream(AStream, -1, ContentTypeToEncoding(AContentType, AQuoteType){$IFDEF STRING_IS_ANSI}, ADestEncoding{$ENDIF});
end;
procedure ReadStringsAsContentType(AStream: TStream; AStrings: TStrings;
const AContentType: String; AQuoteType: TIdHeaderQuotingType
{$IFDEF STRING_IS_ANSI}; ADestEncoding: IIdTextEncoding = nil{$ENDIF}
);
begin
AStrings.Text := ReadStringFromStream(AStream, -1, ContentTypeToEncoding(AContentType, AQuoteType){$IFDEF STRING_IS_ANSI}, ADestEncoding{$ENDIF});
end;
function ReadStringAsCharset(AStream: TStream; const ACharset: String
{$IFDEF STRING_IS_ANSI}; ADestEncoding: IIdTextEncoding = nil{$ENDIF}
): String;
begin
//TODO: Figure out what should happen with Unicode content type.
Result := ReadStringFromStream(AStream, -1, CharsetToEncoding(ACharset){$IFDEF STRING_IS_ANSI}, ADestEncoding{$ENDIF});
end;
procedure ReadStringsAsCharset(AStream: TStream; AStrings: TStrings; const ACharset: String
{$IFDEF STRING_IS_ANSI}; ADestEncoding: IIdTextEncoding = nil{$ENDIF}
);
begin
AStrings.Text := ReadStringFromStream(AStream, -1, CharsetToEncoding(ACharset){$IFDEF STRING_IS_ANSI}, ADestEncoding{$ENDIF});
end;
{ TIdInterfacedObject }
function TIdInterfacedObject._AddRef: Integer;
begin
{$IFDEF DOTNET}
Result := 1;
{$ELSE}
Result := inherited _AddRef;
{$ENDIF}
end;
function TIdInterfacedObject._Release: Integer;
begin
{$IFDEF DOTNET}
Result := 1;
{$ELSE}
Result := inherited _Release;
{$ENDIF}
end;
initialization
{$IFDEF WINDOWS}
ATempPath := TempPath;
{$ENDIF}
SetLength(IndyFalseBoolStrs, 1);
IndyFalseBoolStrs[Low(IndyFalseBoolStrs)] := 'FALSE'; {Do not Localize}
SetLength(IndyTrueBoolStrs, 1);
IndyTrueBoolStrs[Low(IndyTrueBoolStrs)] := 'TRUE'; {Do not Localize}
end.